1#!/usr/bin/env python 2 3'''Phosphor DBus Monitor YAML parser and code generator. 4 5The parser workflow is broken down as follows: 6 1 - Import YAML files as native python type(s) instance(s). 7 2 - Create an instance of the Everything class from the 8 native python type instance(s) with the Everything.load 9 method. 10 3 - The Everything class constructor orchestrates conversion of the 11 native python type(s) instances(s) to render helper types. 12 Each render helper type constructor imports its attributes 13 from the native python type(s) instances(s). 14 4 - Present the converted YAML to the command processing method 15 requested by the script user. 16''' 17 18import os 19import sys 20import yaml 21import mako.lookup 22from argparse import ArgumentParser 23from sdbusplus.renderer import Renderer 24from sdbusplus.namedelement import NamedElement 25import sdbusplus.property 26 27 28class InvalidConfigError(BaseException): 29 '''General purpose config file parsing error.''' 30 31 def __init__(self, path, msg): 32 '''Display configuration file with the syntax 33 error and the error message.''' 34 35 self.config = path 36 self.msg = msg 37 38 39class NotUniqueError(InvalidConfigError): 40 '''Within a config file names must be unique. 41 Display the config file with the duplicate and 42 the duplicate itself.''' 43 44 def __init__(self, path, cls, *names): 45 fmt = 'Duplicate {0}: "{1}"' 46 super(NotUniqueError, self).__init__( 47 path, fmt.format(cls, ' '.join(names))) 48 49 50def get_index(objs, cls, name, config=None): 51 '''Items are usually rendered as C++ arrays and as 52 such are stored in python lists. Given an item name 53 its class, and an optional config file filter, find 54 the item index.''' 55 56 for i, x in enumerate(objs.get(cls, [])): 57 if config and x.configfile != config: 58 continue 59 if x.name != name: 60 continue 61 62 return i 63 raise InvalidConfigError(config, 'Could not find name: "{0}"'.format(name)) 64 65 66def exists(objs, cls, name, config=None): 67 '''Check to see if an item already exists in a list given 68 the item name.''' 69 70 try: 71 get_index(objs, cls, name, config) 72 except: 73 return False 74 75 return True 76 77 78def add_unique(obj, *a, **kw): 79 '''Add an item to one or more lists unless already present, 80 with an option to constrain the search to a specific config file.''' 81 82 for container in a: 83 if not exists(container, obj.cls, obj.name, config=kw.get('config')): 84 container.setdefault(obj.cls, []).append(obj) 85 86 87class Cast(object): 88 '''Decorate an argument by casting it.''' 89 90 def __init__(self, cast, target): 91 '''cast is the cast type (static, const, etc...). 92 target is the cast target type.''' 93 self.cast = cast 94 self.target = target 95 96 def __call__(self, arg): 97 return '{0}_cast<{1}>({2})'.format(self.cast, self.target, arg) 98 99 100class Literal(object): 101 '''Decorate an argument with a literal operator.''' 102 103 integer_types = [ 104 'int8', 105 'int16', 106 'int32', 107 'int64', 108 'uint8', 109 'uint16', 110 'uint32', 111 'uint64' 112 ] 113 114 def __init__(self, type): 115 self.type = type 116 117 def __call__(self, arg): 118 if 'uint' in self.type: 119 arg = '{0}ull'.format(arg) 120 elif 'int' in self.type: 121 arg = '{0}ll'.format(arg) 122 123 if self.type in self.integer_types: 124 return Cast('static', '{0}_t'.format(self.type))(arg) 125 126 if self.type == 'string': 127 return '{0}s'.format(arg) 128 129 return arg 130 131 132class FixBool(object): 133 '''Un-capitalize booleans.''' 134 135 def __call__(self, arg): 136 return '{0}'.format(arg.lower()) 137 138 139class Quote(object): 140 '''Decorate an argument by quoting it.''' 141 142 def __call__(self, arg): 143 return '"{0}"'.format(arg) 144 145 146class Argument(NamedElement, Renderer): 147 '''Define argument type inteface.''' 148 149 def __init__(self, **kw): 150 self.type = kw.pop('type', None) 151 super(Argument, self).__init__(**kw) 152 153 def argument(self, loader, indent): 154 raise NotImplementedError 155 156 157class TrivialArgument(Argument): 158 '''Non-array type arguments.''' 159 160 def __init__(self, **kw): 161 self.value = kw.pop('value') 162 self.decorators = kw.pop('decorators', []) 163 if kw.get('type', None): 164 self.decorators.insert(0, Literal(kw['type'])) 165 if kw.get('type', None) == 'string': 166 self.decorators.insert(0, Quote()) 167 if kw.get('type', None) == 'boolean': 168 self.decorators.insert(0, FixBool()) 169 170 super(TrivialArgument, self).__init__(**kw) 171 172 def argument(self, loader, indent): 173 a = str(self.value) 174 for d in self.decorators: 175 a = d(a) 176 177 return a 178 179 180class Indent(object): 181 '''Help templates be depth agnostic.''' 182 183 def __init__(self, depth=0): 184 self.depth = depth 185 186 def __add__(self, depth): 187 return Indent(self.depth + depth) 188 189 def __call__(self, depth): 190 '''Render an indent at the current depth plus depth.''' 191 return 4*' '*(depth + self.depth) 192 193 194class ConfigEntry(NamedElement): 195 '''Base interface for rendered items.''' 196 197 def __init__(self, *a, **kw): 198 '''Pop the configfile/class/subclass keywords.''' 199 200 self.configfile = kw.pop('configfile') 201 self.cls = kw.pop('class') 202 self.subclass = kw.pop(self.cls) 203 super(ConfigEntry, self).__init__(**kw) 204 205 def factory(self, objs): 206 ''' Optional factory interface for subclasses to add 207 additional items to be rendered.''' 208 209 pass 210 211 def setup(self, objs): 212 ''' Optional setup interface for subclasses, invoked 213 after all factory methods have been run.''' 214 215 pass 216 217 218class Path(ConfigEntry): 219 '''Path/metadata association.''' 220 221 def __init__(self, *a, **kw): 222 super(Path, self).__init__(**kw) 223 224 def factory(self, objs): 225 '''Create path and metadata elements.''' 226 227 args = { 228 'class': 'pathname', 229 'pathname': 'element', 230 'name': self.name['path'] 231 } 232 add_unique(ConfigEntry( 233 configfile=self.configfile, **args), objs) 234 235 args = { 236 'class': 'meta', 237 'meta': 'element', 238 'name': self.name['meta'] 239 } 240 add_unique(ConfigEntry( 241 configfile=self.configfile, **args), objs) 242 243 super(Path, self).factory(objs) 244 245 def setup(self, objs): 246 '''Resolve path and metadata names to indicies.''' 247 248 self.path = get_index( 249 objs, 'pathname', self.name['path']) 250 self.meta = get_index( 251 objs, 'meta', self.name['meta']) 252 253 super(Path, self).setup(objs) 254 255 256class Property(ConfigEntry): 257 '''Property/interface/metadata association.''' 258 259 def __init__(self, *a, **kw): 260 super(Property, self).__init__(**kw) 261 262 def factory(self, objs): 263 '''Create interface, property name and metadata elements.''' 264 265 args = { 266 'class': 'interface', 267 'interface': 'element', 268 'name': self.name['interface'] 269 } 270 add_unique(ConfigEntry( 271 configfile=self.configfile, **args), objs) 272 273 args = { 274 'class': 'propertyname', 275 'propertyname': 'element', 276 'name': self.name['property'] 277 } 278 add_unique(ConfigEntry( 279 configfile=self.configfile, **args), objs) 280 281 args = { 282 'class': 'meta', 283 'meta': 'element', 284 'name': self.name['meta'] 285 } 286 add_unique(ConfigEntry( 287 configfile=self.configfile, **args), objs) 288 289 super(Property, self).factory(objs) 290 291 def setup(self, objs): 292 '''Resolve interface, property and metadata to indicies.''' 293 294 self.interface = get_index( 295 objs, 'interface', self.name['interface']) 296 self.prop = get_index( 297 objs, 'propertyname', self.name['property']) 298 self.meta = get_index( 299 objs, 'meta', self.name['meta']) 300 301 super(Property, self).setup(objs) 302 303 304class Instance(ConfigEntry): 305 '''Property/Path association.''' 306 307 def __init__(self, *a, **kw): 308 super(Instance, self).__init__(**kw) 309 310 def setup(self, objs): 311 '''Resolve elements to indicies.''' 312 313 self.interface = get_index( 314 objs, 'interface', self.name['property']['interface']) 315 self.prop = get_index( 316 objs, 'propertyname', self.name['property']['property']) 317 self.propmeta = get_index( 318 objs, 'meta', self.name['property']['meta']) 319 self.path = get_index( 320 objs, 'pathname', self.name['path']['path']) 321 self.pathmeta = get_index( 322 objs, 'meta', self.name['path']['meta']) 323 324 super(Instance, self).setup(objs) 325 326 327class Group(ConfigEntry): 328 '''Pop the members keyword for groups.''' 329 330 def __init__(self, *a, **kw): 331 self.members = kw.pop('members') 332 super(Group, self).__init__(**kw) 333 334 335class ImplicitGroup(Group): 336 '''Provide a factory method for groups whose members are 337 not explicitly declared in the config files.''' 338 339 def __init__(self, *a, **kw): 340 super(ImplicitGroup, self).__init__(**kw) 341 342 def factory(self, objs): 343 '''Create group members.''' 344 345 factory = Everything.classmap(self.subclass, 'element') 346 for m in self.members: 347 args = { 348 'class': self.subclass, 349 self.subclass: 'element', 350 'name': m 351 } 352 353 obj = factory(configfile=self.configfile, **args) 354 add_unique(obj, objs) 355 obj.factory(objs) 356 357 super(ImplicitGroup, self).factory(objs) 358 359 360class GroupOfPaths(ImplicitGroup): 361 '''Path group config file directive.''' 362 363 def __init__(self, *a, **kw): 364 super(GroupOfPaths, self).__init__(**kw) 365 366 def setup(self, objs): 367 '''Resolve group members.''' 368 369 def map_member(x): 370 path = get_index( 371 objs, 'pathname', x['path']) 372 meta = get_index( 373 objs, 'meta', x['meta']) 374 return (path, meta) 375 376 self.members = map( 377 map_member, 378 self.members) 379 380 super(GroupOfPaths, self).setup(objs) 381 382 383class GroupOfProperties(ImplicitGroup): 384 '''Property group config file directive.''' 385 386 def __init__(self, *a, **kw): 387 self.datatype = sdbusplus.property.Property( 388 name=kw.get('name'), 389 type=kw.pop('type')).cppTypeName 390 391 super(GroupOfProperties, self).__init__(**kw) 392 393 def setup(self, objs): 394 '''Resolve group members.''' 395 396 def map_member(x): 397 iface = get_index( 398 objs, 'interface', x['interface']) 399 prop = get_index( 400 objs, 'propertyname', x['property']) 401 meta = get_index( 402 objs, 'meta', x['meta']) 403 404 return (iface, prop, meta) 405 406 self.members = map( 407 map_member, 408 self.members) 409 410 super(GroupOfProperties, self).setup(objs) 411 412 413class GroupOfInstances(ImplicitGroup): 414 '''A group of property instances.''' 415 416 def __init__(self, *a, **kw): 417 super(GroupOfInstances, self).__init__(**kw) 418 419 def setup(self, objs): 420 '''Resolve group members.''' 421 422 def map_member(x): 423 path = get_index(objs, 'pathname', x['path']['path']) 424 pathmeta = get_index(objs, 'meta', x['path']['meta']) 425 interface = get_index( 426 objs, 'interface', x['property']['interface']) 427 prop = get_index(objs, 'propertyname', x['property']['property']) 428 propmeta = get_index(objs, 'meta', x['property']['meta']) 429 instance = get_index(objs, 'instance', x) 430 431 return (path, pathmeta, interface, prop, propmeta, instance) 432 433 self.members = map( 434 map_member, 435 self.members) 436 437 super(GroupOfInstances, self).setup(objs) 438 439 440class HasPropertyIndex(ConfigEntry): 441 '''Handle config file directives that require an index to be 442 constructed.''' 443 444 def __init__(self, *a, **kw): 445 self.paths = kw.pop('paths') 446 self.properties = kw.pop('properties') 447 super(HasPropertyIndex, self).__init__(**kw) 448 449 def factory(self, objs): 450 '''Create a group of instances for this index.''' 451 452 members = [] 453 path_group = get_index( 454 objs, 'pathgroup', self.paths, config=self.configfile) 455 property_group = get_index( 456 objs, 'propertygroup', self.properties, config=self.configfile) 457 458 for path in objs['pathgroup'][path_group].members: 459 for prop in objs['propertygroup'][property_group].members: 460 member = { 461 'path': path, 462 'property': prop, 463 } 464 members.append(member) 465 466 args = { 467 'members': members, 468 'class': 'instancegroup', 469 'instancegroup': 'instance', 470 'name': '{0} {1}'.format(self.paths, self.properties) 471 } 472 473 group = GroupOfInstances(configfile=self.configfile, **args) 474 add_unique(group, objs, config=self.configfile) 475 group.factory(objs) 476 477 super(HasPropertyIndex, self).factory(objs) 478 479 def setup(self, objs): 480 '''Resolve path, property, and instance groups.''' 481 482 self.instances = get_index( 483 objs, 484 'instancegroup', 485 '{0} {1}'.format(self.paths, self.properties), 486 config=self.configfile) 487 self.paths = get_index( 488 objs, 489 'pathgroup', 490 self.paths, 491 config=self.configfile) 492 self.properties = get_index( 493 objs, 494 'propertygroup', 495 self.properties, 496 config=self.configfile) 497 self.datatype = objs['propertygroup'][self.properties].datatype 498 499 super(HasPropertyIndex, self).setup(objs) 500 501 502class PropertyWatch(HasPropertyIndex): 503 '''Handle the property watch config file directive.''' 504 505 def __init__(self, *a, **kw): 506 self.callback = kw.pop('callback', None) 507 super(PropertyWatch, self).__init__(**kw) 508 509 def setup(self, objs): 510 '''Resolve optional callback.''' 511 512 if self.callback: 513 self.callback = get_index( 514 objs, 515 'callback', 516 self.callback, 517 config=self.configfile) 518 519 super(PropertyWatch, self).setup(objs) 520 521 522class Callback(HasPropertyIndex): 523 '''Interface and common logic for callbacks.''' 524 525 def __init__(self, *a, **kw): 526 super(Callback, self).__init__(**kw) 527 528 529class ConditionCallback(ConfigEntry, Renderer): 530 '''Handle the journal callback config file directive.''' 531 532 def __init__(self, *a, **kw): 533 self.condition = kw.pop('condition') 534 self.instance = kw.pop('instance') 535 super(ConditionCallback, self).__init__(**kw) 536 537 def factory(self, objs): 538 '''Create a graph instance for this callback.''' 539 540 args = { 541 'configfile': self.configfile, 542 'members': [self.instance], 543 'class': 'callbackgroup', 544 'callbackgroup': 'callback', 545 'name': [self.instance] 546 } 547 548 entry = CallbackGraphEntry(**args) 549 add_unique(entry, objs, config=self.configfile) 550 551 super(ConditionCallback, self).factory(objs) 552 553 def setup(self, objs): 554 '''Resolve condition and graph entry.''' 555 556 self.graph = get_index( 557 objs, 558 'callbackgroup', 559 [self.instance], 560 config=self.configfile) 561 562 self.condition = get_index( 563 objs, 564 'condition', 565 self.name, 566 config=self.configfile) 567 568 super(ConditionCallback, self).setup(objs) 569 570 def construct(self, loader, indent): 571 return self.render( 572 loader, 573 'conditional.mako.cpp', 574 c=self, 575 indent=indent) 576 577 578class Condition(HasPropertyIndex): 579 '''Interface and common logic for conditions.''' 580 581 def __init__(self, *a, **kw): 582 self.callback = kw.pop('callback') 583 super(Condition, self).__init__(**kw) 584 585 def factory(self, objs): 586 '''Create a callback instance for this conditional.''' 587 588 args = { 589 'configfile': self.configfile, 590 'condition': self.name, 591 'class': 'callback', 592 'callback': 'conditional', 593 'instance': self.callback, 594 'name': self.name, 595 } 596 597 callback = ConditionCallback(**args) 598 add_unique(callback, objs, config=self.configfile) 599 callback.factory(objs) 600 601 super(Condition, self).factory(objs) 602 603 604class CountCondition(Condition, Renderer): 605 '''Handle the count condition config file directive.''' 606 607 def __init__(self, *a, **kw): 608 self.countop = kw.pop('countop') 609 self.countbound = kw.pop('countbound') 610 self.op = kw.pop('op') 611 self.bound = kw.pop('bound') 612 super(CountCondition, self).__init__(**kw) 613 614 def construct(self, loader, indent): 615 return self.render( 616 loader, 617 'count.mako.cpp', 618 c=self, 619 indent=indent) 620 621 622class Journal(Callback, Renderer): 623 '''Handle the journal callback config file directive.''' 624 625 def __init__(self, *a, **kw): 626 self.severity = kw.pop('severity') 627 self.message = kw.pop('message') 628 super(Journal, self).__init__(**kw) 629 630 def construct(self, loader, indent): 631 return self.render( 632 loader, 633 'journal.mako.cpp', 634 c=self, 635 indent=indent) 636 637 638class Method(ConfigEntry, Renderer): 639 '''Handle the method callback config file directive.''' 640 641 def __init__(self, *a, **kw): 642 self.service = kw.pop('service') 643 self.path = kw.pop('path') 644 self.interface = kw.pop('interface') 645 self.method = kw.pop('method') 646 self.args = [TrivialArgument(**x) for x in kw.pop('args', {})] 647 super(Method, self).__init__(**kw) 648 649 def factory(self, objs): 650 args = { 651 'class': 'interface', 652 'interface': 'element', 653 'name': self.service 654 } 655 add_unique(ConfigEntry( 656 configfile=self.configfile, **args), objs) 657 658 args = { 659 'class': 'pathname', 660 'pathname': 'element', 661 'name': self.path 662 } 663 add_unique(ConfigEntry( 664 configfile=self.configfile, **args), objs) 665 666 args = { 667 'class': 'interface', 668 'interface': 'element', 669 'name': self.interface 670 } 671 add_unique(ConfigEntry( 672 configfile=self.configfile, **args), objs) 673 674 args = { 675 'class': 'propertyname', 676 'propertyname': 'element', 677 'name': self.method 678 } 679 add_unique(ConfigEntry( 680 configfile=self.configfile, **args), objs) 681 682 super(Method, self).factory(objs) 683 684 def setup(self, objs): 685 '''Resolve elements.''' 686 687 self.service = get_index( 688 objs, 689 'interface', 690 self.service) 691 692 self.path = get_index( 693 objs, 694 'pathname', 695 self.path) 696 697 self.interface = get_index( 698 objs, 699 'interface', 700 self.interface) 701 702 self.method = get_index( 703 objs, 704 'propertyname', 705 self.method) 706 707 super(Method, self).setup(objs) 708 709 def construct(self, loader, indent): 710 return self.render( 711 loader, 712 'method.mako.cpp', 713 c=self, 714 indent=indent) 715 716 717class CallbackGraphEntry(Group): 718 '''An entry in a traversal list for groups of callbacks.''' 719 720 def __init__(self, *a, **kw): 721 super(CallbackGraphEntry, self).__init__(**kw) 722 723 def setup(self, objs): 724 '''Resolve group members.''' 725 726 def map_member(x): 727 return get_index( 728 objs, 'callback', x, config=self.configfile) 729 730 self.members = map( 731 map_member, 732 self.members) 733 734 super(CallbackGraphEntry, self).setup(objs) 735 736 737class GroupOfCallbacks(ConfigEntry, Renderer): 738 '''Handle the callback group config file directive.''' 739 740 def __init__(self, *a, **kw): 741 self.members = kw.pop('members') 742 super(GroupOfCallbacks, self).__init__(**kw) 743 744 def factory(self, objs): 745 '''Create a graph instance for this group of callbacks.''' 746 747 args = { 748 'configfile': self.configfile, 749 'members': self.members, 750 'class': 'callbackgroup', 751 'callbackgroup': 'callback', 752 'name': self.members 753 } 754 755 entry = CallbackGraphEntry(**args) 756 add_unique(entry, objs, config=self.configfile) 757 758 super(GroupOfCallbacks, self).factory(objs) 759 760 def setup(self, objs): 761 '''Resolve graph entry.''' 762 763 self.graph = get_index( 764 objs, 'callbackgroup', self.members, config=self.configfile) 765 766 super(GroupOfCallbacks, self).setup(objs) 767 768 def construct(self, loader, indent): 769 return self.render( 770 loader, 771 'callbackgroup.mako.cpp', 772 c=self, 773 indent=indent) 774 775 776class Everything(Renderer): 777 '''Parse/render entry point.''' 778 779 @staticmethod 780 def classmap(cls, sub=None): 781 '''Map render item class and subclass entries to the appropriate 782 handler methods.''' 783 784 class_map = { 785 'path': { 786 'element': Path, 787 }, 788 'pathgroup': { 789 'path': GroupOfPaths, 790 }, 791 'propertygroup': { 792 'property': GroupOfProperties, 793 }, 794 'property': { 795 'element': Property, 796 }, 797 'watch': { 798 'property': PropertyWatch, 799 }, 800 'instance': { 801 'element': Instance, 802 }, 803 'callback': { 804 'journal': Journal, 805 'group': GroupOfCallbacks, 806 'method': Method, 807 }, 808 'condition': { 809 'count': CountCondition, 810 }, 811 } 812 813 if cls not in class_map: 814 raise NotImplementedError('Unknown class: "{0}"'.format(cls)) 815 if sub not in class_map[cls]: 816 raise NotImplementedError('Unknown {0} type: "{1}"'.format( 817 cls, sub)) 818 819 return class_map[cls][sub] 820 821 @staticmethod 822 def load_one_yaml(path, fd, objs): 823 '''Parse a single YAML file. Parsing occurs in three phases. 824 In the first phase a factory method associated with each 825 configuration file directive is invoked. These factory 826 methods generate more factory methods. In the second 827 phase the factory methods created in the first phase 828 are invoked. In the last phase a callback is invoked on 829 each object created in phase two. Typically the callback 830 resolves references to other configuration file directives.''' 831 832 factory_objs = {} 833 for x in yaml.safe_load(fd.read()) or {}: 834 835 # Create factory object for this config file directive. 836 cls = x['class'] 837 sub = x.get(cls) 838 if cls == 'group': 839 cls = '{0}group'.format(sub) 840 841 factory = Everything.classmap(cls, sub) 842 obj = factory(configfile=path, **x) 843 844 # For a given class of directive, validate the file 845 # doesn't have any duplicate names (duplicates are 846 # ok across config files). 847 if exists(factory_objs, obj.cls, obj.name, config=path): 848 raise NotUniqueError(path, cls, obj.name) 849 850 factory_objs.setdefault(cls, []).append(obj) 851 objs.setdefault(cls, []).append(obj) 852 853 for cls, items in factory_objs.items(): 854 for obj in items: 855 # Add objects for template consumption. 856 obj.factory(objs) 857 858 @staticmethod 859 def load(args): 860 '''Aggregate all the YAML in the input directory 861 into a single aggregate.''' 862 863 objs = {} 864 yaml_files = filter( 865 lambda x: x.endswith('.yaml'), 866 os.listdir(args.inputdir)) 867 868 yaml_files.sort() 869 870 for x in yaml_files: 871 path = os.path.join(args.inputdir, x) 872 with open(path, 'r') as fd: 873 Everything.load_one_yaml(path, fd, objs) 874 875 # Configuration file directives reference each other via 876 # the name attribute; however, when rendered the reference 877 # is just an array index. 878 # 879 # At this point all objects have been created but references 880 # have not been resolved to array indicies. Instruct objects 881 # to do that now. 882 for cls, items in objs.items(): 883 for obj in items: 884 obj.setup(objs) 885 886 return Everything(**objs) 887 888 def __init__(self, *a, **kw): 889 self.pathmeta = kw.pop('path', []) 890 self.paths = kw.pop('pathname', []) 891 self.meta = kw.pop('meta', []) 892 self.pathgroups = kw.pop('pathgroup', []) 893 self.interfaces = kw.pop('interface', []) 894 self.properties = kw.pop('property', []) 895 self.propertynames = kw.pop('propertyname', []) 896 self.propertygroups = kw.pop('propertygroup', []) 897 self.instances = kw.pop('instance', []) 898 self.instancegroups = kw.pop('instancegroup', []) 899 self.watches = kw.pop('watch', []) 900 self.callbacks = kw.pop('callback', []) 901 self.callbackgroups = kw.pop('callbackgroup', []) 902 self.conditions = kw.pop('condition', []) 903 904 super(Everything, self).__init__(**kw) 905 906 def generate_cpp(self, loader): 907 '''Render the template with the provided data.''' 908 with open(args.output, 'w') as fd: 909 fd.write( 910 self.render( 911 loader, 912 args.template, 913 meta=self.meta, 914 properties=self.properties, 915 propertynames=self.propertynames, 916 interfaces=self.interfaces, 917 paths=self.paths, 918 pathmeta=self.pathmeta, 919 pathgroups=self.pathgroups, 920 propertygroups=self.propertygroups, 921 instances=self.instances, 922 watches=self.watches, 923 instancegroups=self.instancegroups, 924 callbacks=self.callbacks, 925 callbackgroups=self.callbackgroups, 926 conditions=self.conditions, 927 indent=Indent())) 928 929if __name__ == '__main__': 930 script_dir = os.path.dirname(os.path.realpath(__file__)) 931 valid_commands = { 932 'generate-cpp': 'generate_cpp', 933 } 934 935 parser = ArgumentParser( 936 description='Phosphor DBus Monitor (PDM) YAML ' 937 'scanner and code generator.') 938 939 parser.add_argument( 940 "-o", "--out", dest="output", 941 default='generated.cpp', 942 help="Generated output file name and path.") 943 parser.add_argument( 944 '-t', '--template', dest='template', 945 default='generated.mako.hpp', 946 help='The top level template to render.') 947 parser.add_argument( 948 '-p', '--template-path', dest='template_search', 949 default=script_dir, 950 help='The space delimited mako template search path.') 951 parser.add_argument( 952 '-d', '--dir', dest='inputdir', 953 default=os.path.join(script_dir, 'example'), 954 help='Location of files to process.') 955 parser.add_argument( 956 'command', metavar='COMMAND', type=str, 957 choices=valid_commands.keys(), 958 help='%s.' % " | ".join(valid_commands.keys())) 959 960 args = parser.parse_args() 961 962 if sys.version_info < (3, 0): 963 lookup = mako.lookup.TemplateLookup( 964 directories=args.template_search.split(), 965 disable_unicode=True) 966 else: 967 lookup = mako.lookup.TemplateLookup( 968 directories=args.template_search.split()) 969 try: 970 function = getattr( 971 Everything.load(args), 972 valid_commands[args.command]) 973 function(lookup) 974 except InvalidConfigError as e: 975 sys.stdout.write('{0}: {1}\n\n'.format(e.config, e.msg)) 976 raise 977