comparison sat/bridge/bridge_constructor/base_constructor.py @ 2624:56f94936df1e

code style reformatting using black
author Goffi <goffi@goffi.org>
date Wed, 27 Jun 2018 20:14:46 +0200
parents 26edcf3a30eb
children 779351da2c13
comparison
equal deleted inserted replaced
2623:49533de4540b 2624:56f94936df1e
1 #!/usr/bin/env python2 1 #!/usr/bin/env python2
2 #-*- coding: utf-8 -*- 2 # -*- coding: utf-8 -*-
3 3
4 # SàT: a XMPP client 4 # SàT: a XMPP client
5 # Copyright (C) 2009-2018 Jérôme Poisson (goffi@goffi.org) 5 # Copyright (C) 2009-2018 Jérôme Poisson (goffi@goffi.org)
6 6
7 # This program is free software: you can redistribute it and/or modify 7 # This program is free software: you can redistribute it and/or modify
27 import re 27 import re
28 from importlib import import_module 28 from importlib import import_module
29 29
30 30
31 class ParseError(Exception): 31 class ParseError(Exception):
32 #Used when the signature parsing is going wrong (invalid signature ?) 32 # Used when the signature parsing is going wrong (invalid signature ?)
33 pass 33 pass
34 34
35 35
36 class Constructor(object): 36 class Constructor(object):
37 NAME = None # used in arguments parsing, filename will be used if not set 37 NAME = None # used in arguments parsing, filename will be used if not set
39 # they can be set to dict of strings using python formatting syntax 39 # they can be set to dict of strings using python formatting syntax
40 # dict keys will be used to select part to replace (e.g. "signals" key will 40 # dict keys will be used to select part to replace (e.g. "signals" key will
41 # replace ##SIGNALS_PART## in template), while the value is the format 41 # replace ##SIGNALS_PART## in template), while the value is the format
42 # keys starting with "signal" will be used for signals, while ones starting with 42 # keys starting with "signal" will be used for signals, while ones starting with
43 # "method" will be used for methods 43 # "method" will be used for methods
44 # check D-Bus constructor for an example 44 #  check D-Bus constructor for an example
45 CORE_FORMATS = None 45 CORE_FORMATS = None
46 CORE_TEMPLATE = None 46 CORE_TEMPLATE = None
47 CORE_DEST = None 47 CORE_DEST = None
48 FRONTEND_FORMATS = None 48 FRONTEND_FORMATS = None
49 FRONTEND_TEMPLATE = None 49 FRONTEND_TEMPLATE = None
64 def getValues(self, name): 64 def getValues(self, name):
65 """Return values of a function in a dict 65 """Return values of a function in a dict
66 @param name: Name of the function to get 66 @param name: Name of the function to get
67 @return: dict, each key has the config value or None if the value is not set""" 67 @return: dict, each key has the config value or None if the value is not set"""
68 function = {} 68 function = {}
69 for option in ['type', 'category', 'sig_in', 'sig_out', 'doc']: 69 for option in ["type", "category", "sig_in", "sig_out", "doc"]:
70 try: 70 try:
71 value = self.bridge_template.get(name, option) 71 value = self.bridge_template.get(name, option)
72 except NoOptionError: 72 except NoOptionError:
73 value = None 73 value = None
74 function[option] = value 74 function[option] = value
85 match = def_re.match(option) 85 match = def_re.match(option)
86 if match: 86 if match:
87 try: 87 try:
88 idx = int(match.group(1)) 88 idx = int(match.group(1))
89 except ValueError: 89 except ValueError:
90 raise ParseError("Invalid value [%s] for parameter number" % match.group(1)) 90 raise ParseError(
91 "Invalid value [%s] for parameter number" % match.group(1)
92 )
91 default_dict[idx] = self.bridge_template.get(name, option) 93 default_dict[idx] = self.bridge_template.get(name, option)
92 94
93 return default_dict 95 return default_dict
94 96
95 def getFlags(self, name): 97 def getFlags(self, name):
110 @return: dict, each key is the integer param number (no key if no argument doc), value is a tuple (name, doc)""" 112 @return: dict, each key is the integer param number (no key if no argument doc), value is a tuple (name, doc)"""
111 doc_dict = {} 113 doc_dict = {}
112 option_re = re.compile(r"doc_param_(\d+)") 114 option_re = re.compile(r"doc_param_(\d+)")
113 value_re = re.compile(r"^(\w+): (.*)$", re.MULTILINE | re.DOTALL) 115 value_re = re.compile(r"^(\w+): (.*)$", re.MULTILINE | re.DOTALL)
114 for option in self.bridge_template.options(name): 116 for option in self.bridge_template.options(name):
115 if option == 'doc_return': 117 if option == "doc_return":
116 doc_dict['return'] = self.bridge_template.get(name, option) 118 doc_dict["return"] = self.bridge_template.get(name, option)
117 continue 119 continue
118 match = option_re.match(option) 120 match = option_re.match(option)
119 if match: 121 if match:
120 try: 122 try:
121 idx = int(match.group(1)) 123 idx = int(match.group(1))
122 except ValueError: 124 except ValueError:
123 raise ParseError("Invalid value [%s] for parameter number" % match.group(1)) 125 raise ParseError(
126 "Invalid value [%s] for parameter number" % match.group(1)
127 )
124 value_match = value_re.match(self.bridge_template.get(name, option)) 128 value_match = value_re.match(self.bridge_template.get(name, option))
125 if not value_match: 129 if not value_match:
126 raise ParseError("Invalid value for parameter doc [%i]" % idx) 130 raise ParseError("Invalid value for parameter doc [%i]" % idx)
127 doc_dict[idx] = (value_match.group(1), value_match.group(2)) 131 doc_dict[idx] = (value_match.group(1), value_match.group(2))
128 return doc_dict 132 return doc_dict
139 """Generator which return individual arguments signatures from a global signature""" 143 """Generator which return individual arguments signatures from a global signature"""
140 start = 0 144 start = 0
141 i = 0 145 i = 0
142 146
143 while i < len(signature): 147 while i < len(signature):
144 if signature[i] not in ['b', 'y', 'n', 'i', 'x', 'q', 'u', 't', 'd', 's', 'a']: 148 if signature[i] not in [
149 "b",
150 "y",
151 "n",
152 "i",
153 "x",
154 "q",
155 "u",
156 "t",
157 "d",
158 "s",
159 "a",
160 ]:
145 raise ParseError("Unmanaged attribute type [%c]" % signature[i]) 161 raise ParseError("Unmanaged attribute type [%c]" % signature[i])
146 162
147 if signature[i] == 'a': 163 if signature[i] == "a":
148 i += 1 164 i += 1
149 if signature[i] != '{' and signature[i] != '(': # FIXME: must manage tuples out of arrays 165 if (
166 signature[i] != "{" and signature[i] != "("
167 ): # FIXME: must manage tuples out of arrays
150 i += 1 168 i += 1
151 yield signature[start:i] 169 yield signature[start:i]
152 start = i 170 start = i
153 continue # we have a simple type for the array 171 continue # we have a simple type for the array
154 opening_car = signature[i] 172 opening_car = signature[i]
155 assert(opening_car in ['{', '(']) 173 assert opening_car in ["{", "("]
156 closing_car = '}' if opening_car == '{' else ')' 174 closing_car = "}" if opening_car == "{" else ")"
157 opening_count = 1 175 opening_count = 1
158 while (True): # we have a dict or a list of tuples 176 while True: # we have a dict or a list of tuples
159 i += 1 177 i += 1
160 if i >= len(signature): 178 if i >= len(signature):
161 raise ParseError("missing }") 179 raise ParseError("missing }")
162 if signature[i] == opening_car: 180 if signature[i] == opening_car:
163 opening_count += 1 181 opening_count += 1
180 """ 198 """
181 idx = 0 199 idx = 0
182 attr_string = [] 200 attr_string = []
183 201
184 for arg in self.argumentsParser(signature): 202 for arg in self.argumentsParser(signature):
185 attr_string.append(("unicode(%(name)s)%(default)s" if (unicode_protect and arg == 's') else "%(name)s%(default)s") % { 203 attr_string.append(
186 'name': name[idx][0] if (name and idx in name) else "arg_%i" % idx, 204 (
187 'default': "=" + default[idx] if (default and idx in default) else ''}) 205 "unicode(%(name)s)%(default)s"
188 # give arg_1, arg2, etc or name1, name2=default, etc. 206 if (unicode_protect and arg == "s")
189 #give unicode(arg_1), unicode(arg_2), etc. if unicode_protect is set and arg is a string 207 else "%(name)s%(default)s"
208 )
209 % {
210 "name": name[idx][0] if (name and idx in name) else "arg_%i" % idx,
211 "default": "=" + default[idx] if (default and idx in default) else "",
212 }
213 )
214 # give arg_1, arg2, etc or name1, name2=default, etc.
215 # give unicode(arg_1), unicode(arg_2), etc. if unicode_protect is set and arg is a string
190 idx += 1 216 idx += 1
191 217
192 return ", ".join(attr_string) 218 return ", ".join(attr_string)
193 219
194 def getTemplatePath(self, template_file): 220 def getTemplatePath(self, template_file):
211 pass 237 pass
212 238
213 def frontend_completion_signal(self, completion, function, default, arg_doc, async_): 239 def frontend_completion_signal(self, completion, function, default, arg_doc, async_):
214 """override this method to extend completion""" 240 """override this method to extend completion"""
215 pass 241 pass
216
217 242
218 def generate(self, side): 243 def generate(self, side):
219 """generate bridge 244 """generate bridge
220 245
221 call generateCoreSide or generateFrontendSide if they exists 246 call generateCoreSide or generateFrontendSide if they exists
240 this is a generic method which will use formats found in self.CORE_SIGNAL_FORMAT 265 this is a generic method which will use formats found in self.CORE_SIGNAL_FORMAT
241 and self.CORE_METHOD_FORMAT (standard format method will be used) 266 and self.CORE_METHOD_FORMAT (standard format method will be used)
242 @param side(str): core or frontend 267 @param side(str): core or frontend
243 """ 268 """
244 side_vars = [] 269 side_vars = []
245 for var in ('FORMATS', 'TEMPLATE', 'DEST'): 270 for var in ("FORMATS", "TEMPLATE", "DEST"):
246 attr = "{}_{}".format(side.upper(), var) 271 attr = "{}_{}".format(side.upper(), var)
247 value = getattr(self, attr) 272 value = getattr(self, attr)
248 if value is None: 273 if value is None:
249 raise NotImplementedError 274 raise NotImplementedError
250 side_vars.append(value) 275 side_vars.append(value)
251 276
252 FORMATS, TEMPLATE, DEST = side_vars 277 FORMATS, TEMPLATE, DEST = side_vars
253 del side_vars 278 del side_vars
254 279
255 parts = {part.upper():[] for part in FORMATS} 280 parts = {part.upper(): [] for part in FORMATS}
256 sections = self.bridge_template.sections() 281 sections = self.bridge_template.sections()
257 sections.sort() 282 sections.sort()
258 for section in sections: 283 for section in sections:
259 function = self.getValues(section) 284 function = self.getValues(section)
260 print ("Adding %s %s" % (section, function["type"])) 285 print("Adding %s %s" % (section, function["type"]))
261 default = self.getDefault(section) 286 default = self.getDefault(section)
262 arg_doc = self.getArgumentsDoc(section) 287 arg_doc = self.getArgumentsDoc(section)
263 async_ = "async" in self.getFlags(section) 288 async_ = "async" in self.getFlags(section)
264 completion = { 289 completion = {
265 'sig_in': function['sig_in'] or '', 290 "sig_in": function["sig_in"] or "",
266 'sig_out': function['sig_out'] or '', 291 "sig_out": function["sig_out"] or "",
267 'category': 'plugin' if function['category'] == 'plugin' else 'core', 292 "category": "plugin" if function["category"] == "plugin" else "core",
268 'name': section, 293 "name": section,
269 # arguments with default values 294 # arguments with default values
270 'args': self.getArguments(function['sig_in'], name=arg_doc, default=default), 295 "args": self.getArguments(
271 } 296 function["sig_in"], name=arg_doc, default=default
272 297 ),
273 extend_method = getattr(self, "{}_completion_{}".format(side, function["type"])) 298 }
299
300 extend_method = getattr(
301 self, "{}_completion_{}".format(side, function["type"])
302 )
274 extend_method(completion, function, default, arg_doc, async_) 303 extend_method(completion, function, default, arg_doc, async_)
275 304
276 for part, fmt in FORMATS.iteritems(): 305 for part, fmt in FORMATS.iteritems():
277 if part.startswith(function["type"]): 306 if part.startswith(function["type"]):
278 parts[part.upper()].append(fmt.format(**completion)) 307 parts[part.upper()].append(fmt.format(**completion))
279 308
280 309 # at this point, signals_part, methods_part and direct_calls should be filled,
281 #at this point, signals_part, methods_part and direct_calls should be filled, 310 # we just have to place them in the right part of the template
282 #we just have to place them in the right part of the template
283 bridge = [] 311 bridge = []
284 const_override = {env[len(C.ENV_OVERRIDE):]:v for env,v in os.environ.iteritems() if env.startswith(C.ENV_OVERRIDE)} 312 const_override = {
313 env[len(C.ENV_OVERRIDE) :]: v
314 for env, v in os.environ.iteritems()
315 if env.startswith(C.ENV_OVERRIDE)
316 }
285 template_path = self.getTemplatePath(TEMPLATE) 317 template_path = self.getTemplatePath(TEMPLATE)
286 try: 318 try:
287 with open(template_path) as template: 319 with open(template_path) as template:
288 for line in template: 320 for line in template:
289 321
290 for part, extend_list in parts.iteritems(): 322 for part, extend_list in parts.iteritems():
291 if line.startswith('##{}_PART##'.format(part)): 323 if line.startswith("##{}_PART##".format(part)):
292 bridge.extend(extend_list) 324 bridge.extend(extend_list)
293 break 325 break
294 else: 326 else:
295 # the line is not a magic part replacement 327 # the line is not a magic part replacement
296 if line.startswith('const_'): 328 if line.startswith("const_"):
297 const_name = line[len('const_'):line.find(' = ')].strip() 329 const_name = line[len("const_") : line.find(" = ")].strip()
298 if const_name in const_override: 330 if const_name in const_override:
299 print("const {} overriden".format(const_name)) 331 print("const {} overriden".format(const_name))
300 bridge.append('const_{} = {}'.format(const_name, const_override[const_name])) 332 bridge.append(
333 "const_{} = {}".format(
334 const_name, const_override[const_name]
335 )
336 )
301 continue 337 continue
302 bridge.append(line.replace('\n', '')) 338 bridge.append(line.replace("\n", ""))
303 except IOError: 339 except IOError:
304 print ("can't open template file [{}]".format(template_path)) 340 print("can't open template file [{}]".format(template_path))
305 sys.exit(1) 341 sys.exit(1)
306 342
307 #now we write to final file 343 # now we write to final file
308 self.finalWrite(DEST, bridge) 344 self.finalWrite(DEST, bridge)
309 345
310 def finalWrite(self, filename, file_buf): 346 def finalWrite(self, filename, file_buf):
311 """Write the final generated file in [dest dir]/filename 347 """Write the final generated file in [dest dir]/filename
312 348
313 @param filename: name of the file to generate 349 @param filename: name of the file to generate
314 @param file_buf: list of lines (stings) of the file 350 @param file_buf: list of lines (stings) of the file
315 """ 351 """
316 if os.path.exists(self.args.dest_dir) and not os.path.isdir(self.args.dest_dir): 352 if os.path.exists(self.args.dest_dir) and not os.path.isdir(self.args.dest_dir):
317 print ("The destination dir [%s] can't be created: a file with this name already exists !") 353 print(
354 "The destination dir [%s] can't be created: a file with this name already exists !"
355 )
318 sys.exit(1) 356 sys.exit(1)
319 try: 357 try:
320 if not os.path.exists(self.args.dest_dir): 358 if not os.path.exists(self.args.dest_dir):
321 os.mkdir(self.args.dest_dir) 359 os.mkdir(self.args.dest_dir)
322 full_path = os.path.join(self.args.dest_dir, filename) 360 full_path = os.path.join(self.args.dest_dir, filename)
323 if os.path.exists(full_path) and not self.args.force: 361 if os.path.exists(full_path) and not self.args.force:
324 print ("The destination file [%s] already exists ! Use --force to overwrite it" % full_path) 362 print(
363 "The destination file [%s] already exists ! Use --force to overwrite it"
364 % full_path
365 )
325 try: 366 try:
326 with open(full_path, 'w') as dest_file: 367 with open(full_path, "w") as dest_file:
327 dest_file.write('\n'.join(file_buf)) 368 dest_file.write("\n".join(file_buf))
328 except IOError: 369 except IOError:
329 print ("Can't open destination file [%s]" % full_path) 370 print("Can't open destination file [%s]" % full_path)
330 except OSError: 371 except OSError:
331 print("It's not possible to generate the file, check your permissions") 372 print("It's not possible to generate the file, check your permissions")
332 exit(1) 373 exit(1)