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()