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 'int16', 105 'int32', 106 'int64', 107 'uint16', 108 'uint32', 109 'uint64' 110 ] 111 112 def __init__(self, type): 113 self.type = type 114 115 def __call__(self, arg): 116 if 'uint' in self.type: 117 arg = '{0}ull'.format(arg) 118 elif 'int' in self.type: 119 arg = '{0}ll'.format(arg) 120 121 if self.type in self.integer_types: 122 return Cast('static', '{0}_t'.format(self.type))(arg) 123 elif self.type == 'byte': 124 return Cast('static', 'uint8_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.type = kw.pop('type') 400 self.datatype = sdbusplus.property.Property( 401 name=kw.get('name'), 402 type=self.type).cppTypeName 403 404 super(GroupOfProperties, self).__init__(**kw) 405 406 def setup(self, objs): 407 '''Resolve group members.''' 408 409 def map_member(x): 410 iface = get_index( 411 objs, 'interface', x['interface']) 412 prop = get_index( 413 objs, 'propertyname', x['property']) 414 meta = get_index( 415 objs, 'meta', x['meta']) 416 417 return (iface, prop, meta) 418 419 self.members = map( 420 map_member, 421 self.members) 422 423 super(GroupOfProperties, self).setup(objs) 424 425 426class GroupOfInstances(ImplicitGroup): 427 '''A group of property instances.''' 428 429 def __init__(self, *a, **kw): 430 super(GroupOfInstances, self).__init__(**kw) 431 432 def setup(self, objs): 433 '''Resolve group members.''' 434 435 def map_member(x): 436 path = get_index(objs, 'pathname', x['path']['path']) 437 pathmeta = get_index(objs, 'meta', x['path']['meta']) 438 interface = get_index( 439 objs, 'interface', x['property']['interface']) 440 prop = get_index(objs, 'propertyname', x['property']['property']) 441 propmeta = get_index(objs, 'meta', x['property']['meta']) 442 instance = get_index(objs, 'instance', x) 443 444 return (path, pathmeta, interface, prop, propmeta, instance) 445 446 self.members = map( 447 map_member, 448 self.members) 449 450 super(GroupOfInstances, self).setup(objs) 451 452 453class HasPropertyIndex(ConfigEntry): 454 '''Handle config file directives that require an index to be 455 constructed.''' 456 457 def __init__(self, *a, **kw): 458 self.paths = kw.pop('paths') 459 self.properties = kw.pop('properties') 460 super(HasPropertyIndex, self).__init__(**kw) 461 462 def factory(self, objs): 463 '''Create a group of instances for this index.''' 464 465 members = [] 466 path_group = get_index( 467 objs, 'pathgroup', self.paths, config=self.configfile) 468 property_group = get_index( 469 objs, 'propertygroup', self.properties, config=self.configfile) 470 471 for path in objs['pathgroup'][path_group].members: 472 for prop in objs['propertygroup'][property_group].members: 473 member = { 474 'path': path, 475 'property': prop, 476 } 477 members.append(member) 478 479 args = { 480 'members': members, 481 'class': 'instancegroup', 482 'instancegroup': 'instance', 483 'name': '{0} {1}'.format(self.paths, self.properties) 484 } 485 486 group = GroupOfInstances(configfile=self.configfile, **args) 487 add_unique(group, objs, config=self.configfile) 488 group.factory(objs) 489 490 super(HasPropertyIndex, self).factory(objs) 491 492 def setup(self, objs): 493 '''Resolve path, property, and instance groups.''' 494 495 self.instances = get_index( 496 objs, 497 'instancegroup', 498 '{0} {1}'.format(self.paths, self.properties), 499 config=self.configfile) 500 self.paths = get_index( 501 objs, 502 'pathgroup', 503 self.paths, 504 config=self.configfile) 505 self.properties = get_index( 506 objs, 507 'propertygroup', 508 self.properties, 509 config=self.configfile) 510 self.datatype = objs['propertygroup'][self.properties].datatype 511 self.type = objs['propertygroup'][self.properties].type 512 513 super(HasPropertyIndex, self).setup(objs) 514 515 516class PropertyWatch(HasPropertyIndex): 517 '''Handle the property watch config file directive.''' 518 519 def __init__(self, *a, **kw): 520 self.callback = kw.pop('callback', None) 521 super(PropertyWatch, self).__init__(**kw) 522 523 def setup(self, objs): 524 '''Resolve optional callback.''' 525 526 if self.callback: 527 self.callback = get_index( 528 objs, 529 'callback', 530 self.callback, 531 config=self.configfile) 532 533 super(PropertyWatch, self).setup(objs) 534 535 536class Callback(HasPropertyIndex): 537 '''Interface and common logic for callbacks.''' 538 539 def __init__(self, *a, **kw): 540 super(Callback, self).__init__(**kw) 541 542 543class ConditionCallback(ConfigEntry, Renderer): 544 '''Handle the journal callback config file directive.''' 545 546 def __init__(self, *a, **kw): 547 self.condition = kw.pop('condition') 548 self.instance = kw.pop('instance') 549 self.defer = kw.pop('defer', None) 550 super(ConditionCallback, self).__init__(**kw) 551 552 def factory(self, objs): 553 '''Create a graph instance for this callback.''' 554 555 args = { 556 'configfile': self.configfile, 557 'members': [self.instance], 558 'class': 'callbackgroup', 559 'callbackgroup': 'callback', 560 'name': [self.instance] 561 } 562 563 entry = CallbackGraphEntry(**args) 564 add_unique(entry, objs, config=self.configfile) 565 566 super(ConditionCallback, self).factory(objs) 567 568 def setup(self, objs): 569 '''Resolve condition and graph entry.''' 570 571 self.graph = get_index( 572 objs, 573 'callbackgroup', 574 [self.instance], 575 config=self.configfile) 576 577 self.condition = get_index( 578 objs, 579 'condition', 580 self.name, 581 config=self.configfile) 582 583 super(ConditionCallback, self).setup(objs) 584 585 def construct(self, loader, indent): 586 return self.render( 587 loader, 588 'conditional.mako.cpp', 589 c=self, 590 indent=indent) 591 592 593class Condition(HasPropertyIndex): 594 '''Interface and common logic for conditions.''' 595 596 def __init__(self, *a, **kw): 597 self.callback = kw.pop('callback') 598 self.defer = kw.pop('defer', None) 599 super(Condition, self).__init__(**kw) 600 601 def factory(self, objs): 602 '''Create a callback instance for this conditional.''' 603 604 args = { 605 'configfile': self.configfile, 606 'condition': self.name, 607 'class': 'callback', 608 'callback': 'conditional', 609 'instance': self.callback, 610 'name': self.name, 611 'defer': self.defer 612 } 613 614 callback = ConditionCallback(**args) 615 add_unique(callback, objs, config=self.configfile) 616 callback.factory(objs) 617 618 super(Condition, self).factory(objs) 619 620 621class CountCondition(Condition, Renderer): 622 '''Handle the count condition config file directive.''' 623 624 def __init__(self, *a, **kw): 625 self.countop = kw.pop('countop') 626 self.countbound = kw.pop('countbound') 627 self.op = kw.pop('op') 628 self.bound = kw.pop('bound') 629 super(CountCondition, self).__init__(**kw) 630 631 def setup(self, objs): 632 '''Resolve type.''' 633 634 super(CountCondition, self).setup(objs) 635 self.bound = TrivialArgument( 636 type=self.type, 637 value=self.bound) 638 639 def construct(self, loader, indent): 640 return self.render( 641 loader, 642 'count.mako.cpp', 643 c=self, 644 indent=indent) 645 646 647class Journal(Callback, Renderer): 648 '''Handle the journal callback config file directive.''' 649 650 def __init__(self, *a, **kw): 651 self.severity = kw.pop('severity') 652 self.message = kw.pop('message') 653 super(Journal, self).__init__(**kw) 654 655 def construct(self, loader, indent): 656 return self.render( 657 loader, 658 'journal.mako.cpp', 659 c=self, 660 indent=indent) 661 662 663class Method(ConfigEntry, Renderer): 664 '''Handle the method callback config file directive.''' 665 666 def __init__(self, *a, **kw): 667 self.service = kw.pop('service') 668 self.path = kw.pop('path') 669 self.interface = kw.pop('interface') 670 self.method = kw.pop('method') 671 self.args = [TrivialArgument(**x) for x in kw.pop('args', {})] 672 super(Method, self).__init__(**kw) 673 674 def factory(self, objs): 675 args = { 676 'class': 'interface', 677 'interface': 'element', 678 'name': self.service 679 } 680 add_unique(ConfigEntry( 681 configfile=self.configfile, **args), objs) 682 683 args = { 684 'class': 'pathname', 685 'pathname': 'element', 686 'name': self.path 687 } 688 add_unique(ConfigEntry( 689 configfile=self.configfile, **args), objs) 690 691 args = { 692 'class': 'interface', 693 'interface': 'element', 694 'name': self.interface 695 } 696 add_unique(ConfigEntry( 697 configfile=self.configfile, **args), objs) 698 699 args = { 700 'class': 'propertyname', 701 'propertyname': 'element', 702 'name': self.method 703 } 704 add_unique(ConfigEntry( 705 configfile=self.configfile, **args), objs) 706 707 super(Method, self).factory(objs) 708 709 def setup(self, objs): 710 '''Resolve elements.''' 711 712 self.service = get_index( 713 objs, 714 'interface', 715 self.service) 716 717 self.path = get_index( 718 objs, 719 'pathname', 720 self.path) 721 722 self.interface = get_index( 723 objs, 724 'interface', 725 self.interface) 726 727 self.method = get_index( 728 objs, 729 'propertyname', 730 self.method) 731 732 super(Method, self).setup(objs) 733 734 def construct(self, loader, indent): 735 return self.render( 736 loader, 737 'method.mako.cpp', 738 c=self, 739 indent=indent) 740 741 742class CallbackGraphEntry(Group): 743 '''An entry in a traversal list for groups of callbacks.''' 744 745 def __init__(self, *a, **kw): 746 super(CallbackGraphEntry, self).__init__(**kw) 747 748 def setup(self, objs): 749 '''Resolve group members.''' 750 751 def map_member(x): 752 return get_index( 753 objs, 'callback', x, config=self.configfile) 754 755 self.members = map( 756 map_member, 757 self.members) 758 759 super(CallbackGraphEntry, self).setup(objs) 760 761 762class GroupOfCallbacks(ConfigEntry, Renderer): 763 '''Handle the callback group config file directive.''' 764 765 def __init__(self, *a, **kw): 766 self.members = kw.pop('members') 767 super(GroupOfCallbacks, self).__init__(**kw) 768 769 def factory(self, objs): 770 '''Create a graph instance for this group of callbacks.''' 771 772 args = { 773 'configfile': self.configfile, 774 'members': self.members, 775 'class': 'callbackgroup', 776 'callbackgroup': 'callback', 777 'name': self.members 778 } 779 780 entry = CallbackGraphEntry(**args) 781 add_unique(entry, objs, config=self.configfile) 782 783 super(GroupOfCallbacks, self).factory(objs) 784 785 def setup(self, objs): 786 '''Resolve graph entry.''' 787 788 self.graph = get_index( 789 objs, 'callbackgroup', self.members, config=self.configfile) 790 791 super(GroupOfCallbacks, self).setup(objs) 792 793 def construct(self, loader, indent): 794 return self.render( 795 loader, 796 'callbackgroup.mako.cpp', 797 c=self, 798 indent=indent) 799 800 801class Everything(Renderer): 802 '''Parse/render entry point.''' 803 804 @staticmethod 805 def classmap(cls, sub=None): 806 '''Map render item class and subclass entries to the appropriate 807 handler methods.''' 808 809 class_map = { 810 'path': { 811 'element': Path, 812 }, 813 'pathgroup': { 814 'path': GroupOfPaths, 815 }, 816 'propertygroup': { 817 'property': GroupOfProperties, 818 }, 819 'property': { 820 'element': Property, 821 }, 822 'watch': { 823 'property': PropertyWatch, 824 }, 825 'instance': { 826 'element': Instance, 827 }, 828 'callback': { 829 'journal': Journal, 830 'group': GroupOfCallbacks, 831 'method': Method, 832 }, 833 'condition': { 834 'count': CountCondition, 835 }, 836 } 837 838 if cls not in class_map: 839 raise NotImplementedError('Unknown class: "{0}"'.format(cls)) 840 if sub not in class_map[cls]: 841 raise NotImplementedError('Unknown {0} type: "{1}"'.format( 842 cls, sub)) 843 844 return class_map[cls][sub] 845 846 @staticmethod 847 def load_one_yaml(path, fd, objs): 848 '''Parse a single YAML file. Parsing occurs in three phases. 849 In the first phase a factory method associated with each 850 configuration file directive is invoked. These factory 851 methods generate more factory methods. In the second 852 phase the factory methods created in the first phase 853 are invoked. In the last phase a callback is invoked on 854 each object created in phase two. Typically the callback 855 resolves references to other configuration file directives.''' 856 857 factory_objs = {} 858 for x in yaml.safe_load(fd.read()) or {}: 859 860 # Create factory object for this config file directive. 861 cls = x['class'] 862 sub = x.get(cls) 863 if cls == 'group': 864 cls = '{0}group'.format(sub) 865 866 factory = Everything.classmap(cls, sub) 867 obj = factory(configfile=path, **x) 868 869 # For a given class of directive, validate the file 870 # doesn't have any duplicate names (duplicates are 871 # ok across config files). 872 if exists(factory_objs, obj.cls, obj.name, config=path): 873 raise NotUniqueError(path, cls, obj.name) 874 875 factory_objs.setdefault(cls, []).append(obj) 876 objs.setdefault(cls, []).append(obj) 877 878 for cls, items in factory_objs.items(): 879 for obj in items: 880 # Add objects for template consumption. 881 obj.factory(objs) 882 883 @staticmethod 884 def load(args): 885 '''Aggregate all the YAML in the input directory 886 into a single aggregate.''' 887 888 objs = {} 889 yaml_files = filter( 890 lambda x: x.endswith('.yaml'), 891 os.listdir(args.inputdir)) 892 893 yaml_files.sort() 894 895 for x in yaml_files: 896 path = os.path.join(args.inputdir, x) 897 with open(path, 'r') as fd: 898 Everything.load_one_yaml(path, fd, objs) 899 900 # Configuration file directives reference each other via 901 # the name attribute; however, when rendered the reference 902 # is just an array index. 903 # 904 # At this point all objects have been created but references 905 # have not been resolved to array indicies. Instruct objects 906 # to do that now. 907 for cls, items in objs.items(): 908 for obj in items: 909 obj.setup(objs) 910 911 return Everything(**objs) 912 913 def __init__(self, *a, **kw): 914 self.pathmeta = kw.pop('path', []) 915 self.paths = kw.pop('pathname', []) 916 self.meta = kw.pop('meta', []) 917 self.pathgroups = kw.pop('pathgroup', []) 918 self.interfaces = kw.pop('interface', []) 919 self.properties = kw.pop('property', []) 920 self.propertynames = kw.pop('propertyname', []) 921 self.propertygroups = kw.pop('propertygroup', []) 922 self.instances = kw.pop('instance', []) 923 self.instancegroups = kw.pop('instancegroup', []) 924 self.watches = kw.pop('watch', []) 925 self.callbacks = kw.pop('callback', []) 926 self.callbackgroups = kw.pop('callbackgroup', []) 927 self.conditions = kw.pop('condition', []) 928 929 super(Everything, self).__init__(**kw) 930 931 def generate_cpp(self, loader): 932 '''Render the template with the provided data.''' 933 with open(args.output, 'w') as fd: 934 fd.write( 935 self.render( 936 loader, 937 args.template, 938 meta=self.meta, 939 properties=self.properties, 940 propertynames=self.propertynames, 941 interfaces=self.interfaces, 942 paths=self.paths, 943 pathmeta=self.pathmeta, 944 pathgroups=self.pathgroups, 945 propertygroups=self.propertygroups, 946 instances=self.instances, 947 watches=self.watches, 948 instancegroups=self.instancegroups, 949 callbacks=self.callbacks, 950 callbackgroups=self.callbackgroups, 951 conditions=self.conditions, 952 indent=Indent())) 953 954if __name__ == '__main__': 955 script_dir = os.path.dirname(os.path.realpath(__file__)) 956 valid_commands = { 957 'generate-cpp': 'generate_cpp', 958 } 959 960 parser = ArgumentParser( 961 description='Phosphor DBus Monitor (PDM) YAML ' 962 'scanner and code generator.') 963 964 parser.add_argument( 965 "-o", "--out", dest="output", 966 default='generated.cpp', 967 help="Generated output file name and path.") 968 parser.add_argument( 969 '-t', '--template', dest='template', 970 default='generated.mako.hpp', 971 help='The top level template to render.') 972 parser.add_argument( 973 '-p', '--template-path', dest='template_search', 974 default=script_dir, 975 help='The space delimited mako template search path.') 976 parser.add_argument( 977 '-d', '--dir', dest='inputdir', 978 default=os.path.join(script_dir, 'example'), 979 help='Location of files to process.') 980 parser.add_argument( 981 'command', metavar='COMMAND', type=str, 982 choices=valid_commands.keys(), 983 help='%s.' % " | ".join(valid_commands.keys())) 984 985 args = parser.parse_args() 986 987 if sys.version_info < (3, 0): 988 lookup = mako.lookup.TemplateLookup( 989 directories=args.template_search.split(), 990 disable_unicode=True) 991 else: 992 lookup = mako.lookup.TemplateLookup( 993 directories=args.template_search.split()) 994 try: 995 function = getattr( 996 Everything.load(args), 997 valid_commands[args.command]) 998 function(lookup) 999 except InvalidConfigError as e: 1000 sys.stdout.write('{0}: {1}\n\n'.format(e.config, e.msg)) 1001 raise 1002