| Matthew Barth | db440d4 | 2017-04-17 15:49:37 -0500 | [diff] [blame] | 1 | #!/usr/bin/env python | 
|  | 2 |  | 
| Brad Bishop | 34a7acd | 2017-04-27 23:47:23 -0400 | [diff] [blame] | 3 | '''Phosphor DBus Monitor YAML parser and code generator. | 
|  | 4 |  | 
|  | 5 | The 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 |  | 
| Matthew Barth | db440d4 | 2017-04-17 15:49:37 -0500 | [diff] [blame] | 18 | import os | 
|  | 19 | import sys | 
|  | 20 | import yaml | 
| Brad Bishop | 34a7acd | 2017-04-27 23:47:23 -0400 | [diff] [blame] | 21 | import mako.lookup | 
| Matthew Barth | db440d4 | 2017-04-17 15:49:37 -0500 | [diff] [blame] | 22 | from argparse import ArgumentParser | 
| Brad Bishop | 34a7acd | 2017-04-27 23:47:23 -0400 | [diff] [blame] | 23 | from sdbusplus.renderer import Renderer | 
| Brad Bishop | 05b0c1e | 2017-05-23 00:24:01 -0400 | [diff] [blame] | 24 | from sdbusplus.namedelement import NamedElement | 
| Brad Bishop | e73b2c3 | 2017-05-23 18:01:54 -0400 | [diff] [blame] | 25 | import sdbusplus.property | 
| Brad Bishop | 05b0c1e | 2017-05-23 00:24:01 -0400 | [diff] [blame] | 26 |  | 
|  | 27 |  | 
|  | 28 | class 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 |  | 
|  | 39 | class 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 |  | 
|  | 50 | def 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 |  | 
|  | 66 | def 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 |  | 
|  | 78 | def 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) | 
| Matthew Barth | db440d4 | 2017-04-17 15:49:37 -0500 | [diff] [blame] | 85 |  | 
|  | 86 |  | 
| Brad Bishop | 0107989 | 2017-05-26 10:56:45 -0400 | [diff] [blame] | 87 | class 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 |  | 
|  | 100 | class Literal(object): | 
|  | 101 | '''Decorate an argument with a literal operator.''' | 
|  | 102 |  | 
|  | 103 | integer_types = [ | 
| Brad Bishop | 0107989 | 2017-05-26 10:56:45 -0400 | [diff] [blame] | 104 | 'int16', | 
|  | 105 | 'int32', | 
|  | 106 | 'int64', | 
| Brad Bishop | 0107989 | 2017-05-26 10:56:45 -0400 | [diff] [blame] | 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) | 
| Brad Bishop | ec2ed2f | 2017-05-31 21:10:43 -0400 | [diff] [blame] | 123 | elif self.type == 'byte': | 
|  | 124 | return Cast('static', 'uint8_t'.format(self.type))(arg) | 
| Brad Bishop | 0107989 | 2017-05-26 10:56:45 -0400 | [diff] [blame] | 125 |  | 
|  | 126 | if self.type == 'string': | 
|  | 127 | return '{0}s'.format(arg) | 
|  | 128 |  | 
|  | 129 | return arg | 
|  | 130 |  | 
|  | 131 |  | 
|  | 132 | class FixBool(object): | 
|  | 133 | '''Un-capitalize booleans.''' | 
|  | 134 |  | 
|  | 135 | def __call__(self, arg): | 
|  | 136 | return '{0}'.format(arg.lower()) | 
|  | 137 |  | 
|  | 138 |  | 
|  | 139 | class Quote(object): | 
|  | 140 | '''Decorate an argument by quoting it.''' | 
|  | 141 |  | 
|  | 142 | def __call__(self, arg): | 
|  | 143 | return '"{0}"'.format(arg) | 
|  | 144 |  | 
|  | 145 |  | 
|  | 146 | class 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 |  | 
|  | 157 | class 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 |  | 
| Matt Spinler | 80e9b65 | 2017-11-02 14:21:04 -0500 | [diff] [blame^] | 179 |  | 
| Gunnar Mills | 30474cf | 2017-08-11 09:38:54 -0500 | [diff] [blame] | 180 | class Metadata(Argument): | 
|  | 181 | '''Metadata type arguments.''' | 
|  | 182 |  | 
|  | 183 | def __init__(self, **kw): | 
|  | 184 | self.value = kw.pop('value') | 
|  | 185 | self.decorators = kw.pop('decorators', []) | 
|  | 186 | if kw.get('type', None) == 'string': | 
|  | 187 | self.decorators.insert(0, Quote()) | 
|  | 188 |  | 
|  | 189 | super(Metadata, self).__init__(**kw) | 
|  | 190 |  | 
|  | 191 | def argument(self, loader, indent): | 
|  | 192 | a = str(self.value) | 
|  | 193 | for d in self.decorators: | 
|  | 194 | a = d(a) | 
|  | 195 |  | 
|  | 196 | return a | 
| Brad Bishop | 0107989 | 2017-05-26 10:56:45 -0400 | [diff] [blame] | 197 |  | 
| Matt Spinler | 80e9b65 | 2017-11-02 14:21:04 -0500 | [diff] [blame^] | 198 |  | 
| Brad Bishop | 34a7acd | 2017-04-27 23:47:23 -0400 | [diff] [blame] | 199 | class Indent(object): | 
|  | 200 | '''Help templates be depth agnostic.''' | 
| Matthew Barth | db440d4 | 2017-04-17 15:49:37 -0500 | [diff] [blame] | 201 |  | 
| Brad Bishop | 34a7acd | 2017-04-27 23:47:23 -0400 | [diff] [blame] | 202 | def __init__(self, depth=0): | 
|  | 203 | self.depth = depth | 
| Matthew Barth | db440d4 | 2017-04-17 15:49:37 -0500 | [diff] [blame] | 204 |  | 
| Brad Bishop | 34a7acd | 2017-04-27 23:47:23 -0400 | [diff] [blame] | 205 | def __add__(self, depth): | 
|  | 206 | return Indent(self.depth + depth) | 
|  | 207 |  | 
|  | 208 | def __call__(self, depth): | 
|  | 209 | '''Render an indent at the current depth plus depth.''' | 
|  | 210 | return 4*' '*(depth + self.depth) | 
|  | 211 |  | 
|  | 212 |  | 
| Brad Bishop | 05b0c1e | 2017-05-23 00:24:01 -0400 | [diff] [blame] | 213 | class ConfigEntry(NamedElement): | 
|  | 214 | '''Base interface for rendered items.''' | 
|  | 215 |  | 
|  | 216 | def __init__(self, *a, **kw): | 
|  | 217 | '''Pop the configfile/class/subclass keywords.''' | 
|  | 218 |  | 
|  | 219 | self.configfile = kw.pop('configfile') | 
|  | 220 | self.cls = kw.pop('class') | 
|  | 221 | self.subclass = kw.pop(self.cls) | 
|  | 222 | super(ConfigEntry, self).__init__(**kw) | 
|  | 223 |  | 
|  | 224 | def factory(self, objs): | 
|  | 225 | ''' Optional factory interface for subclasses to add | 
|  | 226 | additional items to be rendered.''' | 
|  | 227 |  | 
|  | 228 | pass | 
|  | 229 |  | 
|  | 230 | def setup(self, objs): | 
|  | 231 | ''' Optional setup interface for subclasses, invoked | 
|  | 232 | after all factory methods have been run.''' | 
|  | 233 |  | 
|  | 234 | pass | 
|  | 235 |  | 
|  | 236 |  | 
| Brad Bishop | 0e7df13 | 2017-05-23 17:58:12 -0400 | [diff] [blame] | 237 | class Path(ConfigEntry): | 
|  | 238 | '''Path/metadata association.''' | 
|  | 239 |  | 
|  | 240 | def __init__(self, *a, **kw): | 
|  | 241 | super(Path, self).__init__(**kw) | 
|  | 242 |  | 
| Brad Bishop | babf3b7 | 2017-05-31 19:44:53 -0400 | [diff] [blame] | 243 | if self.name['meta'].upper() != self.name['meta']: | 
|  | 244 | raise InvalidConfigError( | 
|  | 245 | self.configfile, | 
|  | 246 | 'Metadata tag "{0}" must be upper case.'.format( | 
|  | 247 | self.name['meta'])) | 
|  | 248 |  | 
| Brad Bishop | 0e7df13 | 2017-05-23 17:58:12 -0400 | [diff] [blame] | 249 | def factory(self, objs): | 
|  | 250 | '''Create path and metadata elements.''' | 
|  | 251 |  | 
|  | 252 | args = { | 
|  | 253 | 'class': 'pathname', | 
|  | 254 | 'pathname': 'element', | 
|  | 255 | 'name': self.name['path'] | 
|  | 256 | } | 
|  | 257 | add_unique(ConfigEntry( | 
|  | 258 | configfile=self.configfile, **args), objs) | 
|  | 259 |  | 
|  | 260 | args = { | 
|  | 261 | 'class': 'meta', | 
|  | 262 | 'meta': 'element', | 
|  | 263 | 'name': self.name['meta'] | 
|  | 264 | } | 
|  | 265 | add_unique(ConfigEntry( | 
|  | 266 | configfile=self.configfile, **args), objs) | 
|  | 267 |  | 
|  | 268 | super(Path, self).factory(objs) | 
|  | 269 |  | 
|  | 270 | def setup(self, objs): | 
| Gunnar Mills | 78199b4 | 2017-10-25 16:30:18 -0500 | [diff] [blame] | 271 | '''Resolve path and metadata names to indices.''' | 
| Brad Bishop | 0e7df13 | 2017-05-23 17:58:12 -0400 | [diff] [blame] | 272 |  | 
|  | 273 | self.path = get_index( | 
|  | 274 | objs, 'pathname', self.name['path']) | 
|  | 275 | self.meta = get_index( | 
|  | 276 | objs, 'meta', self.name['meta']) | 
|  | 277 |  | 
|  | 278 | super(Path, self).setup(objs) | 
|  | 279 |  | 
|  | 280 |  | 
| Brad Bishop | e73b2c3 | 2017-05-23 18:01:54 -0400 | [diff] [blame] | 281 | class Property(ConfigEntry): | 
|  | 282 | '''Property/interface/metadata association.''' | 
|  | 283 |  | 
|  | 284 | def __init__(self, *a, **kw): | 
|  | 285 | super(Property, self).__init__(**kw) | 
|  | 286 |  | 
| Brad Bishop | babf3b7 | 2017-05-31 19:44:53 -0400 | [diff] [blame] | 287 | if self.name['meta'].upper() != self.name['meta']: | 
|  | 288 | raise InvalidConfigError( | 
|  | 289 | self.configfile, | 
|  | 290 | 'Metadata tag "{0}" must be upper case.'.format( | 
|  | 291 | self.name['meta'])) | 
|  | 292 |  | 
| Brad Bishop | e73b2c3 | 2017-05-23 18:01:54 -0400 | [diff] [blame] | 293 | def factory(self, objs): | 
|  | 294 | '''Create interface, property name and metadata elements.''' | 
|  | 295 |  | 
|  | 296 | args = { | 
|  | 297 | 'class': 'interface', | 
|  | 298 | 'interface': 'element', | 
|  | 299 | 'name': self.name['interface'] | 
|  | 300 | } | 
|  | 301 | add_unique(ConfigEntry( | 
|  | 302 | configfile=self.configfile, **args), objs) | 
|  | 303 |  | 
|  | 304 | args = { | 
|  | 305 | 'class': 'propertyname', | 
|  | 306 | 'propertyname': 'element', | 
|  | 307 | 'name': self.name['property'] | 
|  | 308 | } | 
|  | 309 | add_unique(ConfigEntry( | 
|  | 310 | configfile=self.configfile, **args), objs) | 
|  | 311 |  | 
|  | 312 | args = { | 
|  | 313 | 'class': 'meta', | 
|  | 314 | 'meta': 'element', | 
|  | 315 | 'name': self.name['meta'] | 
|  | 316 | } | 
|  | 317 | add_unique(ConfigEntry( | 
|  | 318 | configfile=self.configfile, **args), objs) | 
|  | 319 |  | 
|  | 320 | super(Property, self).factory(objs) | 
|  | 321 |  | 
|  | 322 | def setup(self, objs): | 
| Gunnar Mills | 78199b4 | 2017-10-25 16:30:18 -0500 | [diff] [blame] | 323 | '''Resolve interface, property and metadata to indices.''' | 
| Brad Bishop | e73b2c3 | 2017-05-23 18:01:54 -0400 | [diff] [blame] | 324 |  | 
|  | 325 | self.interface = get_index( | 
|  | 326 | objs, 'interface', self.name['interface']) | 
|  | 327 | self.prop = get_index( | 
|  | 328 | objs, 'propertyname', self.name['property']) | 
|  | 329 | self.meta = get_index( | 
|  | 330 | objs, 'meta', self.name['meta']) | 
|  | 331 |  | 
|  | 332 | super(Property, self).setup(objs) | 
|  | 333 |  | 
|  | 334 |  | 
| Brad Bishop | 4b916f1 | 2017-05-23 18:06:38 -0400 | [diff] [blame] | 335 | class Instance(ConfigEntry): | 
|  | 336 | '''Property/Path association.''' | 
|  | 337 |  | 
|  | 338 | def __init__(self, *a, **kw): | 
|  | 339 | super(Instance, self).__init__(**kw) | 
|  | 340 |  | 
|  | 341 | def setup(self, objs): | 
| Gunnar Mills | 78199b4 | 2017-10-25 16:30:18 -0500 | [diff] [blame] | 342 | '''Resolve elements to indices.''' | 
| Brad Bishop | 4b916f1 | 2017-05-23 18:06:38 -0400 | [diff] [blame] | 343 |  | 
|  | 344 | self.interface = get_index( | 
|  | 345 | objs, 'interface', self.name['property']['interface']) | 
|  | 346 | self.prop = get_index( | 
|  | 347 | objs, 'propertyname', self.name['property']['property']) | 
|  | 348 | self.propmeta = get_index( | 
|  | 349 | objs, 'meta', self.name['property']['meta']) | 
|  | 350 | self.path = get_index( | 
|  | 351 | objs, 'pathname', self.name['path']['path']) | 
|  | 352 | self.pathmeta = get_index( | 
|  | 353 | objs, 'meta', self.name['path']['meta']) | 
|  | 354 |  | 
|  | 355 | super(Instance, self).setup(objs) | 
|  | 356 |  | 
|  | 357 |  | 
| Brad Bishop | 05b0c1e | 2017-05-23 00:24:01 -0400 | [diff] [blame] | 358 | class Group(ConfigEntry): | 
|  | 359 | '''Pop the members keyword for groups.''' | 
|  | 360 |  | 
|  | 361 | def __init__(self, *a, **kw): | 
|  | 362 | self.members = kw.pop('members') | 
|  | 363 | super(Group, self).__init__(**kw) | 
|  | 364 |  | 
|  | 365 |  | 
|  | 366 | class ImplicitGroup(Group): | 
|  | 367 | '''Provide a factory method for groups whose members are | 
|  | 368 | not explicitly declared in the config files.''' | 
|  | 369 |  | 
|  | 370 | def __init__(self, *a, **kw): | 
|  | 371 | super(ImplicitGroup, self).__init__(**kw) | 
|  | 372 |  | 
|  | 373 | def factory(self, objs): | 
|  | 374 | '''Create group members.''' | 
|  | 375 |  | 
|  | 376 | factory = Everything.classmap(self.subclass, 'element') | 
|  | 377 | for m in self.members: | 
|  | 378 | args = { | 
|  | 379 | 'class': self.subclass, | 
|  | 380 | self.subclass: 'element', | 
|  | 381 | 'name': m | 
|  | 382 | } | 
|  | 383 |  | 
|  | 384 | obj = factory(configfile=self.configfile, **args) | 
|  | 385 | add_unique(obj, objs) | 
|  | 386 | obj.factory(objs) | 
|  | 387 |  | 
|  | 388 | super(ImplicitGroup, self).factory(objs) | 
|  | 389 |  | 
|  | 390 |  | 
| Brad Bishop | 0e7df13 | 2017-05-23 17:58:12 -0400 | [diff] [blame] | 391 | class GroupOfPaths(ImplicitGroup): | 
|  | 392 | '''Path group config file directive.''' | 
|  | 393 |  | 
|  | 394 | def __init__(self, *a, **kw): | 
|  | 395 | super(GroupOfPaths, self).__init__(**kw) | 
|  | 396 |  | 
|  | 397 | def setup(self, objs): | 
|  | 398 | '''Resolve group members.''' | 
|  | 399 |  | 
|  | 400 | def map_member(x): | 
|  | 401 | path = get_index( | 
|  | 402 | objs, 'pathname', x['path']) | 
|  | 403 | meta = get_index( | 
|  | 404 | objs, 'meta', x['meta']) | 
|  | 405 | return (path, meta) | 
|  | 406 |  | 
|  | 407 | self.members = map( | 
|  | 408 | map_member, | 
|  | 409 | self.members) | 
|  | 410 |  | 
|  | 411 | super(GroupOfPaths, self).setup(objs) | 
|  | 412 |  | 
|  | 413 |  | 
| Brad Bishop | e73b2c3 | 2017-05-23 18:01:54 -0400 | [diff] [blame] | 414 | class GroupOfProperties(ImplicitGroup): | 
|  | 415 | '''Property group config file directive.''' | 
|  | 416 |  | 
|  | 417 | def __init__(self, *a, **kw): | 
| Brad Bishop | ec2ed2f | 2017-05-31 21:10:43 -0400 | [diff] [blame] | 418 | self.type = kw.pop('type') | 
| Brad Bishop | e73b2c3 | 2017-05-23 18:01:54 -0400 | [diff] [blame] | 419 | self.datatype = sdbusplus.property.Property( | 
|  | 420 | name=kw.get('name'), | 
| Brad Bishop | ec2ed2f | 2017-05-31 21:10:43 -0400 | [diff] [blame] | 421 | type=self.type).cppTypeName | 
| Brad Bishop | e73b2c3 | 2017-05-23 18:01:54 -0400 | [diff] [blame] | 422 |  | 
|  | 423 | super(GroupOfProperties, self).__init__(**kw) | 
|  | 424 |  | 
|  | 425 | def setup(self, objs): | 
|  | 426 | '''Resolve group members.''' | 
|  | 427 |  | 
|  | 428 | def map_member(x): | 
|  | 429 | iface = get_index( | 
|  | 430 | objs, 'interface', x['interface']) | 
|  | 431 | prop = get_index( | 
|  | 432 | objs, 'propertyname', x['property']) | 
|  | 433 | meta = get_index( | 
|  | 434 | objs, 'meta', x['meta']) | 
|  | 435 |  | 
|  | 436 | return (iface, prop, meta) | 
|  | 437 |  | 
|  | 438 | self.members = map( | 
|  | 439 | map_member, | 
|  | 440 | self.members) | 
|  | 441 |  | 
|  | 442 | super(GroupOfProperties, self).setup(objs) | 
|  | 443 |  | 
|  | 444 |  | 
| Brad Bishop | 4b916f1 | 2017-05-23 18:06:38 -0400 | [diff] [blame] | 445 | class GroupOfInstances(ImplicitGroup): | 
|  | 446 | '''A group of property instances.''' | 
|  | 447 |  | 
|  | 448 | def __init__(self, *a, **kw): | 
|  | 449 | super(GroupOfInstances, self).__init__(**kw) | 
|  | 450 |  | 
|  | 451 | def setup(self, objs): | 
|  | 452 | '''Resolve group members.''' | 
|  | 453 |  | 
|  | 454 | def map_member(x): | 
|  | 455 | path = get_index(objs, 'pathname', x['path']['path']) | 
|  | 456 | pathmeta = get_index(objs, 'meta', x['path']['meta']) | 
|  | 457 | interface = get_index( | 
|  | 458 | objs, 'interface', x['property']['interface']) | 
|  | 459 | prop = get_index(objs, 'propertyname', x['property']['property']) | 
|  | 460 | propmeta = get_index(objs, 'meta', x['property']['meta']) | 
|  | 461 | instance = get_index(objs, 'instance', x) | 
|  | 462 |  | 
|  | 463 | return (path, pathmeta, interface, prop, propmeta, instance) | 
|  | 464 |  | 
|  | 465 | self.members = map( | 
|  | 466 | map_member, | 
|  | 467 | self.members) | 
|  | 468 |  | 
|  | 469 | super(GroupOfInstances, self).setup(objs) | 
|  | 470 |  | 
|  | 471 |  | 
|  | 472 | class HasPropertyIndex(ConfigEntry): | 
|  | 473 | '''Handle config file directives that require an index to be | 
|  | 474 | constructed.''' | 
|  | 475 |  | 
|  | 476 | def __init__(self, *a, **kw): | 
|  | 477 | self.paths = kw.pop('paths') | 
|  | 478 | self.properties = kw.pop('properties') | 
|  | 479 | super(HasPropertyIndex, self).__init__(**kw) | 
|  | 480 |  | 
|  | 481 | def factory(self, objs): | 
|  | 482 | '''Create a group of instances for this index.''' | 
|  | 483 |  | 
|  | 484 | members = [] | 
|  | 485 | path_group = get_index( | 
|  | 486 | objs, 'pathgroup', self.paths, config=self.configfile) | 
|  | 487 | property_group = get_index( | 
|  | 488 | objs, 'propertygroup', self.properties, config=self.configfile) | 
|  | 489 |  | 
|  | 490 | for path in objs['pathgroup'][path_group].members: | 
|  | 491 | for prop in objs['propertygroup'][property_group].members: | 
|  | 492 | member = { | 
|  | 493 | 'path': path, | 
|  | 494 | 'property': prop, | 
|  | 495 | } | 
|  | 496 | members.append(member) | 
|  | 497 |  | 
|  | 498 | args = { | 
|  | 499 | 'members': members, | 
|  | 500 | 'class': 'instancegroup', | 
|  | 501 | 'instancegroup': 'instance', | 
|  | 502 | 'name': '{0} {1}'.format(self.paths, self.properties) | 
|  | 503 | } | 
|  | 504 |  | 
|  | 505 | group = GroupOfInstances(configfile=self.configfile, **args) | 
|  | 506 | add_unique(group, objs, config=self.configfile) | 
|  | 507 | group.factory(objs) | 
|  | 508 |  | 
|  | 509 | super(HasPropertyIndex, self).factory(objs) | 
|  | 510 |  | 
|  | 511 | def setup(self, objs): | 
|  | 512 | '''Resolve path, property, and instance groups.''' | 
|  | 513 |  | 
|  | 514 | self.instances = get_index( | 
|  | 515 | objs, | 
|  | 516 | 'instancegroup', | 
|  | 517 | '{0} {1}'.format(self.paths, self.properties), | 
|  | 518 | config=self.configfile) | 
|  | 519 | self.paths = get_index( | 
|  | 520 | objs, | 
|  | 521 | 'pathgroup', | 
|  | 522 | self.paths, | 
|  | 523 | config=self.configfile) | 
|  | 524 | self.properties = get_index( | 
|  | 525 | objs, | 
|  | 526 | 'propertygroup', | 
|  | 527 | self.properties, | 
|  | 528 | config=self.configfile) | 
|  | 529 | self.datatype = objs['propertygroup'][self.properties].datatype | 
| Brad Bishop | ec2ed2f | 2017-05-31 21:10:43 -0400 | [diff] [blame] | 530 | self.type = objs['propertygroup'][self.properties].type | 
| Brad Bishop | 4b916f1 | 2017-05-23 18:06:38 -0400 | [diff] [blame] | 531 |  | 
|  | 532 | super(HasPropertyIndex, self).setup(objs) | 
|  | 533 |  | 
|  | 534 |  | 
|  | 535 | class PropertyWatch(HasPropertyIndex): | 
|  | 536 | '''Handle the property watch config file directive.''' | 
|  | 537 |  | 
|  | 538 | def __init__(self, *a, **kw): | 
| Brad Bishop | fccdc39 | 2017-05-22 21:11:09 -0400 | [diff] [blame] | 539 | self.callback = kw.pop('callback', None) | 
| Brad Bishop | 4b916f1 | 2017-05-23 18:06:38 -0400 | [diff] [blame] | 540 | super(PropertyWatch, self).__init__(**kw) | 
|  | 541 |  | 
| Brad Bishop | fccdc39 | 2017-05-22 21:11:09 -0400 | [diff] [blame] | 542 | def setup(self, objs): | 
|  | 543 | '''Resolve optional callback.''' | 
|  | 544 |  | 
|  | 545 | if self.callback: | 
|  | 546 | self.callback = get_index( | 
|  | 547 | objs, | 
|  | 548 | 'callback', | 
|  | 549 | self.callback, | 
|  | 550 | config=self.configfile) | 
|  | 551 |  | 
|  | 552 | super(PropertyWatch, self).setup(objs) | 
|  | 553 |  | 
| Brad Bishop | 4b916f1 | 2017-05-23 18:06:38 -0400 | [diff] [blame] | 554 |  | 
| Brad Bishop | c1283ae | 2017-05-20 21:42:38 -0400 | [diff] [blame] | 555 | class Callback(HasPropertyIndex): | 
|  | 556 | '''Interface and common logic for callbacks.''' | 
|  | 557 |  | 
|  | 558 | def __init__(self, *a, **kw): | 
|  | 559 | super(Callback, self).__init__(**kw) | 
|  | 560 |  | 
|  | 561 |  | 
| Brad Bishop | 4041d72 | 2017-05-21 10:06:07 -0400 | [diff] [blame] | 562 | class ConditionCallback(ConfigEntry, Renderer): | 
|  | 563 | '''Handle the journal callback config file directive.''' | 
|  | 564 |  | 
|  | 565 | def __init__(self, *a, **kw): | 
|  | 566 | self.condition = kw.pop('condition') | 
|  | 567 | self.instance = kw.pop('instance') | 
| Brad Bishop | 3539db6 | 2017-05-30 14:21:12 -0400 | [diff] [blame] | 568 | self.defer = kw.pop('defer', None) | 
| Brad Bishop | 4041d72 | 2017-05-21 10:06:07 -0400 | [diff] [blame] | 569 | super(ConditionCallback, self).__init__(**kw) | 
|  | 570 |  | 
|  | 571 | def factory(self, objs): | 
|  | 572 | '''Create a graph instance for this callback.''' | 
|  | 573 |  | 
|  | 574 | args = { | 
|  | 575 | 'configfile': self.configfile, | 
|  | 576 | 'members': [self.instance], | 
|  | 577 | 'class': 'callbackgroup', | 
|  | 578 | 'callbackgroup': 'callback', | 
|  | 579 | 'name': [self.instance] | 
|  | 580 | } | 
|  | 581 |  | 
|  | 582 | entry = CallbackGraphEntry(**args) | 
|  | 583 | add_unique(entry, objs, config=self.configfile) | 
|  | 584 |  | 
|  | 585 | super(ConditionCallback, self).factory(objs) | 
|  | 586 |  | 
|  | 587 | def setup(self, objs): | 
|  | 588 | '''Resolve condition and graph entry.''' | 
|  | 589 |  | 
|  | 590 | self.graph = get_index( | 
|  | 591 | objs, | 
|  | 592 | 'callbackgroup', | 
|  | 593 | [self.instance], | 
|  | 594 | config=self.configfile) | 
|  | 595 |  | 
|  | 596 | self.condition = get_index( | 
|  | 597 | objs, | 
|  | 598 | 'condition', | 
|  | 599 | self.name, | 
|  | 600 | config=self.configfile) | 
|  | 601 |  | 
|  | 602 | super(ConditionCallback, self).setup(objs) | 
|  | 603 |  | 
|  | 604 | def construct(self, loader, indent): | 
|  | 605 | return self.render( | 
|  | 606 | loader, | 
|  | 607 | 'conditional.mako.cpp', | 
|  | 608 | c=self, | 
|  | 609 | indent=indent) | 
|  | 610 |  | 
|  | 611 |  | 
|  | 612 | class Condition(HasPropertyIndex): | 
|  | 613 | '''Interface and common logic for conditions.''' | 
|  | 614 |  | 
|  | 615 | def __init__(self, *a, **kw): | 
|  | 616 | self.callback = kw.pop('callback') | 
| Brad Bishop | 3539db6 | 2017-05-30 14:21:12 -0400 | [diff] [blame] | 617 | self.defer = kw.pop('defer', None) | 
| Brad Bishop | 4041d72 | 2017-05-21 10:06:07 -0400 | [diff] [blame] | 618 | super(Condition, self).__init__(**kw) | 
|  | 619 |  | 
|  | 620 | def factory(self, objs): | 
|  | 621 | '''Create a callback instance for this conditional.''' | 
|  | 622 |  | 
|  | 623 | args = { | 
|  | 624 | 'configfile': self.configfile, | 
|  | 625 | 'condition': self.name, | 
|  | 626 | 'class': 'callback', | 
|  | 627 | 'callback': 'conditional', | 
|  | 628 | 'instance': self.callback, | 
|  | 629 | 'name': self.name, | 
| Brad Bishop | 3539db6 | 2017-05-30 14:21:12 -0400 | [diff] [blame] | 630 | 'defer': self.defer | 
| Brad Bishop | 4041d72 | 2017-05-21 10:06:07 -0400 | [diff] [blame] | 631 | } | 
|  | 632 |  | 
|  | 633 | callback = ConditionCallback(**args) | 
|  | 634 | add_unique(callback, objs, config=self.configfile) | 
|  | 635 | callback.factory(objs) | 
|  | 636 |  | 
|  | 637 | super(Condition, self).factory(objs) | 
|  | 638 |  | 
|  | 639 |  | 
|  | 640 | class CountCondition(Condition, Renderer): | 
|  | 641 | '''Handle the count condition config file directive.''' | 
|  | 642 |  | 
|  | 643 | def __init__(self, *a, **kw): | 
|  | 644 | self.countop = kw.pop('countop') | 
|  | 645 | self.countbound = kw.pop('countbound') | 
|  | 646 | self.op = kw.pop('op') | 
|  | 647 | self.bound = kw.pop('bound') | 
|  | 648 | super(CountCondition, self).__init__(**kw) | 
|  | 649 |  | 
| Brad Bishop | ec2ed2f | 2017-05-31 21:10:43 -0400 | [diff] [blame] | 650 | def setup(self, objs): | 
|  | 651 | '''Resolve type.''' | 
|  | 652 |  | 
|  | 653 | super(CountCondition, self).setup(objs) | 
|  | 654 | self.bound = TrivialArgument( | 
|  | 655 | type=self.type, | 
|  | 656 | value=self.bound) | 
|  | 657 |  | 
| Brad Bishop | 4041d72 | 2017-05-21 10:06:07 -0400 | [diff] [blame] | 658 | def construct(self, loader, indent): | 
|  | 659 | return self.render( | 
|  | 660 | loader, | 
|  | 661 | 'count.mako.cpp', | 
|  | 662 | c=self, | 
|  | 663 | indent=indent) | 
|  | 664 |  | 
|  | 665 |  | 
| Brad Bishop | c1283ae | 2017-05-20 21:42:38 -0400 | [diff] [blame] | 666 | class Journal(Callback, Renderer): | 
|  | 667 | '''Handle the journal callback config file directive.''' | 
|  | 668 |  | 
|  | 669 | def __init__(self, *a, **kw): | 
|  | 670 | self.severity = kw.pop('severity') | 
|  | 671 | self.message = kw.pop('message') | 
|  | 672 | super(Journal, self).__init__(**kw) | 
|  | 673 |  | 
|  | 674 | def construct(self, loader, indent): | 
|  | 675 | return self.render( | 
|  | 676 | loader, | 
|  | 677 | 'journal.mako.cpp', | 
|  | 678 | c=self, | 
|  | 679 | indent=indent) | 
|  | 680 |  | 
|  | 681 |  | 
| Gunnar Mills | d5faea3 | 2017-08-08 14:19:36 -0500 | [diff] [blame] | 682 | class Elog(Callback, Renderer): | 
|  | 683 | '''Handle the elog callback config file directive.''' | 
|  | 684 |  | 
|  | 685 | def __init__(self, *a, **kw): | 
|  | 686 | self.error = kw.pop('error') | 
| Gunnar Mills | 30474cf | 2017-08-11 09:38:54 -0500 | [diff] [blame] | 687 | self.metadata = [Metadata(**x) for x in kw.pop('metadata', {})] | 
| Gunnar Mills | d5faea3 | 2017-08-08 14:19:36 -0500 | [diff] [blame] | 688 | super(Elog, self).__init__(**kw) | 
|  | 689 |  | 
|  | 690 | def construct(self, loader, indent): | 
| Gunnar Mills | d5faea3 | 2017-08-08 14:19:36 -0500 | [diff] [blame] | 691 | with open('errors.hpp', 'a') as fd: | 
|  | 692 | fd.write( | 
|  | 693 | self.render( | 
|  | 694 | loader, | 
|  | 695 | 'errors.mako.hpp', | 
|  | 696 | c=self)) | 
|  | 697 | return self.render( | 
|  | 698 | loader, | 
|  | 699 | 'elog.mako.cpp', | 
|  | 700 | c=self, | 
|  | 701 | indent=indent) | 
|  | 702 |  | 
|  | 703 |  | 
| Matt Spinler | 1d6ca48 | 2017-11-01 10:48:02 -0500 | [diff] [blame] | 704 | class ResolveCallout(Callback, Renderer): | 
|  | 705 | '''Handle the 'resolve callout' callback config file directive.''' | 
|  | 706 |  | 
|  | 707 | def __init__(self, *a, **kw): | 
|  | 708 | self.callout = kw.pop('callout') | 
|  | 709 | super(ResolveCallout, self).__init__(**kw) | 
|  | 710 |  | 
|  | 711 | def construct(self, loader, indent): | 
|  | 712 | return self.render( | 
|  | 713 | loader, | 
|  | 714 | 'resolve_errors.mako.cpp', | 
|  | 715 | c=self, | 
|  | 716 | indent=indent) | 
|  | 717 |  | 
|  | 718 |  | 
| Brad Bishop | 0df00be | 2017-05-25 23:38:37 -0400 | [diff] [blame] | 719 | class Method(ConfigEntry, Renderer): | 
|  | 720 | '''Handle the method callback config file directive.''' | 
|  | 721 |  | 
|  | 722 | def __init__(self, *a, **kw): | 
|  | 723 | self.service = kw.pop('service') | 
|  | 724 | self.path = kw.pop('path') | 
|  | 725 | self.interface = kw.pop('interface') | 
|  | 726 | self.method = kw.pop('method') | 
|  | 727 | self.args = [TrivialArgument(**x) for x in kw.pop('args', {})] | 
|  | 728 | super(Method, self).__init__(**kw) | 
|  | 729 |  | 
|  | 730 | def factory(self, objs): | 
|  | 731 | args = { | 
|  | 732 | 'class': 'interface', | 
|  | 733 | 'interface': 'element', | 
|  | 734 | 'name': self.service | 
|  | 735 | } | 
|  | 736 | add_unique(ConfigEntry( | 
|  | 737 | configfile=self.configfile, **args), objs) | 
|  | 738 |  | 
|  | 739 | args = { | 
|  | 740 | 'class': 'pathname', | 
|  | 741 | 'pathname': 'element', | 
|  | 742 | 'name': self.path | 
|  | 743 | } | 
|  | 744 | add_unique(ConfigEntry( | 
|  | 745 | configfile=self.configfile, **args), objs) | 
|  | 746 |  | 
|  | 747 | args = { | 
|  | 748 | 'class': 'interface', | 
|  | 749 | 'interface': 'element', | 
|  | 750 | 'name': self.interface | 
|  | 751 | } | 
|  | 752 | add_unique(ConfigEntry( | 
|  | 753 | configfile=self.configfile, **args), objs) | 
|  | 754 |  | 
|  | 755 | args = { | 
|  | 756 | 'class': 'propertyname', | 
|  | 757 | 'propertyname': 'element', | 
|  | 758 | 'name': self.method | 
|  | 759 | } | 
|  | 760 | add_unique(ConfigEntry( | 
|  | 761 | configfile=self.configfile, **args), objs) | 
|  | 762 |  | 
|  | 763 | super(Method, self).factory(objs) | 
|  | 764 |  | 
|  | 765 | def setup(self, objs): | 
|  | 766 | '''Resolve elements.''' | 
|  | 767 |  | 
|  | 768 | self.service = get_index( | 
|  | 769 | objs, | 
|  | 770 | 'interface', | 
|  | 771 | self.service) | 
|  | 772 |  | 
|  | 773 | self.path = get_index( | 
|  | 774 | objs, | 
|  | 775 | 'pathname', | 
|  | 776 | self.path) | 
|  | 777 |  | 
|  | 778 | self.interface = get_index( | 
|  | 779 | objs, | 
|  | 780 | 'interface', | 
|  | 781 | self.interface) | 
|  | 782 |  | 
|  | 783 | self.method = get_index( | 
|  | 784 | objs, | 
|  | 785 | 'propertyname', | 
|  | 786 | self.method) | 
|  | 787 |  | 
|  | 788 | super(Method, self).setup(objs) | 
|  | 789 |  | 
|  | 790 | def construct(self, loader, indent): | 
|  | 791 | return self.render( | 
|  | 792 | loader, | 
|  | 793 | 'method.mako.cpp', | 
|  | 794 | c=self, | 
|  | 795 | indent=indent) | 
|  | 796 |  | 
|  | 797 |  | 
| Brad Bishop | 49e6617 | 2017-05-23 19:16:21 -0400 | [diff] [blame] | 798 | class CallbackGraphEntry(Group): | 
|  | 799 | '''An entry in a traversal list for groups of callbacks.''' | 
|  | 800 |  | 
|  | 801 | def __init__(self, *a, **kw): | 
|  | 802 | super(CallbackGraphEntry, self).__init__(**kw) | 
|  | 803 |  | 
|  | 804 | def setup(self, objs): | 
|  | 805 | '''Resolve group members.''' | 
|  | 806 |  | 
|  | 807 | def map_member(x): | 
|  | 808 | return get_index( | 
|  | 809 | objs, 'callback', x, config=self.configfile) | 
|  | 810 |  | 
|  | 811 | self.members = map( | 
|  | 812 | map_member, | 
|  | 813 | self.members) | 
|  | 814 |  | 
|  | 815 | super(CallbackGraphEntry, self).setup(objs) | 
|  | 816 |  | 
|  | 817 |  | 
|  | 818 | class GroupOfCallbacks(ConfigEntry, Renderer): | 
|  | 819 | '''Handle the callback group config file directive.''' | 
|  | 820 |  | 
|  | 821 | def __init__(self, *a, **kw): | 
|  | 822 | self.members = kw.pop('members') | 
|  | 823 | super(GroupOfCallbacks, self).__init__(**kw) | 
|  | 824 |  | 
|  | 825 | def factory(self, objs): | 
|  | 826 | '''Create a graph instance for this group of callbacks.''' | 
|  | 827 |  | 
|  | 828 | args = { | 
|  | 829 | 'configfile': self.configfile, | 
|  | 830 | 'members': self.members, | 
|  | 831 | 'class': 'callbackgroup', | 
|  | 832 | 'callbackgroup': 'callback', | 
|  | 833 | 'name': self.members | 
|  | 834 | } | 
|  | 835 |  | 
|  | 836 | entry = CallbackGraphEntry(**args) | 
|  | 837 | add_unique(entry, objs, config=self.configfile) | 
|  | 838 |  | 
|  | 839 | super(GroupOfCallbacks, self).factory(objs) | 
|  | 840 |  | 
|  | 841 | def setup(self, objs): | 
|  | 842 | '''Resolve graph entry.''' | 
|  | 843 |  | 
|  | 844 | self.graph = get_index( | 
|  | 845 | objs, 'callbackgroup', self.members, config=self.configfile) | 
|  | 846 |  | 
|  | 847 | super(GroupOfCallbacks, self).setup(objs) | 
|  | 848 |  | 
|  | 849 | def construct(self, loader, indent): | 
|  | 850 | return self.render( | 
|  | 851 | loader, | 
|  | 852 | 'callbackgroup.mako.cpp', | 
|  | 853 | c=self, | 
|  | 854 | indent=indent) | 
|  | 855 |  | 
|  | 856 |  | 
| Brad Bishop | 34a7acd | 2017-04-27 23:47:23 -0400 | [diff] [blame] | 857 | class Everything(Renderer): | 
|  | 858 | '''Parse/render entry point.''' | 
|  | 859 |  | 
|  | 860 | @staticmethod | 
| Brad Bishop | 05b0c1e | 2017-05-23 00:24:01 -0400 | [diff] [blame] | 861 | def classmap(cls, sub=None): | 
|  | 862 | '''Map render item class and subclass entries to the appropriate | 
|  | 863 | handler methods.''' | 
|  | 864 |  | 
|  | 865 | class_map = { | 
| Brad Bishop | 0e7df13 | 2017-05-23 17:58:12 -0400 | [diff] [blame] | 866 | 'path': { | 
|  | 867 | 'element': Path, | 
|  | 868 | }, | 
|  | 869 | 'pathgroup': { | 
|  | 870 | 'path': GroupOfPaths, | 
|  | 871 | }, | 
| Brad Bishop | e73b2c3 | 2017-05-23 18:01:54 -0400 | [diff] [blame] | 872 | 'propertygroup': { | 
|  | 873 | 'property': GroupOfProperties, | 
|  | 874 | }, | 
|  | 875 | 'property': { | 
|  | 876 | 'element': Property, | 
|  | 877 | }, | 
| Brad Bishop | 4b916f1 | 2017-05-23 18:06:38 -0400 | [diff] [blame] | 878 | 'watch': { | 
|  | 879 | 'property': PropertyWatch, | 
|  | 880 | }, | 
|  | 881 | 'instance': { | 
|  | 882 | 'element': Instance, | 
|  | 883 | }, | 
| Brad Bishop | c1283ae | 2017-05-20 21:42:38 -0400 | [diff] [blame] | 884 | 'callback': { | 
|  | 885 | 'journal': Journal, | 
| Gunnar Mills | d5faea3 | 2017-08-08 14:19:36 -0500 | [diff] [blame] | 886 | 'elog': Elog, | 
| Brad Bishop | 49e6617 | 2017-05-23 19:16:21 -0400 | [diff] [blame] | 887 | 'group': GroupOfCallbacks, | 
| Brad Bishop | 0df00be | 2017-05-25 23:38:37 -0400 | [diff] [blame] | 888 | 'method': Method, | 
| Matt Spinler | 1d6ca48 | 2017-11-01 10:48:02 -0500 | [diff] [blame] | 889 | 'resolve callout': ResolveCallout, | 
| Brad Bishop | c1283ae | 2017-05-20 21:42:38 -0400 | [diff] [blame] | 890 | }, | 
| Brad Bishop | 4041d72 | 2017-05-21 10:06:07 -0400 | [diff] [blame] | 891 | 'condition': { | 
|  | 892 | 'count': CountCondition, | 
|  | 893 | }, | 
| Brad Bishop | 05b0c1e | 2017-05-23 00:24:01 -0400 | [diff] [blame] | 894 | } | 
|  | 895 |  | 
|  | 896 | if cls not in class_map: | 
|  | 897 | raise NotImplementedError('Unknown class: "{0}"'.format(cls)) | 
|  | 898 | if sub not in class_map[cls]: | 
|  | 899 | raise NotImplementedError('Unknown {0} type: "{1}"'.format( | 
|  | 900 | cls, sub)) | 
|  | 901 |  | 
|  | 902 | return class_map[cls][sub] | 
|  | 903 |  | 
|  | 904 | @staticmethod | 
|  | 905 | def load_one_yaml(path, fd, objs): | 
|  | 906 | '''Parse a single YAML file.  Parsing occurs in three phases. | 
|  | 907 | In the first phase a factory method associated with each | 
|  | 908 | configuration file directive is invoked.  These factory | 
|  | 909 | methods generate more factory methods.  In the second | 
|  | 910 | phase the factory methods created in the first phase | 
|  | 911 | are invoked.  In the last phase a callback is invoked on | 
|  | 912 | each object created in phase two.  Typically the callback | 
|  | 913 | resolves references to other configuration file directives.''' | 
|  | 914 |  | 
|  | 915 | factory_objs = {} | 
|  | 916 | for x in yaml.safe_load(fd.read()) or {}: | 
|  | 917 |  | 
|  | 918 | # Create factory object for this config file directive. | 
|  | 919 | cls = x['class'] | 
|  | 920 | sub = x.get(cls) | 
|  | 921 | if cls == 'group': | 
|  | 922 | cls = '{0}group'.format(sub) | 
|  | 923 |  | 
|  | 924 | factory = Everything.classmap(cls, sub) | 
|  | 925 | obj = factory(configfile=path, **x) | 
|  | 926 |  | 
|  | 927 | # For a given class of directive, validate the file | 
|  | 928 | # doesn't have any duplicate names (duplicates are | 
|  | 929 | # ok across config files). | 
|  | 930 | if exists(factory_objs, obj.cls, obj.name, config=path): | 
|  | 931 | raise NotUniqueError(path, cls, obj.name) | 
|  | 932 |  | 
|  | 933 | factory_objs.setdefault(cls, []).append(obj) | 
|  | 934 | objs.setdefault(cls, []).append(obj) | 
|  | 935 |  | 
|  | 936 | for cls, items in factory_objs.items(): | 
|  | 937 | for obj in items: | 
|  | 938 | # Add objects for template consumption. | 
|  | 939 | obj.factory(objs) | 
|  | 940 |  | 
|  | 941 | @staticmethod | 
| Brad Bishop | 34a7acd | 2017-04-27 23:47:23 -0400 | [diff] [blame] | 942 | def load(args): | 
|  | 943 | '''Aggregate all the YAML in the input directory | 
|  | 944 | into a single aggregate.''' | 
|  | 945 |  | 
| Brad Bishop | 05b0c1e | 2017-05-23 00:24:01 -0400 | [diff] [blame] | 946 | objs = {} | 
|  | 947 | yaml_files = filter( | 
|  | 948 | lambda x: x.endswith('.yaml'), | 
|  | 949 | os.listdir(args.inputdir)) | 
| Brad Bishop | 34a7acd | 2017-04-27 23:47:23 -0400 | [diff] [blame] | 950 |  | 
| Brad Bishop | 05b0c1e | 2017-05-23 00:24:01 -0400 | [diff] [blame] | 951 | yaml_files.sort() | 
| Brad Bishop | 34a7acd | 2017-04-27 23:47:23 -0400 | [diff] [blame] | 952 |  | 
| Brad Bishop | 05b0c1e | 2017-05-23 00:24:01 -0400 | [diff] [blame] | 953 | for x in yaml_files: | 
|  | 954 | path = os.path.join(args.inputdir, x) | 
|  | 955 | with open(path, 'r') as fd: | 
|  | 956 | Everything.load_one_yaml(path, fd, objs) | 
|  | 957 |  | 
|  | 958 | # Configuration file directives reference each other via | 
|  | 959 | # the name attribute; however, when rendered the reference | 
|  | 960 | # is just an array index. | 
|  | 961 | # | 
|  | 962 | # At this point all objects have been created but references | 
| Gunnar Mills | 78199b4 | 2017-10-25 16:30:18 -0500 | [diff] [blame] | 963 | # have not been resolved to array indices.  Instruct objects | 
| Brad Bishop | 05b0c1e | 2017-05-23 00:24:01 -0400 | [diff] [blame] | 964 | # to do that now. | 
|  | 965 | for cls, items in objs.items(): | 
|  | 966 | for obj in items: | 
|  | 967 | obj.setup(objs) | 
|  | 968 |  | 
|  | 969 | return Everything(**objs) | 
| Brad Bishop | 34a7acd | 2017-04-27 23:47:23 -0400 | [diff] [blame] | 970 |  | 
|  | 971 | def __init__(self, *a, **kw): | 
| Brad Bishop | 0e7df13 | 2017-05-23 17:58:12 -0400 | [diff] [blame] | 972 | self.pathmeta = kw.pop('path', []) | 
|  | 973 | self.paths = kw.pop('pathname', []) | 
|  | 974 | self.meta = kw.pop('meta', []) | 
|  | 975 | self.pathgroups = kw.pop('pathgroup', []) | 
| Brad Bishop | e73b2c3 | 2017-05-23 18:01:54 -0400 | [diff] [blame] | 976 | self.interfaces = kw.pop('interface', []) | 
|  | 977 | self.properties = kw.pop('property', []) | 
|  | 978 | self.propertynames = kw.pop('propertyname', []) | 
|  | 979 | self.propertygroups = kw.pop('propertygroup', []) | 
| Brad Bishop | 4b916f1 | 2017-05-23 18:06:38 -0400 | [diff] [blame] | 980 | self.instances = kw.pop('instance', []) | 
|  | 981 | self.instancegroups = kw.pop('instancegroup', []) | 
|  | 982 | self.watches = kw.pop('watch', []) | 
| Brad Bishop | c1283ae | 2017-05-20 21:42:38 -0400 | [diff] [blame] | 983 | self.callbacks = kw.pop('callback', []) | 
| Brad Bishop | 49e6617 | 2017-05-23 19:16:21 -0400 | [diff] [blame] | 984 | self.callbackgroups = kw.pop('callbackgroup', []) | 
| Brad Bishop | 4041d72 | 2017-05-21 10:06:07 -0400 | [diff] [blame] | 985 | self.conditions = kw.pop('condition', []) | 
| Brad Bishop | 0e7df13 | 2017-05-23 17:58:12 -0400 | [diff] [blame] | 986 |  | 
| Brad Bishop | 34a7acd | 2017-04-27 23:47:23 -0400 | [diff] [blame] | 987 | super(Everything, self).__init__(**kw) | 
|  | 988 |  | 
|  | 989 | def generate_cpp(self, loader): | 
|  | 990 | '''Render the template with the provided data.''' | 
| Gunnar Mills | d5faea3 | 2017-08-08 14:19:36 -0500 | [diff] [blame] | 991 | # errors.hpp is used by generated.hpp to included any error.hpp files | 
|  | 992 | open('errors.hpp', 'w+') | 
|  | 993 |  | 
| Brad Bishop | e3a01af | 2017-05-15 17:09:04 -0400 | [diff] [blame] | 994 | with open(args.output, 'w') as fd: | 
| Brad Bishop | 34a7acd | 2017-04-27 23:47:23 -0400 | [diff] [blame] | 995 | fd.write( | 
|  | 996 | self.render( | 
|  | 997 | loader, | 
| Brad Bishop | e3a01af | 2017-05-15 17:09:04 -0400 | [diff] [blame] | 998 | args.template, | 
| Brad Bishop | 0e7df13 | 2017-05-23 17:58:12 -0400 | [diff] [blame] | 999 | meta=self.meta, | 
| Brad Bishop | e73b2c3 | 2017-05-23 18:01:54 -0400 | [diff] [blame] | 1000 | properties=self.properties, | 
|  | 1001 | propertynames=self.propertynames, | 
|  | 1002 | interfaces=self.interfaces, | 
| Brad Bishop | 0e7df13 | 2017-05-23 17:58:12 -0400 | [diff] [blame] | 1003 | paths=self.paths, | 
|  | 1004 | pathmeta=self.pathmeta, | 
|  | 1005 | pathgroups=self.pathgroups, | 
| Brad Bishop | e73b2c3 | 2017-05-23 18:01:54 -0400 | [diff] [blame] | 1006 | propertygroups=self.propertygroups, | 
| Brad Bishop | 4b916f1 | 2017-05-23 18:06:38 -0400 | [diff] [blame] | 1007 | instances=self.instances, | 
|  | 1008 | watches=self.watches, | 
|  | 1009 | instancegroups=self.instancegroups, | 
| Brad Bishop | c1283ae | 2017-05-20 21:42:38 -0400 | [diff] [blame] | 1010 | callbacks=self.callbacks, | 
| Brad Bishop | 49e6617 | 2017-05-23 19:16:21 -0400 | [diff] [blame] | 1011 | callbackgroups=self.callbackgroups, | 
| Brad Bishop | 4041d72 | 2017-05-21 10:06:07 -0400 | [diff] [blame] | 1012 | conditions=self.conditions, | 
| Brad Bishop | 34a7acd | 2017-04-27 23:47:23 -0400 | [diff] [blame] | 1013 | indent=Indent())) | 
| Matthew Barth | db440d4 | 2017-04-17 15:49:37 -0500 | [diff] [blame] | 1014 |  | 
|  | 1015 | if __name__ == '__main__': | 
| Brad Bishop | 34a7acd | 2017-04-27 23:47:23 -0400 | [diff] [blame] | 1016 | script_dir = os.path.dirname(os.path.realpath(__file__)) | 
|  | 1017 | valid_commands = { | 
|  | 1018 | 'generate-cpp': 'generate_cpp', | 
|  | 1019 | } | 
|  | 1020 |  | 
|  | 1021 | parser = ArgumentParser( | 
|  | 1022 | description='Phosphor DBus Monitor (PDM) YAML ' | 
|  | 1023 | 'scanner and code generator.') | 
|  | 1024 |  | 
| Matthew Barth | db440d4 | 2017-04-17 15:49:37 -0500 | [diff] [blame] | 1025 | parser.add_argument( | 
| Brad Bishop | e3a01af | 2017-05-15 17:09:04 -0400 | [diff] [blame] | 1026 | "-o", "--out", dest="output", | 
|  | 1027 | default='generated.cpp', | 
|  | 1028 | help="Generated output file name and path.") | 
|  | 1029 | parser.add_argument( | 
|  | 1030 | '-t', '--template', dest='template', | 
| Brad Bishop | 870c3fc | 2017-05-22 23:23:13 -0400 | [diff] [blame] | 1031 | default='generated.mako.hpp', | 
| Brad Bishop | e3a01af | 2017-05-15 17:09:04 -0400 | [diff] [blame] | 1032 | help='The top level template to render.') | 
|  | 1033 | parser.add_argument( | 
|  | 1034 | '-p', '--template-path', dest='template_search', | 
|  | 1035 | default=script_dir, | 
|  | 1036 | help='The space delimited mako template search path.') | 
| Brad Bishop | 34a7acd | 2017-04-27 23:47:23 -0400 | [diff] [blame] | 1037 | parser.add_argument( | 
|  | 1038 | '-d', '--dir', dest='inputdir', | 
|  | 1039 | default=os.path.join(script_dir, 'example'), | 
|  | 1040 | help='Location of files to process.') | 
|  | 1041 | parser.add_argument( | 
|  | 1042 | 'command', metavar='COMMAND', type=str, | 
|  | 1043 | choices=valid_commands.keys(), | 
|  | 1044 | help='%s.' % " | ".join(valid_commands.keys())) | 
| Matthew Barth | db440d4 | 2017-04-17 15:49:37 -0500 | [diff] [blame] | 1045 |  | 
| Brad Bishop | 34a7acd | 2017-04-27 23:47:23 -0400 | [diff] [blame] | 1046 | args = parser.parse_args() | 
|  | 1047 |  | 
|  | 1048 | if sys.version_info < (3, 0): | 
|  | 1049 | lookup = mako.lookup.TemplateLookup( | 
| Brad Bishop | e3a01af | 2017-05-15 17:09:04 -0400 | [diff] [blame] | 1050 | directories=args.template_search.split(), | 
| Brad Bishop | 34a7acd | 2017-04-27 23:47:23 -0400 | [diff] [blame] | 1051 | disable_unicode=True) | 
|  | 1052 | else: | 
|  | 1053 | lookup = mako.lookup.TemplateLookup( | 
| Brad Bishop | e3a01af | 2017-05-15 17:09:04 -0400 | [diff] [blame] | 1054 | directories=args.template_search.split()) | 
| Brad Bishop | 05b0c1e | 2017-05-23 00:24:01 -0400 | [diff] [blame] | 1055 | try: | 
|  | 1056 | function = getattr( | 
|  | 1057 | Everything.load(args), | 
|  | 1058 | valid_commands[args.command]) | 
|  | 1059 | function(lookup) | 
|  | 1060 | except InvalidConfigError as e: | 
|  | 1061 | sys.stdout.write('{0}: {1}\n\n'.format(e.config, e.msg)) | 
|  | 1062 | raise |