xref: /openbmc/phosphor-dbus-monitor/src/pdmgen.py (revision 0df00be00999bc3285b8b0a401b48e42fb862250)
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