Mercurial > libervia-backend
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) |