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 if self.name['meta'].upper() != self.name['meta']: 225 raise InvalidConfigError( 226 self.configfile, 227 'Metadata tag "{0}" must be upper case.'.format( 228 self.name['meta'])) 229 230 def factory(self, objs): 231 '''Create path and metadata elements.''' 232 233 args = { 234 'class': 'pathname', 235 'pathname': 'element', 236 'name': self.name['path'] 237 } 238 add_unique(ConfigEntry( 239 configfile=self.configfile, **args), objs) 240 241 args = { 242 'class': 'meta', 243 'meta': 'element', 244 'name': self.name['meta'] 245 } 246 add_unique(ConfigEntry( 247 configfile=self.configfile, **args), objs) 248 249 super(Path, self).factory(objs) 250 251 def setup(self, objs): 252 '''Resolve path and metadata names to indicies.''' 253 254 self.path = get_index( 255 objs, 'pathname', self.name['path']) 256 self.meta = get_index( 257 objs, 'meta', self.name['meta']) 258 259 super(Path, self).setup(objs) 260 261 262class Property(ConfigEntry): 263 '''Property/interface/metadata association.''' 264 265 def __init__(self, *a, **kw): 266 super(Property, self).__init__(**kw) 267 268 if self.name['meta'].upper() != self.name['meta']: 269 raise InvalidConfigError( 270 self.configfile, 271 'Metadata tag "{0}" must be upper case.'.format( 272 self.name['meta'])) 273 274 def factory(self, objs): 275 '''Create interface, property name and metadata elements.''' 276 277 args = { 278 'class': 'interface', 279 'interface': 'element', 280 'name': self.name['interface'] 281 } 282 add_unique(ConfigEntry( 283 configfile=self.configfile, **args), objs) 284 285 args = { 286 'class': 'propertyname', 287 'propertyname': 'element', 288 'name': self.name['property'] 289 } 290 add_unique(ConfigEntry( 291 configfile=self.configfile, **args), objs) 292 293 args = { 294 'class': 'meta', 295 'meta': 'element', 296 'name': self.name['meta'] 297 } 298 add_unique(ConfigEntry( 299 configfile=self.configfile, **args), objs) 300 301 super(Property, self).factory(objs) 302 303 def setup(self, objs): 304 '''Resolve interface, property and metadata to indicies.''' 305 306 self.interface = get_index( 307 objs, 'interface', self.name['interface']) 308 self.prop = get_index( 309 objs, 'propertyname', self.name['property']) 310 self.meta = get_index( 311 objs, 'meta', self.name['meta']) 312 313 super(Property, self).setup(objs) 314 315 316class Instance(ConfigEntry): 317 '''Property/Path association.''' 318 319 def __init__(self, *a, **kw): 320 super(Instance, self).__init__(**kw) 321 322 def setup(self, objs): 323 '''Resolve elements to indicies.''' 324 325 self.interface = get_index( 326 objs, 'interface', self.name['property']['interface']) 327 self.prop = get_index( 328 objs, 'propertyname', self.name['property']['property']) 329 self.propmeta = get_index( 330 objs, 'meta', self.name['property']['meta']) 331 self.path = get_index( 332 objs, 'pathname', self.name['path']['path']) 333 self.pathmeta = get_index( 334 objs, 'meta', self.name['path']['meta']) 335 336 super(Instance, self).setup(objs) 337 338 339class Group(ConfigEntry): 340 '''Pop the members keyword for groups.''' 341 342 def __init__(self, *a, **kw): 343 self.members = kw.pop('members') 344 super(Group, self).__init__(**kw) 345 346 347class ImplicitGroup(Group): 348 '''Provide a factory method for groups whose members are 349 not explicitly declared in the config files.''' 350 351 def __init__(self, *a, **kw): 352 super(ImplicitGroup, self).__init__(**kw) 353 354 def factory(self, objs): 355 '''Create group members.''' 356 357 factory = Everything.classmap(self.subclass, 'element') 358 for m in self.members: 359 args = { 360 'class': self.subclass, 361 self.subclass: 'element', 362 'name': m 363 } 364 365 obj = factory(configfile=self.configfile, **args) 366 add_unique(obj, objs) 367 obj.factory(objs) 368 369 super(ImplicitGroup, self).factory(objs) 370 371 372class GroupOfPaths(ImplicitGroup): 373 '''Path group config file directive.''' 374 375 def __init__(self, *a, **kw): 376 super(GroupOfPaths, self).__init__(**kw) 377 378 def setup(self, objs): 379 '''Resolve group members.''' 380 381 def map_member(x): 382 path = get_index( 383 objs, 'pathname', x['path']) 384 meta = get_index( 385 objs, 'meta', x['meta']) 386 return (path, meta) 387 388 self.members = map( 389 map_member, 390 self.members) 391 392 super(GroupOfPaths, self).setup(objs) 393 394 395class GroupOfProperties(ImplicitGroup): 396 '''Property group config file directive.''' 397 398 def __init__(self, *a, **kw): 399 self.datatype = sdbusplus.property.Property( 400 name=kw.get('name'), 401 type=kw.pop('type')).cppTypeName 402 403 super(GroupOfProperties, self).__init__(**kw) 404 405 def setup(self, objs): 406 '''Resolve group members.''' 407 408 def map_member(x): 409 iface = get_index( 410 objs, 'interface', x['interface']) 411 prop = get_index( 412 objs, 'propertyname', x['property']) 413 meta = get_index( 414 objs, 'meta', x['meta']) 415 416 return (iface, prop, meta) 417 418 self.members = map( 419 map_member, 420 self.members) 421 422 super(GroupOfProperties, self).setup(objs) 423 424 425class GroupOfInstances(ImplicitGroup): 426 '''A group of property instances.''' 427 428 def __init__(self, *a, **kw): 429 super(GroupOfInstances, self).__init__(**kw) 430 431 def setup(self, objs): 432 '''Resolve group members.''' 433 434 def map_member(x): 435 path = get_index(objs, 'pathname', x['path']['path']) 436 pathmeta = get_index(objs, 'meta', x['path']['meta']) 437 interface = get_index( 438 objs, 'interface', x['property']['interface']) 439 prop = get_index(objs, 'propertyname', x['property']['property']) 440 propmeta = get_index(objs, 'meta', x['property']['meta']) 441 instance = get_index(objs, 'instance', x) 442 443 return (path, pathmeta, interface, prop, propmeta, instance) 444 445 self.members = map( 446 map_member, 447 self.members) 448 449 super(GroupOfInstances, self).setup(objs) 450 451 452class HasPropertyIndex(ConfigEntry): 453 '''Handle config file directives that require an index to be 454 constructed.''' 455 456 def __init__(self, *a, **kw): 457 self.paths = kw.pop('paths') 458 self.properties = kw.pop('properties') 459 super(HasPropertyIndex, self).__init__(**kw) 460 461 def factory(self, objs): 462 '''Create a group of instances for this index.''' 463 464 members = [] 465 path_group = get_index( 466 objs, 'pathgroup', self.paths, config=self.configfile) 467 property_group = get_index( 468 objs, 'propertygroup', self.properties, config=self.configfile) 469 470 for path in objs['pathgroup'][path_group].members: 471 for prop in objs['propertygroup'][property_group].members: 472 member = { 473 'path': path, 474 'property': prop, 475 } 476 members.append(member) 477 478 args = { 479 'members': members, 480 'class': 'instancegroup', 481 'instancegroup': 'instance', 482 'name': '{0} {1}'.format(self.paths, self.properties) 483 } 484 485 group = GroupOfInstances(configfile=self.configfile, **args) 486 add_unique(group, objs, config=self.configfile) 487 group.factory(objs) 488 489 super(HasPropertyIndex, self).factory(objs) 490 491 def setup(self, objs): 492 '''Resolve path, property, and instance groups.''' 493 494 self.instances = get_index( 495 objs, 496 'instancegroup', 497 '{0} {1}'.format(self.paths, self.properties), 498 config=self.configfile) 499 self.paths = get_index( 500 objs, 501 'pathgroup', 502 self.paths, 503 config=self.configfile) 504 self.properties = get_index( 505 objs, 506 'propertygroup', 507 self.properties, 508 config=self.configfile) 509 self.datatype = objs['propertygroup'][self.properties].datatype 510 511 super(HasPropertyIndex, self).setup(objs) 512 513 514class PropertyWatch(HasPropertyIndex): 515 '''Handle the property watch config file directive.''' 516 517 def __init__(self, *a, **kw): 518 self.callback = kw.pop('callback', None) 519 super(PropertyWatch, self).__init__(**kw) 520 521 def setup(self, objs): 522 '''Resolve optional callback.''' 523 524 if self.callback: 525 self.callback = get_index( 526 objs, 527 'callback', 528 self.callback, 529 config=self.configfile) 530 531 super(PropertyWatch, self).setup(objs) 532 533 534class Callback(HasPropertyIndex): 535 '''Interface and common logic for callbacks.''' 536 537 def __init__(self, *a, **kw): 538 super(Callback, self).__init__(**kw) 539 540 541class ConditionCallback(ConfigEntry, Renderer): 542 '''Handle the journal callback config file directive.''' 543 544 def __init__(self, *a, **kw): 545 self.condition = kw.pop('condition') 546 self.instance = kw.pop('instance') 547 self.defer = kw.pop('defer', None) 548 super(ConditionCallback, self).__init__(**kw) 549 550 def factory(self, objs): 551 '''Create a graph instance for this callback.''' 552 553 args = { 554 'configfile': self.configfile, 555 'members': [self.instance], 556 'class': 'callbackgroup', 557 'callbackgroup': 'callback', 558 'name': [self.instance] 559 } 560 561 entry = CallbackGraphEntry(**args) 562 add_unique(entry, objs, config=self.configfile) 563 564 super(ConditionCallback, self).factory(objs) 565 566 def setup(self, objs): 567 '''Resolve condition and graph entry.''' 568 569 self.graph = get_index( 570 objs, 571 'callbackgroup', 572 [self.instance], 573 config=self.configfile) 574 575 self.condition = get_index( 576 objs, 577 'condition', 578 self.name, 579 config=self.configfile) 580 581 super(ConditionCallback, self).setup(objs) 582 583 def construct(self, loader, indent): 584 return self.render( 585 loader, 586 'conditional.mako.cpp', 587 c=self, 588 indent=indent) 589 590 591class Condition(HasPropertyIndex): 592 '''Interface and common logic for conditions.''' 593 594 def __init__(self, *a, **kw): 595 self.callback = kw.pop('callback') 596 self.defer = kw.pop('defer', None) 597 super(Condition, self).__init__(**kw) 598 599 def factory(self, objs): 600 '''Create a callback instance for this conditional.''' 601 602 args = { 603 'configfile': self.configfile, 604 'condition': self.name, 605 'class': 'callback', 606 'callback': 'conditional', 607 'instance': self.callback, 608 'name': self.name, 609 'defer': self.defer 610 } 611 612 callback = ConditionCallback(**args) 613 add_unique(callback, objs, config=self.configfile) 614 callback.factory(objs) 615 616 super(Condition, self).factory(objs) 617 618 619class CountCondition(Condition, Renderer): 620 '''Handle the count condition config file directive.''' 621 622 def __init__(self, *a, **kw): 623 self.countop = kw.pop('countop') 624 self.countbound = kw.pop('countbound') 625 self.op = kw.pop('op') 626 self.bound = kw.pop('bound') 627 super(CountCondition, self).__init__(**kw) 628 629 def construct(self, loader, indent): 630 return self.render( 631 loader, 632 'count.mako.cpp', 633 c=self, 634 indent=indent) 635 636 637class Journal(Callback, Renderer): 638 '''Handle the journal callback config file directive.''' 639 640 def __init__(self, *a, **kw): 641 self.severity = kw.pop('severity') 642 self.message = kw.pop('message') 643 super(Journal, self).__init__(**kw) 644 645 def construct(self, loader, indent): 646 return self.render( 647 loader, 648 'journal.mako.cpp', 649 c=self, 650 indent=indent) 651 652 653class Method(ConfigEntry, Renderer): 654 '''Handle the method callback config file directive.''' 655 656 def __init__(self, *a, **kw): 657 self.service = kw.pop('service') 658 self.path = kw.pop('path') 659 self.interface = kw.pop('interface') 660 self.method = kw.pop('method') 661 self.args = [TrivialArgument(**x) for x in kw.pop('args', {})] 662 super(Method, self).__init__(**kw) 663 664 def factory(self, objs): 665 args = { 666 'class': 'interface', 667 'interface': 'element', 668 'name': self.service 669 } 670 add_unique(ConfigEntry( 671 configfile=self.configfile, **args), objs) 672 673 args = { 674 'class': 'pathname', 675 'pathname': 'element', 676 'name': self.path 677 } 678 add_unique(ConfigEntry( 679 configfile=self.configfile, **args), objs) 680 681 args = { 682 'class': 'interface', 683 'interface': 'element', 684 'name': self.interface 685 } 686 add_unique(ConfigEntry( 687 configfile=self.configfile, **args), objs) 688 689 args = { 690 'class': 'propertyname', 691 'propertyname': 'element', 692 'name': self.method 693 } 694 add_unique(ConfigEntry( 695 configfile=self.configfile, **args), objs) 696 697 super(Method, self).factory(objs) 698 699 def setup(self, objs): 700 '''Resolve elements.''' 701 702 self.service = get_index( 703 objs, 704 'interface', 705 self.service) 706 707 self.path = get_index( 708 objs, 709 'pathname', 710 self.path) 711 712 self.interface = get_index( 713 objs, 714 'interface', 715 self.interface) 716 717 self.method = get_index( 718 objs, 719 'propertyname', 720 self.method) 721 722 super(Method, self).setup(objs) 723 724 def construct(self, loader, indent): 725 return self.render( 726 loader, 727 'method.mako.cpp', 728 c=self, 729 indent=indent) 730 731 732class CallbackGraphEntry(Group): 733 '''An entry in a traversal list for groups of callbacks.''' 734 735 def __init__(self, *a, **kw): 736 super(CallbackGraphEntry, self).__init__(**kw) 737 738 def setup(self, objs): 739 '''Resolve group members.''' 740 741 def map_member(x): 742 return get_index( 743 objs, 'callback', x, config=self.configfile) 744 745 self.members = map( 746 map_member, 747 self.members) 748 749 super(CallbackGraphEntry, self).setup(objs) 750 751 752class GroupOfCallbacks(ConfigEntry, Renderer): 753 '''Handle the callback group config file directive.''' 754 755 def __init__(self, *a, **kw): 756 self.members = kw.pop('members') 757 super(GroupOfCallbacks, self).__init__(**kw) 758 759 def factory(self, objs): 760 '''Create a graph instance for this group of callbacks.''' 761 762 args = { 763 'configfile': self.configfile, 764 'members': self.members, 765 'class': 'callbackgroup', 766 'callbackgroup': 'callback', 767 'name': self.members 768 } 769 770 entry = CallbackGraphEntry(**args) 771 add_unique(entry, objs, config=self.configfile) 772 773 super(GroupOfCallbacks, self).factory(objs) 774 775 def setup(self, objs): 776 '''Resolve graph entry.''' 777 778 self.graph = get_index( 779 objs, 'callbackgroup', self.members, config=self.configfile) 780 781 super(GroupOfCallbacks, self).setup(objs) 782 783 def construct(self, loader, indent): 784 return self.render( 785 loader, 786 'callbackgroup.mako.cpp', 787 c=self, 788 indent=indent) 789 790 791class Everything(Renderer): 792 '''Parse/render entry point.''' 793 794 @staticmethod 795 def classmap(cls, sub=None): 796 '''Map render item class and subclass entries to the appropriate 797 handler methods.''' 798 799 class_map = { 800 'path': { 801 'element': Path, 802 }, 803 'pathgroup': { 804 'path': GroupOfPaths, 805 }, 806 'propertygroup': { 807 'property': GroupOfProperties, 808 }, 809 'property': { 810 'element': Property, 811 }, 812 'watch': { 813 'property': PropertyWatch, 814 }, 815 'instance': { 816 'element': Instance, 817 }, 818 'callback': { 819 'journal': Journal, 820 'group': GroupOfCallbacks, 821 'method': Method, 822 }, 823 'condition': { 824 'count': CountCondition, 825 }, 826 } 827 828 if cls not in class_map: 829 raise NotImplementedError('Unknown class: "{0}"'.format(cls)) 830 if sub not in class_map[cls]: 831 raise NotImplementedError('Unknown {0} type: "{1}"'.format( 832 cls, sub)) 833 834 return class_map[cls][sub] 835 836 @staticmethod 837 def load_one_yaml(path, fd, objs): 838 '''Parse a single YAML file. Parsing occurs in three phases. 839 In the first phase a factory method associated with each 840 configuration file directive is invoked. These factory 841 methods generate more factory methods. In the second 842 phase the factory methods created in the first phase 843 are invoked. In the last phase a callback is invoked on 844 each object created in phase two. Typically the callback 845 resolves references to other configuration file directives.''' 846 847 factory_objs = {} 848 for x in yaml.safe_load(fd.read()) or {}: 849 850 # Create factory object for this config file directive. 851 cls = x['class'] 852 sub = x.get(cls) 853 if cls == 'group': 854 cls = '{0}group'.format(sub) 855 856 factory = Everything.classmap(cls, sub) 857 obj = factory(configfile=path, **x) 858 859 # For a given class of directive, validate the file 860 # doesn't have any duplicate names (duplicates are 861 # ok across config files). 862 if exists(factory_objs, obj.cls, obj.name, config=path): 863 raise NotUniqueError(path, cls, obj.name) 864 865 factory_objs.setdefault(cls, []).append(obj) 866 objs.setdefault(cls, []).append(obj) 867 868 for cls, items in factory_objs.items(): 869 for obj in items: 870 # Add objects for template consumption. 871 obj.factory(objs) 872 873 @staticmethod 874 def load(args): 875 '''Aggregate all the YAML in the input directory 876 into a single aggregate.''' 877 878 objs = {} 879 yaml_files = filter( 880 lambda x: x.endswith('.yaml'), 881 os.listdir(args.inputdir)) 882 883 yaml_files.sort() 884 885 for x in yaml_files: 886 path = os.path.join(args.inputdir, x) 887 with open(path, 'r') as fd: 888 Everything.load_one_yaml(path, fd, objs) 889 890 # Configuration file directives reference each other via 891 # the name attribute; however, when rendered the reference 892 # is just an array index. 893 # 894 # At this point all objects have been created but references 895 # have not been resolved to array indicies. Instruct objects 896 # to do that now. 897 for cls, items in objs.items(): 898 for obj in items: 899 obj.setup(objs) 900 901 return Everything(**objs) 902 903 def __init__(self, *a, **kw): 904 self.pathmeta = kw.pop('path', []) 905 self.paths = kw.pop('pathname', []) 906 self.meta = kw.pop('meta', []) 907 self.pathgroups = kw.pop('pathgroup', []) 908 self.interfaces = kw.pop('interface', []) 909 self.properties = kw.pop('property', []) 910 self.propertynames = kw.pop('propertyname', []) 911 self.propertygroups = kw.pop('propertygroup', []) 912 self.instances = kw.pop('instance', []) 913 self.instancegroups = kw.pop('instancegroup', []) 914 self.watches = kw.pop('watch', []) 915 self.callbacks = kw.pop('callback', []) 916 self.callbackgroups = kw.pop('callbackgroup', []) 917 self.conditions = kw.pop('condition', []) 918 919 super(Everything, self).__init__(**kw) 920 921 def generate_cpp(self, loader): 922 '''Render the template with the provided data.''' 923 with open(args.output, 'w') as fd: 924 fd.write( 925 self.render( 926 loader, 927 args.template, 928 meta=self.meta, 929 properties=self.properties, 930 propertynames=self.propertynames, 931 interfaces=self.interfaces, 932 paths=self.paths, 933 pathmeta=self.pathmeta, 934 pathgroups=self.pathgroups, 935 propertygroups=self.propertygroups, 936 instances=self.instances, 937 watches=self.watches, 938 instancegroups=self.instancegroups, 939 callbacks=self.callbacks, 940 callbackgroups=self.callbackgroups, 941 conditions=self.conditions, 942 indent=Indent())) 943 944if __name__ == '__main__': 945 script_dir = os.path.dirname(os.path.realpath(__file__)) 946 valid_commands = { 947 'generate-cpp': 'generate_cpp', 948 } 949 950 parser = ArgumentParser( 951 description='Phosphor DBus Monitor (PDM) YAML ' 952 'scanner and code generator.') 953 954 parser.add_argument( 955 "-o", "--out", dest="output", 956 default='generated.cpp', 957 help="Generated output file name and path.") 958 parser.add_argument( 959 '-t', '--template', dest='template', 960 default='generated.mako.hpp', 961 help='The top level template to render.') 962 parser.add_argument( 963 '-p', '--template-path', dest='template_search', 964 default=script_dir, 965 help='The space delimited mako template search path.') 966 parser.add_argument( 967 '-d', '--dir', dest='inputdir', 968 default=os.path.join(script_dir, 'example'), 969 help='Location of files to process.') 970 parser.add_argument( 971 'command', metavar='COMMAND', type=str, 972 choices=valid_commands.keys(), 973 help='%s.' % " | ".join(valid_commands.keys())) 974 975 args = parser.parse_args() 976 977 if sys.version_info < (3, 0): 978 lookup = mako.lookup.TemplateLookup( 979 directories=args.template_search.split(), 980 disable_unicode=True) 981 else: 982 lookup = mako.lookup.TemplateLookup( 983 directories=args.template_search.split()) 984 try: 985 function = getattr( 986 Everything.load(args), 987 valid_commands[args.command]) 988 function(lookup) 989 except InvalidConfigError as e: 990 sys.stdout.write('{0}: {1}\n\n'.format(e.config, e.msg)) 991 raise 992