Mercurial > libervia-backend
comparison src/bridge/bridge_constructor/bridge_constructor.py @ 2084:e1015a5df6f5
bridge(constructor): constructor now uses argparse instead of optparse
author | Goffi <goffi@goffi.org> |
---|---|
date | Sun, 02 Oct 2016 15:56:20 +0200 |
parents | 046449cc2bff |
children | da4097de5a95 |
comparison
equal
deleted
inserted
replaced
2083:7999d5299ddc | 2084:e1015a5df6f5 |
---|---|
1 #!/usr/bin/env python2 | 1 #!/usr/bin/env python2 |
2 #-*- coding: utf-8 -*- | 2 #-*- coding: utf-8 -*- |
3 | 3 |
4 # SAT: a jabber client | 4 # SàT: a XMPP client |
5 # Copyright (C) 2009-2016 Jérôme Poisson (goffi@goffi.org) | 5 # Copyright (C) 2009-2016 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 |
8 # it under the terms of the GNU Affero General Public License as published by | 8 # it under the terms of the GNU Affero General Public License as published by |
9 # the Free Software Foundation, either version 3 of the License, or | 9 # the Free Software Foundation, either version 3 of the License, or |
15 # GNU Affero General Public License for more details. | 15 # GNU Affero General Public License for more details. |
16 | 16 |
17 # You should have received a copy of the GNU Affero General Public License | 17 # You should have received a copy of the GNU Affero General Public License |
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. | 18 # along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | 19 |
20 #consts | 20 from sat.core.constants import Const as C |
21 NAME = u"bridge_constructor" | |
22 VERSION = "0.1.0" | |
23 DEST_DIR = "generated" | |
24 ABOUT = NAME + u""" v%s (c) Jérôme Poisson (aka Goffi) 2011 | |
25 | |
26 --- | |
27 """ + NAME + u""" Copyright (C) 2011 Jérôme Poisson (aka Goffi) | |
28 This program comes with ABSOLUTELY NO WARRANTY; | |
29 This is free software, and you are welcome to redistribute it | |
30 under certain conditions. | |
31 --- | |
32 | |
33 This script construct a SàT bridge using the given protocol | |
34 """ | |
35 MANAGED_PROTOCOLES = ['dbus', 'mediawiki', 'dbus-xml'] | |
36 DEFAULT_PROTOCOLE = 'dbus' | |
37 FLAGS = ['deprecated', 'async'] | |
38 | |
39 ENV_OVERRIDE = "SAT_BRIDGE_CONST_" # Prefix used to override a constant | |
40 | |
41 import sys | 21 import sys |
42 import os | 22 import os |
43 from optparse import OptionParser | 23 import argparse |
44 from ConfigParser import SafeConfigParser as Parser | 24 from ConfigParser import SafeConfigParser as Parser |
45 from ConfigParser import NoOptionError | 25 from ConfigParser import NoOptionError |
46 import re | 26 import re |
47 from datetime import datetime | 27 from datetime import datetime |
48 from xml.dom import minidom | 28 from xml.dom import minidom |
49 | 29 |
30 #consts | |
31 NAME = u"bridge_constructor" | |
32 __version__ = C.APP_VERSION | |
33 DEST_DIR_DEFAULT = "generated" | |
34 DESCRIPTION = u"""{name} Copyright (C) 2009-2016 Jérôme Poisson (aka Goffi) | |
35 | |
36 This script construct a SàT bridge using the given protocol | |
37 | |
38 This program comes with ABSOLUTELY NO WARRANTY; | |
39 This is free software, and you are welcome to redistribute it | |
40 under certain conditions. | |
41 """.format(name=NAME, version=__version__) | |
42 # TODO: move protocoles in separate files (plugins?) | |
43 MANAGED_PROTOCOLES = ['dbus', 'mediawiki', 'dbus-xml'] | |
44 DEFAULT_PROTOCOLE = 'dbus' | |
45 | |
46 # flags used method/signal declaration (not to be confused with constructor flags) | |
47 DECLARATION_FLAGS = ['deprecated', 'async'] | |
48 | |
49 ENV_OVERRIDE = "SAT_BRIDGE_CONST_" # Prefix used to override a constant | |
50 | |
51 | |
50 | 52 |
51 class ParseError(Exception): | 53 class ParseError(Exception): |
52 #Used when the signature parsing is going wrong (invalid signature ?) | 54 #Used when the signature parsing is going wrong (invalid signature ?) |
53 pass | 55 pass |
54 | 56 |
55 | 57 |
56 class Constructor(object): | 58 class Constructor(object): |
57 | 59 |
58 def __init__(self, bridge_template, options): | 60 def __init__(self, bridge_template, options): |
59 self.bridge_template = bridge_template | 61 self.bridge_template = bridge_template |
60 self.options = options | 62 self.args = options |
61 | 63 |
62 def getValues(self, name): | 64 def getValues(self, name): |
63 """Return values of a function in a dict | 65 """Return values of a function in a dict |
64 @param name: Name of the function to get | 66 @param name: Name of the function to get |
65 @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""" |
94 """Return list of flags set for this function | 96 """Return list of flags set for this function |
95 @param name: Name of the function to get | 97 @param name: Name of the function to get |
96 @return: List of flags (string)""" | 98 @return: List of flags (string)""" |
97 flags = [] | 99 flags = [] |
98 for option in self.bridge_template.options(name): | 100 for option in self.bridge_template.options(name): |
99 if option in FLAGS: | 101 if option in DECLARATION_FLAGS: |
100 flags.append(option) | 102 flags.append(option) |
101 return flags | 103 return flags |
102 | 104 |
103 def getArgumentsDoc(self, name): | 105 def getArgumentsDoc(self, name): |
104 """Return documentation of arguments | 106 """Return documentation of arguments |
194 def generateFrontendSide(self): | 196 def generateFrontendSide(self): |
195 """create the constructor in SàT frontend side""" | 197 """create the constructor in SàT frontend side""" |
196 raise NotImplementedError | 198 raise NotImplementedError |
197 | 199 |
198 def finalWrite(self, filename, file_buf): | 200 def finalWrite(self, filename, file_buf): |
199 """Write the final generated file in DEST_DIR/filename | 201 """Write the final generated file in [dest dir]/filename |
202 | |
200 @param filename: name of the file to generate | 203 @param filename: name of the file to generate |
201 @param file_buf: list of lines (stings) of the file""" | 204 @param file_buf: list of lines (stings) of the file |
202 if os.path.exists(DEST_DIR) and not os.path.isdir(DEST_DIR): | 205 """ |
206 if os.path.exists(self.args.dest_dir) and not os.path.isdir(self.args.dest_dir): | |
203 print ("The destination dir [%s] can't be created: a file with this name already exists !") | 207 print ("The destination dir [%s] can't be created: a file with this name already exists !") |
204 sys.exit(1) | 208 sys.exit(1) |
205 try: | 209 try: |
206 if not os.path.exists(DEST_DIR): | 210 if not os.path.exists(self.args.dest_dir): |
207 os.mkdir(DEST_DIR) | 211 os.mkdir(self.args.dest_dir) |
208 full_path = os.path.join(DEST_DIR, filename) | 212 full_path = os.path.join(self.args.dest_dir, filename) |
209 if os.path.exists(full_path) and not self.options.force: | 213 if os.path.exists(full_path) and not self.args.force: |
210 print ("The destination file [%s] already exists ! Use --force to overwrite it" % full_path) | 214 print ("The destination file [%s] already exists ! Use --force to overwrite it" % full_path) |
211 try: | 215 try: |
212 with open(full_path, 'w') as dest_file: | 216 with open(full_path, 'w') as dest_file: |
213 dest_file.write('\n'.join(file_buf)) | 217 dest_file.write('\n'.join(file_buf)) |
214 except IOError: | 218 except IOError: |
371 'category': 'PLUGIN' if function['category'] == 'plugin' else 'CORE', | 375 'category': 'PLUGIN' if function['category'] == 'plugin' else 'CORE', |
372 'name': section, | 376 'name': section, |
373 'args': self.getArguments(function['sig_in'], name=arg_doc, default=default)} | 377 'args': self.getArguments(function['sig_in'], name=arg_doc, default=default)} |
374 | 378 |
375 if function["type"] == "signal": | 379 if function["type"] == "signal": |
376 completion['body'] = "pass" if not self.options.debug else 'log.debug ("%s")' % section | 380 completion['body'] = "pass" if not self.args.debug else 'log.debug ("%s")' % section |
377 signals_part.append("""\ | 381 signals_part.append("""\ |
378 @dbus.service.signal(const_INT_PREFIX+const_%(category)s_SUFFIX, | 382 @dbus.service.signal(const_INT_PREFIX+const_%(category)s_SUFFIX, |
379 signature='%(sig_in)s') | 383 signature='%(sig_in)s') |
380 def %(name)s(self, %(args)s): | 384 def %(name)s(self, %(args)s): |
381 %(body)s | 385 %(body)s |
384 def %(name)s(self, %(args)s): | 388 def %(name)s(self, %(args)s): |
385 self.dbus_bridge.%(name)s(%(args)s) | 389 self.dbus_bridge.%(name)s(%(args)s) |
386 """ % completion) | 390 """ % completion) |
387 | 391 |
388 elif function["type"] == "method": | 392 elif function["type"] == "method": |
389 completion['debug'] = "" if not self.options.debug else 'log.debug ("%s")\n%s' % (section, 8 * ' ') | 393 completion['debug'] = "" if not self.args.debug else 'log.debug ("%s")\n%s' % (section, 8 * ' ') |
390 completion['args_result'] = self.getArguments(function['sig_in'], name=arg_doc, unicode_protect=self.options.unicode) | 394 completion['args_result'] = self.getArguments(function['sig_in'], name=arg_doc, unicode_protect=self.args.unicode) |
391 completion['async_comma'] = ', ' if async and function['sig_in'] else '' | 395 completion['async_comma'] = ', ' if async and function['sig_in'] else '' |
392 completion['async_args_def'] = 'callback=None, errback=None' if async else '' | 396 completion['async_args_def'] = 'callback=None, errback=None' if async else '' |
393 completion['async_args_call'] = 'callback=callback, errback=errback' if async else '' | 397 completion['async_args_call'] = 'callback=callback, errback=errback' if async else '' |
394 completion['async_callbacks'] = "('callback', 'errback')" if async else "None" | 398 completion['async_callbacks'] = "('callback', 'errback')" if async else "None" |
395 methods_part.append("""\ | 399 methods_part.append("""\ |
446 'name': section, | 450 'name': section, |
447 'args': self.getArguments(function['sig_in'], name=arg_doc, default=default)} | 451 'args': self.getArguments(function['sig_in'], name=arg_doc, default=default)} |
448 | 452 |
449 if function["type"] == "method": | 453 if function["type"] == "method": |
450 # XXX: we can manage blocking call in the same way as async one: if callback is None the call will be blocking | 454 # XXX: we can manage blocking call in the same way as async one: if callback is None the call will be blocking |
451 completion['debug'] = "" if not self.options.debug else 'log.debug ("%s")\n%s' % (section, 8 * ' ') | 455 completion['debug'] = "" if not self.args.debug else 'log.debug ("%s")\n%s' % (section, 8 * ' ') |
452 completion['args_result'] = self.getArguments(function['sig_in'], name=arg_doc) | 456 completion['args_result'] = self.getArguments(function['sig_in'], name=arg_doc) |
453 completion['async_args'] = 'callback=None, errback=None' | 457 completion['async_args'] = 'callback=None, errback=None' |
454 completion['async_comma'] = ', ' if function['sig_in'] else '' | 458 completion['async_comma'] = ', ' if function['sig_in'] else '' |
455 completion['error_handler'] = """if callback is None: | 459 completion['error_handler'] = """if callback is None: |
456 error_handler = None | 460 error_handler = None |
470 kwargs['reply_handler'] = callback | 474 kwargs['reply_handler'] = callback |
471 kwargs['error_handler'] = error_handler | 475 kwargs['error_handler'] = error_handler |
472 """ | 476 """ |
473 completion['async_args_result'] = '**kwargs' | 477 completion['async_args_result'] = '**kwargs' |
474 result = "self.db_%(category)s_iface.%(name)s(%(args_result)s%(async_comma)s%(async_args_result)s)" % completion | 478 result = "self.db_%(category)s_iface.%(name)s(%(args_result)s%(async_comma)s%(async_args_result)s)" % completion |
475 completion['result'] = ("unicode(%s)" if self.options.unicode and function['sig_out'] == 's' else "%s") % result | 479 completion['result'] = ("unicode(%s)" if self.args.unicode and function['sig_out'] == 's' else "%s") % result |
476 methods_part.append("""\ | 480 methods_part.append("""\ |
477 def %(name)s(self, %(args)s%(async_comma)s%(async_args)s): | 481 def %(name)s(self, %(args)s%(async_comma)s%(async_args)s): |
478 %(error_handler)s%(blocking_call)s%(debug)sreturn %(result)s | 482 %(error_handler)s%(blocking_call)s%(debug)sreturn %(result)s |
479 """ % completion) | 483 """ % completion) |
480 | 484 |
545 arg_elt.setAttribute('name', args_doc[idx][0] if idx in args_doc else "arg_%i" % idx) | 549 arg_elt.setAttribute('name', args_doc[idx][0] if idx in args_doc else "arg_%i" % idx) |
546 arg_elt.setAttribute('type', arg) | 550 arg_elt.setAttribute('type', arg) |
547 _direction = 'in' if function["type"] == 'method' else 'out' | 551 _direction = 'in' if function["type"] == 'method' else 'out' |
548 arg_elt.setAttribute('direction', _direction) | 552 arg_elt.setAttribute('direction', _direction) |
549 new_elt.appendChild(arg_elt) | 553 new_elt.appendChild(arg_elt) |
550 if "annotation" in self.options.flags: | 554 if "annotation" in self.args.flags: |
551 if arg in self.default_annotation: | 555 if arg in self.default_annotation: |
552 annot_elt = doc.createElement("annotation") | 556 annot_elt = doc.createElement("annotation") |
553 annot_elt.setAttribute('name', "com.trolltech.QtDBus.QtTypeName.In%d" % idx) | 557 annot_elt.setAttribute('name', "com.trolltech.QtDBus.QtTypeName.In%d" % idx) |
554 annot_elt.setAttribute('value', self.default_annotation[arg]) | 558 annot_elt.setAttribute('value', self.default_annotation[arg]) |
555 new_elt.appendChild(annot_elt) | 559 new_elt.appendChild(annot_elt) |
558 if function['sig_out']: | 562 if function['sig_out']: |
559 arg_elt = doc.createElement('arg') | 563 arg_elt = doc.createElement('arg') |
560 arg_elt.setAttribute('type', function['sig_out']) | 564 arg_elt.setAttribute('type', function['sig_out']) |
561 arg_elt.setAttribute('direction', 'out') | 565 arg_elt.setAttribute('direction', 'out') |
562 new_elt.appendChild(arg_elt) | 566 new_elt.appendChild(arg_elt) |
563 if "annotation" in self.options.flags: | 567 if "annotation" in self.args.flags: |
564 if function['sig_out'] in self.default_annotation: | 568 if function['sig_out'] in self.default_annotation: |
565 annot_elt = doc.createElement("annotation") | 569 annot_elt = doc.createElement("annotation") |
566 annot_elt.setAttribute('name', "com.trolltech.QtDBus.QtTypeName.Out0") | 570 annot_elt.setAttribute('name', "com.trolltech.QtDBus.QtTypeName.Out0") |
567 annot_elt.setAttribute('value', self.default_annotation[function['sig_out']]) | 571 annot_elt.setAttribute('value', self.default_annotation[function['sig_out']]) |
568 new_elt.appendChild(annot_elt) | 572 new_elt.appendChild(annot_elt) |
589 raise ConstructorError('Unknown constructor type') | 593 raise ConstructorError('Unknown constructor type') |
590 | 594 |
591 | 595 |
592 class BridgeConstructor(object): | 596 class BridgeConstructor(object): |
593 def __init__(self): | 597 def __init__(self): |
594 self.options = None | 598 self.args = None |
595 | 599 |
596 def check_options(self): | 600 def parse_args(self): |
597 """Check command line options""" | 601 """Check command line options""" |
598 _usage = """ | 602 parser = argparse.ArgumentParser(description=DESCRIPTION, formatter_class=argparse.RawDescriptionHelpFormatter) |
599 %prog [options] | 603 |
600 | 604 parser.add_argument("--version", action="version", version= __version__) |
601 %prog --help for options list | 605 parser.add_argument("-p", "--protocole", choices=MANAGED_PROTOCOLES, default=DEFAULT_PROTOCOLE, |
602 """ | 606 help="generate bridge using PROTOCOLE (default: %(default)s)") # (default: %s, possible values: [%s])" % (DEFAULT_PROTOCOLE, ", ".join(MANAGED_PROTOCOLES))) |
603 parser = OptionParser(usage=_usage, version=ABOUT % VERSION) | 607 parser.add_argument("-s", "--side", choices=("core", "frontend"), default="core", |
604 | 608 help="which side of the bridge do you want to make ?") # (default: %default, possible values: [core, frontend])") |
605 parser.add_option("-p", "--protocole", action="store", type="string", default=DEFAULT_PROTOCOLE, | 609 parser.add_argument("-t", "--template", type=file, default='bridge_template.ini', |
606 help="Generate bridge using PROTOCOLE (default: %s, possible values: [%s])" % (DEFAULT_PROTOCOLE, ", ".join(MANAGED_PROTOCOLES))) | 610 help="use TEMPLATE to generate bridge (default: %(default)s)") |
607 parser.add_option("-s", "--side", action="store", type="string", default="core", | 611 parser.add_argument("-f", "--force", action="store_true", |
608 help="Which side of the bridge do you want to make ? (default: %default, possible values: [core, frontend])") | 612 help=("force overwritting of existing files")) |
609 parser.add_option("-t", "--template", action="store", type="string", default='bridge_template.ini', | 613 parser.add_argument("-d", "--debug", action="store_true", |
610 help="Use TEMPLATE to generate bridge (default: %default)") | 614 help=("add debug information printing")) |
611 parser.add_option("-f", "--force", action="store_true", default=False, | 615 parser.add_argument("--no-unicode", action="store_false", dest="unicode", |
612 help=("Force overwritting of existing files")) | 616 help=("remove unicode type protection from string results")) |
613 parser.add_option("-d", "--debug", action="store_true", default=False, | 617 parser.add_argument("--flags", nargs='+', default=[], |
614 help=("Add debug information printing")) | 618 help=("constructors' specific flags")) |
615 parser.add_option("--no_unicode", action="store_false", dest="unicode", default=True, | 619 parser.add_argument("--dest-dir", default=DEST_DIR_DEFAULT, |
616 help=("Remove unicode type protection from string results")) | 620 help=("directory when the generated files will be written (default: %(default)s")) |
617 parser.add_option("--flags", action="store", type="string", | 621 |
618 help=("Constructors' specific flags, comma separated")) | 622 return parser.parse_args() |
619 | |
620 (self.options, args) = parser.parse_args() | |
621 self.options.flags = self.options.flags.split(',') if self.options.flags else [] | |
622 return args | |
623 | 623 |
624 def go(self): | 624 def go(self): |
625 self.check_options() | 625 args = self.parse_args() |
626 self.template = Parser() | 626 self.template = Parser() |
627 try: | 627 try: |
628 self.template.readfp(open(self.options.template)) | 628 self.template.readfp(args.template) |
629 except IOError: | 629 except IOError: |
630 print ("The template file doesn't exist or is not accessible") | 630 print ("The template file doesn't exist or is not accessible") |
631 exit(1) | 631 exit(1) |
632 constructor = ConstructorFactory().create(self.template, self.options) | 632 constructor = ConstructorFactory().create(self.template, args) |
633 if self.options.side == "core": | 633 if args.side == "core": |
634 constructor.generateCoreSide() | 634 constructor.generateCoreSide() |
635 elif self.options.side == "frontend": | 635 elif args.side == "frontend": |
636 constructor.generateFrontendSide() | 636 constructor.generateFrontendSide() |
637 | 637 |
638 if __name__ == "__main__": | 638 if __name__ == "__main__": |
639 bc = BridgeConstructor() | 639 bc = BridgeConstructor() |
640 bc.go() | 640 bc.go() |