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 self.defer = kw.pop('defer', None) 536 super(ConditionCallback, self).__init__(**kw) 537 538 def factory(self, objs): 539 '''Create a graph instance for this callback.''' 540 541 args = { 542 'configfile': self.configfile, 543 'members': [self.instance], 544 'class': 'callbackgroup', 545 'callbackgroup': 'callback', 546 'name': [self.instance] 547 } 548 549 entry = CallbackGraphEntry(**args) 550 add_unique(entry, objs, config=self.configfile) 551 552 super(ConditionCallback, self).factory(objs) 553 554 def setup(self, objs): 555 '''Resolve condition and graph entry.''' 556 557 self.graph = get_index( 558 objs, 559 'callbackgroup', 560 [self.instance], 561 config=self.configfile) 562 563 self.condition = get_index( 564 objs, 565 'condition', 566 self.name, 567 config=self.configfile) 568 569 super(ConditionCallback, self).setup(objs) 570 571 def construct(self, loader, indent): 572 return self.render( 573 loader, 574 'conditional.mako.cpp', 575 c=self, 576 indent=indent) 577 578 579class Condition(HasPropertyIndex): 580 '''Interface and common logic for conditions.''' 581 582 def __init__(self, *a, **kw): 583 self.callback = kw.pop('callback') 584 self.defer = kw.pop('defer', None) 585 super(Condition, self).__init__(**kw) 586 587 def factory(self, objs): 588 '''Create a callback instance for this conditional.''' 589 590 args = { 591 'configfile': self.configfile, 592 'condition': self.name, 593 'class': 'callback', 594 'callback': 'conditional', 595 'instance': self.callback, 596 'name': self.name, 597 'defer': self.defer 598 } 599 600 callback = ConditionCallback(**args) 601 add_unique(callback, objs, config=self.configfile) 602 callback.factory(objs) 603 604 super(Condition, self).factory(objs) 605 606 607class CountCondition(Condition, Renderer): 608 '''Handle the count condition config file directive.''' 609 610 def __init__(self, *a, **kw): 611 self.countop = kw.pop('countop') 612 self.countbound = kw.pop('countbound') 613 self.op = kw.pop('op') 614 self.bound = kw.pop('bound') 615 super(CountCondition, self).__init__(**kw) 616 617 def construct(self, loader, indent): 618 return self.render( 619 loader, 620 'count.mako.cpp', 621 c=self, 622 indent=indent) 623 624 625class Journal(Callback, Renderer): 626 '''Handle the journal callback config file directive.''' 627 628 def __init__(self, *a, **kw): 629 self.severity = kw.pop('severity') 630 self.message = kw.pop('message') 631 super(Journal, self).__init__(**kw) 632 633 def construct(self, loader, indent): 634 return self.render( 635 loader, 636 'journal.mako.cpp', 637 c=self, 638 indent=indent) 639 640 641class Method(ConfigEntry, Renderer): 642 '''Handle the method callback config file directive.''' 643 644 def __init__(self, *a, **kw): 645 self.service = kw.pop('service') 646 self.path = kw.pop('path') 647 self.interface = kw.pop('interface') 648 self.method = kw.pop('method') 649 self.args = [TrivialArgument(**x) for x in kw.pop('args', {})] 650 super(Method, self).__init__(**kw) 651 652 def factory(self, objs): 653 args = { 654 'class': 'interface', 655 'interface': 'element', 656 'name': self.service 657 } 658 add_unique(ConfigEntry( 659 configfile=self.configfile, **args), objs) 660 661 args = { 662 'class': 'pathname', 663 'pathname': 'element', 664 'name': self.path 665 } 666 add_unique(ConfigEntry( 667 configfile=self.configfile, **args), objs) 668 669 args = { 670 'class': 'interface', 671 'interface': 'element', 672 'name': self.interface 673 } 674 add_unique(ConfigEntry( 675 configfile=self.configfile, **args), objs) 676 677 args = { 678 'class': 'propertyname', 679 'propertyname': 'element', 680 'name': self.method 681 } 682 add_unique(ConfigEntry( 683 configfile=self.configfile, **args), objs) 684 685 super(Method, self).factory(objs) 686 687 def setup(self, objs): 688 '''Resolve elements.''' 689 690 self.service = get_index( 691 objs, 692 'interface', 693 self.service) 694 695 self.path = get_index( 696 objs, 697 'pathname', 698 self.path) 699 700 self.interface = get_index( 701 objs, 702 'interface', 703 self.interface) 704 705 self.method = get_index( 706 objs, 707 'propertyname', 708 self.method) 709 710 super(Method, self).setup(objs) 711 712 def construct(self, loader, indent): 713 return self.render( 714 loader, 715 'method.mako.cpp', 716 c=self, 717 indent=indent) 718 719 720class CallbackGraphEntry(Group): 721 '''An entry in a traversal list for groups of callbacks.''' 722 723 def __init__(self, *a, **kw): 724 super(CallbackGraphEntry, self).__init__(**kw) 725 726 def setup(self, objs): 727 '''Resolve group members.''' 728 729 def map_member(x): 730 return get_index( 731 objs, 'callback', x, config=self.configfile) 732 733 self.members = map( 734 map_member, 735 self.members) 736 737 super(CallbackGraphEntry, self).setup(objs) 738 739 740class GroupOfCallbacks(ConfigEntry, Renderer): 741 '''Handle the callback group config file directive.''' 742 743 def __init__(self, *a, **kw): 744 self.members = kw.pop('members') 745 super(GroupOfCallbacks, self).__init__(**kw) 746 747 def factory(self, objs): 748 '''Create a graph instance for this group of callbacks.''' 749 750 args = { 751 'configfile': self.configfile, 752 'members': self.members, 753 'class': 'callbackgroup', 754 'callbackgroup': 'callback', 755 'name': self.members 756 } 757 758 entry = CallbackGraphEntry(**args) 759 add_unique(entry, objs, config=self.configfile) 760 761 super(GroupOfCallbacks, self).factory(objs) 762 763 def setup(self, objs): 764 '''Resolve graph entry.''' 765 766 self.graph = get_index( 767 objs, 'callbackgroup', self.members, config=self.configfile) 768 769 super(GroupOfCallbacks, self).setup(objs) 770 771 def construct(self, loader, indent): 772 return self.render( 773 loader, 774 'callbackgroup.mako.cpp', 775 c=self, 776 indent=indent) 777 778 779class Everything(Renderer): 780 '''Parse/render entry point.''' 781 782 @staticmethod 783 def classmap(cls, sub=None): 784 '''Map render item class and subclass entries to the appropriate 785 handler methods.''' 786 787 class_map = { 788 'path': { 789 'element': Path, 790 }, 791 'pathgroup': { 792 'path': GroupOfPaths, 793 }, 794 'propertygroup': { 795 'property': GroupOfProperties, 796 }, 797 'property': { 798 'element': Property, 799 }, 800 'watch': { 801 'property': PropertyWatch, 802 }, 803 'instance': { 804 'element': Instance, 805 }, 806 'callback': { 807 'journal': Journal, 808 'group': GroupOfCallbacks, 809 'method': Method, 810 }, 811 'condition': { 812 'count': CountCondition, 813 }, 814 } 815 816 if cls not in class_map: 817 raise NotImplementedError('Unknown class: "{0}"'.format(cls)) 818 if sub not in class_map[cls]: 819 raise NotImplementedError('Unknown {0} type: "{1}"'.format( 820 cls, sub)) 821 822 return class_map[cls][sub] 823 824 @staticmethod 825 def load_one_yaml(path, fd, objs): 826 '''Parse a single YAML file. Parsing occurs in three phases. 827 In the first phase a factory method associated with each 828 configuration file directive is invoked. These factory 829 methods generate more factory methods. In the second 830 phase the factory methods created in the first phase 831 are invoked. In the last phase a callback is invoked on 832 each object created in phase two. Typically the callback 833 resolves references to other configuration file directives.''' 834 835 factory_objs = {} 836 for x in yaml.safe_load(fd.read()) or {}: 837 838 # Create factory object for this config file directive. 839 cls = x['class'] 840 sub = x.get(cls) 841 if cls == 'group': 842 cls = '{0}group'.format(sub) 843 844 factory = Everything.classmap(cls, sub) 845 obj = factory(configfile=path, **x) 846 847 # For a given class of directive, validate the file 848 # doesn't have any duplicate names (duplicates are 849 # ok across config files). 850 if exists(factory_objs, obj.cls, obj.name, config=path): 851 raise NotUniqueError(path, cls, obj.name) 852 853 factory_objs.setdefault(cls, []).append(obj) 854 objs.setdefault(cls, []).append(obj) 855 856 for cls, items in factory_objs.items(): 857 for obj in items: 858 # Add objects for template consumption. 859 obj.factory(objs) 860 861 @staticmethod 862 def load(args): 863 '''Aggregate all the YAML in the input directory 864 into a single aggregate.''' 865 866 objs = {} 867 yaml_files = filter( 868 lambda x: x.endswith('.yaml'), 869 os.listdir(args.inputdir)) 870 871 yaml_files.sort() 872 873 for x in yaml_files: 874 path = os.path.join(args.inputdir, x) 875 with open(path, 'r') as fd: 876 Everything.load_one_yaml(path, fd, objs) 877 878 # Configuration file directives reference each other via 879 # the name attribute; however, when rendered the reference 880 # is just an array index. 881 # 882 # At this point all objects have been created but references 883 # have not been resolved to array indicies. Instruct objects 884 # to do that now. 885 for cls, items in objs.items(): 886 for obj in items: 887 obj.setup(objs) 888 889 return Everything(**objs) 890 891 def __init__(self, *a, **kw): 892 self.pathmeta = kw.pop('path', []) 893 self.paths = kw.pop('pathname', []) 894 self.meta = kw.pop('meta', []) 895 self.pathgroups = kw.pop('pathgroup', []) 896 self.interfaces = kw.pop('interface', []) 897 self.properties = kw.pop('property', []) 898 self.propertynames = kw.pop('propertyname', []) 899 self.propertygroups = kw.pop('propertygroup', []) 900 self.instances = kw.pop('instance', []) 901 self.instancegroups = kw.pop('instancegroup', []) 902 self.watches = kw.pop('watch', []) 903 self.callbacks = kw.pop('callback', []) 904 self.callbackgroups = kw.pop('callbackgroup', []) 905 self.conditions = kw.pop('condition', []) 906 907 super(Everything, self).__init__(**kw) 908 909 def generate_cpp(self, loader): 910 '''Render the template with the provided data.''' 911 with open(args.output, 'w') as fd: 912 fd.write( 913 self.render( 914 loader, 915 args.template, 916 meta=self.meta, 917 properties=self.properties, 918 propertynames=self.propertynames, 919 interfaces=self.interfaces, 920 paths=self.paths, 921 pathmeta=self.pathmeta, 922 pathgroups=self.pathgroups, 923 propertygroups=self.propertygroups, 924 instances=self.instances, 925 watches=self.watches, 926 instancegroups=self.instancegroups, 927 callbacks=self.callbacks, 928 callbackgroups=self.callbackgroups, 929 conditions=self.conditions, 930 indent=Indent())) 931 932if __name__ == '__main__': 933 script_dir = os.path.dirname(os.path.realpath(__file__)) 934 valid_commands = { 935 'generate-cpp': 'generate_cpp', 936 } 937 938 parser = ArgumentParser( 939 description='Phosphor DBus Monitor (PDM) YAML ' 940 'scanner and code generator.') 941 942 parser.add_argument( 943 "-o", "--out", dest="output", 944 default='generated.cpp', 945 help="Generated output file name and path.") 946 parser.add_argument( 947 '-t', '--template', dest='template', 948 default='generated.mako.hpp', 949 help='The top level template to render.') 950 parser.add_argument( 951 '-p', '--template-path', dest='template_search', 952 default=script_dir, 953 help='The space delimited mako template search path.') 954 parser.add_argument( 955 '-d', '--dir', dest='inputdir', 956 default=os.path.join(script_dir, 'example'), 957 help='Location of files to process.') 958 parser.add_argument( 959 'command', metavar='COMMAND', type=str, 960 choices=valid_commands.keys(), 961 help='%s.' % " | ".join(valid_commands.keys())) 962 963 args = parser.parse_args() 964 965 if sys.version_info < (3, 0): 966 lookup = mako.lookup.TemplateLookup( 967 directories=args.template_search.split(), 968 disable_unicode=True) 969 else: 970 lookup = mako.lookup.TemplateLookup( 971 directories=args.template_search.split()) 972 try: 973 function = getattr( 974 Everything.load(args), 975 valid_commands[args.command]) 976 function(lookup) 977 except InvalidConfigError as e: 978 sys.stdout.write('{0}: {1}\n\n'.format(e.config, e.msg)) 979 raise 980