Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(14)

Side by Side Diff: tools/json_schema_compiler/model.py

Issue 23594008: Initial code generation for features. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Addressing #6. Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import os.path 5 import os.path
6 6
7 from json_parse import OrderedDict 7 from json_parse import OrderedDict
8 from memoize import memoize 8 from memoize import memoize
9 9
10
10 class ParseException(Exception): 11 class ParseException(Exception):
11 """Thrown when data in the model is invalid. 12 """Thrown when data in the model is invalid.
12 """ 13 """
13 def __init__(self, parent, message): 14 def __init__(self, parent, message):
14 hierarchy = _GetModelHierarchy(parent) 15 hierarchy = _GetModelHierarchy(parent)
15 hierarchy.append(message) 16 hierarchy.append(message)
16 Exception.__init__( 17 Exception.__init__(
17 self, 'Model parse exception at:\n' + '\n'.join(hierarchy)) 18 self, 'Model parse exception at:\n' + '\n'.join(hierarchy))
18 19
20
19 class Model(object): 21 class Model(object):
20 """Model of all namespaces that comprise an API. 22 """Model of all namespaces that comprise an API.
21 23
22 Properties: 24 Properties:
23 - |namespaces| a map of a namespace name to its model.Namespace 25 - |namespaces| a map of a namespace name to its model.Namespace
24 """ 26 """
25 def __init__(self): 27 def __init__(self):
26 self.namespaces = {} 28 self.namespaces = {}
27 29
28 def AddNamespace(self, json, source_file, include_compiler_options=False): 30 def AddNamespace(self, json, source_file, include_compiler_options=False):
29 """Add a namespace's json to the model and returns the namespace. 31 """Add a namespace's json to the model and returns the namespace.
30 """ 32 """
31 namespace = Namespace(json, 33 namespace = Namespace(json,
32 source_file, 34 source_file,
33 include_compiler_options=include_compiler_options) 35 include_compiler_options=include_compiler_options)
34 self.namespaces[namespace.name] = namespace 36 self.namespaces[namespace.name] = namespace
35 return namespace 37 return namespace
36 38
39
40 def CreateFeature(name, model):
41 if isinstance(model, dict):
42 return SimpleFeature(name, model)
43 return ComplexFeature(name, [SimpleFeature(name, child) for child in model])
44
45
46 class ComplexFeature(object):
47 """A complex feature which may be made of several simple features.
48
49 Properties:
50 - |name| the name of the feature
51 - |unix_name| the unix_name of the feature
52 - |feature_list| a list of simple features which make up the feature
53 """
54 def __init__(self, feature_name, features):
55 self.name = feature_name
56 self.unix_name = UnixName(self.name)
57 self.feature_list = features
58
59 class SimpleFeature(object):
60 """A simple feature, which can make up a complex feature, as specified in
61 files such as chrome/common/extensions/api/_permission_features.json.
62
63 Properties:
64 - |name| the name of the feature
65 - |unix_name| the unix_name of the feature
66 - |channel| the channel where the feature is released
67 - |extension_types| the types which can use the feature
68 - |whitelist| a list of extensions allowed to use the feature
69 """
70 def __init__(self, feature_name, feature_def):
71 self.name = feature_name
72 self.unix_name = UnixName(self.name)
73 self.channel = feature_def['channel']
74 self.extension_types = feature_def['extension_types']
75 self.whitelist = feature_def.get('whitelist')
76
77
37 class Namespace(object): 78 class Namespace(object):
38 """An API namespace. 79 """An API namespace.
39 80
40 Properties: 81 Properties:
41 - |name| the name of the namespace 82 - |name| the name of the namespace
42 - |description| the description of the namespace 83 - |description| the description of the namespace
43 - |unix_name| the unix_name of the namespace 84 - |unix_name| the unix_name of the namespace
44 - |source_file| the file that contained the namespace definition 85 - |source_file| the file that contained the namespace definition
45 - |source_file_dir| the directory component of |source_file| 86 - |source_file_dir| the directory component of |source_file|
46 - |source_file_filename| the filename component of |source_file| 87 - |source_file_filename| the filename component of |source_file|
(...skipping 20 matching lines...) Expand all
67 self.parent = None 108 self.parent = None
68 self.platforms = _GetPlatforms(json) 109 self.platforms = _GetPlatforms(json)
69 toplevel_origin = Origin(from_client=True, from_json=True) 110 toplevel_origin = Origin(from_client=True, from_json=True)
70 self.types = _GetTypes(self, json, self, toplevel_origin) 111 self.types = _GetTypes(self, json, self, toplevel_origin)
71 self.functions = _GetFunctions(self, json, self) 112 self.functions = _GetFunctions(self, json, self)
72 self.events = _GetEvents(self, json, self) 113 self.events = _GetEvents(self, json, self)
73 self.properties = _GetProperties(self, json, self, toplevel_origin) 114 self.properties = _GetProperties(self, json, self, toplevel_origin)
74 self.compiler_options = (json.get('compiler_options', {}) 115 self.compiler_options = (json.get('compiler_options', {})
75 if include_compiler_options else {}) 116 if include_compiler_options else {})
76 117
118
77 class Origin(object): 119 class Origin(object):
78 """Stores the possible origin of model object as a pair of bools. These are: 120 """Stores the possible origin of model object as a pair of bools. These are:
79 121
80 |from_client| indicating that instances can originate from users of 122 |from_client| indicating that instances can originate from users of
81 generated code (for example, function results), or 123 generated code (for example, function results), or
82 |from_json| indicating that instances can originate from the JSON (for 124 |from_json| indicating that instances can originate from the JSON (for
83 example, function parameters) 125 example, function parameters)
84 126
85 It is possible for model objects to originate from both the client and json, 127 It is possible for model objects to originate from both the client and json,
86 for example Types defined in the top-level schema, in which case both 128 for example Types defined in the top-level schema, in which case both
87 |from_client| and |from_json| would be True. 129 |from_client| and |from_json| would be True.
88 """ 130 """
89 def __init__(self, from_client=False, from_json=False): 131 def __init__(self, from_client=False, from_json=False):
90 if not from_client and not from_json: 132 if not from_client and not from_json:
91 raise ValueError('One of from_client or from_json must be true') 133 raise ValueError('One of from_client or from_json must be true')
92 self.from_client = from_client 134 self.from_client = from_client
93 self.from_json = from_json 135 self.from_json = from_json
94 136
137
95 class Type(object): 138 class Type(object):
96 """A Type defined in the json. 139 """A Type defined in the json.
97 140
98 Properties: 141 Properties:
99 - |name| the type name 142 - |name| the type name
100 - |namespace| the Type's namespace 143 - |namespace| the Type's namespace
101 - |description| the description of the type (if provided) 144 - |description| the description of the type (if provided)
102 - |properties| a map of property unix_names to their model.Property 145 - |properties| a map of property unix_names to their model.Property
103 - |functions| a map of function names to their model.Function 146 - |functions| a map of function names to their model.Function
104 - |events| a map of event names to their model.Event 147 - |events| a map of event names to their model.Event
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
191 self.additional_properties = None 234 self.additional_properties = None
192 elif json_type == 'function': 235 elif json_type == 'function':
193 self.property_type = PropertyType.FUNCTION 236 self.property_type = PropertyType.FUNCTION
194 # Sometimes we might have an unnamed function, e.g. if it's a property 237 # Sometimes we might have an unnamed function, e.g. if it's a property
195 # of an object. Use the name of the property in that case. 238 # of an object. Use the name of the property in that case.
196 function_name = json.get('name', name) 239 function_name = json.get('name', name)
197 self.function = Function(self, function_name, json, namespace, origin) 240 self.function = Function(self, function_name, json, namespace, origin)
198 else: 241 else:
199 raise ParseException(self, 'Unsupported JSON type %s' % json_type) 242 raise ParseException(self, 'Unsupported JSON type %s' % json_type)
200 243
244
201 class Function(object): 245 class Function(object):
202 """A Function defined in the API. 246 """A Function defined in the API.
203 247
204 Properties: 248 Properties:
205 - |name| the function name 249 - |name| the function name
206 - |platforms| if not None, the list of platforms that the function is 250 - |platforms| if not None, the list of platforms that the function is
207 available to 251 available to
208 - |params| a list of parameters to the function (order matters). A separate 252 - |params| a list of parameters to the function (order matters). A separate
209 parameter is used for each choice of a 'choices' parameter 253 parameter is used for each choice of a 'choices' parameter
210 - |description| a description of the function (if provided) 254 - |description| a description of the function (if provided)
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
261 Origin(from_client=True)) 305 Origin(from_client=True))
262 306
263 self.returns = None 307 self.returns = None
264 if 'returns' in json: 308 if 'returns' in json:
265 self.returns = Type(self, 309 self.returns = Type(self,
266 '%sReturnType' % name, 310 '%sReturnType' % name,
267 json['returns'], 311 json['returns'],
268 namespace, 312 namespace,
269 origin) 313 origin)
270 314
315
271 class Property(object): 316 class Property(object):
272 """A property of a type OR a parameter to a function. 317 """A property of a type OR a parameter to a function.
273 Properties: 318 Properties:
274 - |name| name of the property as in the json. This shouldn't change since 319 - |name| name of the property as in the json. This shouldn't change since
275 it is the key used to access DictionaryValues 320 it is the key used to access DictionaryValues
276 - |unix_name| the unix_style_name of the property. Used as variable name 321 - |unix_name| the unix_style_name of the property. Used as variable name
277 - |optional| a boolean representing whether the property is optional 322 - |optional| a boolean representing whether the property is optional
278 - |description| a description of the property (if provided) 323 - |description| a description of the property (if provided)
279 - |type_| the model.Type of this property 324 - |type_| the model.Type of this property
280 - |simple_name| the name of this Property without a namespace 325 - |simple_name| the name of this Property without a namespace
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
332 return 377 return
333 if self._unix_name_used: 378 if self._unix_name_used:
334 raise AttributeError( 379 raise AttributeError(
335 'Cannot set the unix_name on %s; ' 380 'Cannot set the unix_name on %s; '
336 'it is already used elsewhere as %s' % 381 'it is already used elsewhere as %s' %
337 (self.name, self._unix_name)) 382 (self.name, self._unix_name))
338 self._unix_name = unix_name 383 self._unix_name = unix_name
339 384
340 unix_name = property(GetUnixName, SetUnixName) 385 unix_name = property(GetUnixName, SetUnixName)
341 386
387
342 class _Enum(object): 388 class _Enum(object):
343 """Superclass for enum types with a "name" field, setting up repr/eq/ne. 389 """Superclass for enum types with a "name" field, setting up repr/eq/ne.
344 Enums need to do this so that equality/non-equality work over pickling. 390 Enums need to do this so that equality/non-equality work over pickling.
345 """ 391 """
346 @staticmethod 392 @staticmethod
347 def GetAll(cls): 393 def GetAll(cls):
348 """Yields all _Enum objects declared in |cls|. 394 """Yields all _Enum objects declared in |cls|.
349 """ 395 """
350 for prop_key in dir(cls): 396 for prop_key in dir(cls):
351 prop_value = getattr(cls, prop_key) 397 prop_value = getattr(cls, prop_key)
352 if isinstance(prop_value, _Enum): 398 if isinstance(prop_value, _Enum):
353 yield prop_value 399 yield prop_value
354 400
355 def __init__(self, name): 401 def __init__(self, name):
356 self.name = name 402 self.name = name
357 403
358 def __eq__(self, other): 404 def __eq__(self, other):
359 return type(other) == type(self) and other.name == self.name 405 return type(other) == type(self) and other.name == self.name
360 def __ne__(self, other): 406 def __ne__(self, other):
361 return not (self == other) 407 return not (self == other)
362 408
363 def __repr__(self): 409 def __repr__(self):
364 return self.name 410 return self.name
365 411
366 def __str__(self): 412 def __str__(self):
367 return repr(self) 413 return repr(self)
368 414
415
369 class _PropertyTypeInfo(_Enum): 416 class _PropertyTypeInfo(_Enum):
370 def __init__(self, is_fundamental, name): 417 def __init__(self, is_fundamental, name):
371 _Enum.__init__(self, name) 418 _Enum.__init__(self, name)
372 self.is_fundamental = is_fundamental 419 self.is_fundamental = is_fundamental
373 420
421
374 class PropertyType(object): 422 class PropertyType(object):
375 """Enum of different types of properties/parameters. 423 """Enum of different types of properties/parameters.
376 """ 424 """
377 ANY = _PropertyTypeInfo(False, "any") 425 ANY = _PropertyTypeInfo(False, "any")
378 ARRAY = _PropertyTypeInfo(False, "array") 426 ARRAY = _PropertyTypeInfo(False, "array")
379 BINARY = _PropertyTypeInfo(False, "binary") 427 BINARY = _PropertyTypeInfo(False, "binary")
380 BOOLEAN = _PropertyTypeInfo(True, "boolean") 428 BOOLEAN = _PropertyTypeInfo(True, "boolean")
381 CHOICES = _PropertyTypeInfo(False, "choices") 429 CHOICES = _PropertyTypeInfo(False, "choices")
382 DOUBLE = _PropertyTypeInfo(True, "double") 430 DOUBLE = _PropertyTypeInfo(True, "double")
383 ENUM = _PropertyTypeInfo(False, "enum") 431 ENUM = _PropertyTypeInfo(False, "enum")
384 FUNCTION = _PropertyTypeInfo(False, "function") 432 FUNCTION = _PropertyTypeInfo(False, "function")
385 INT64 = _PropertyTypeInfo(True, "int64") 433 INT64 = _PropertyTypeInfo(True, "int64")
386 INTEGER = _PropertyTypeInfo(True, "integer") 434 INTEGER = _PropertyTypeInfo(True, "integer")
387 OBJECT = _PropertyTypeInfo(False, "object") 435 OBJECT = _PropertyTypeInfo(False, "object")
388 REF = _PropertyTypeInfo(False, "ref") 436 REF = _PropertyTypeInfo(False, "ref")
389 STRING = _PropertyTypeInfo(True, "string") 437 STRING = _PropertyTypeInfo(True, "string")
390 438
439
391 @memoize 440 @memoize
392 def UnixName(name): 441 def UnixName(name):
393 '''Returns the unix_style name for a given lowerCamelCase string. 442 '''Returns the unix_style name for a given lowerCamelCase string.
394 ''' 443 '''
395 unix_name = [] 444 unix_name = []
396 for i, c in enumerate(name): 445 for i, c in enumerate(name):
397 if c.isupper() and i > 0 and name[i - 1] != '_': 446 if c.isupper() and i > 0 and name[i - 1] != '_':
398 # Replace lowerUpper with lower_Upper. 447 # Replace lowerUpper with lower_Upper.
399 if name[i - 1].islower(): 448 if name[i - 1].islower():
400 unix_name.append('_') 449 unix_name.append('_')
401 # Replace ACMEWidgets with ACME_Widgets 450 # Replace ACMEWidgets with ACME_Widgets
402 elif i + 1 < len(name) and name[i + 1].islower(): 451 elif i + 1 < len(name) and name[i + 1].islower():
403 unix_name.append('_') 452 unix_name.append('_')
404 if c == '.': 453 if c == '.':
405 # Replace hello.world with hello_world. 454 # Replace hello.world with hello_world.
406 unix_name.append('_') 455 unix_name.append('_')
407 else: 456 else:
408 # Everything is lowercase. 457 # Everything is lowercase.
409 unix_name.append(c.lower()) 458 unix_name.append(c.lower())
410 return ''.join(unix_name) 459 return ''.join(unix_name)
411 460
461
412 def _StripNamespace(name, namespace): 462 def _StripNamespace(name, namespace):
413 if name.startswith(namespace.name + '.'): 463 if name.startswith(namespace.name + '.'):
414 return name[len(namespace.name + '.'):] 464 return name[len(namespace.name + '.'):]
415 return name 465 return name
416 466
467
417 def _GetModelHierarchy(entity): 468 def _GetModelHierarchy(entity):
418 """Returns the hierarchy of the given model entity.""" 469 """Returns the hierarchy of the given model entity."""
419 hierarchy = [] 470 hierarchy = []
420 while entity is not None: 471 while entity is not None:
421 hierarchy.append(getattr(entity, 'name', repr(entity))) 472 hierarchy.append(getattr(entity, 'name', repr(entity)))
422 if isinstance(entity, Namespace): 473 if isinstance(entity, Namespace):
423 hierarchy.insert(0, ' in %s' % entity.source_file) 474 hierarchy.insert(0, ' in %s' % entity.source_file)
424 entity = getattr(entity, 'parent', None) 475 entity = getattr(entity, 'parent', None)
425 hierarchy.reverse() 476 hierarchy.reverse()
426 return hierarchy 477 return hierarchy
427 478
479
428 def _GetTypes(parent, json, namespace, origin): 480 def _GetTypes(parent, json, namespace, origin):
429 """Creates Type objects extracted from |json|. 481 """Creates Type objects extracted from |json|.
430 """ 482 """
431 types = OrderedDict() 483 types = OrderedDict()
432 for type_json in json.get('types', []): 484 for type_json in json.get('types', []):
433 type_ = Type(parent, type_json['id'], type_json, namespace, origin) 485 type_ = Type(parent, type_json['id'], type_json, namespace, origin)
434 types[type_.name] = type_ 486 types[type_.name] = type_
435 return types 487 return types
436 488
489
437 def _GetFunctions(parent, json, namespace): 490 def _GetFunctions(parent, json, namespace):
438 """Creates Function objects extracted from |json|. 491 """Creates Function objects extracted from |json|.
439 """ 492 """
440 functions = OrderedDict() 493 functions = OrderedDict()
441 for function_json in json.get('functions', []): 494 for function_json in json.get('functions', []):
442 function = Function(parent, 495 function = Function(parent,
443 function_json['name'], 496 function_json['name'],
444 function_json, 497 function_json,
445 namespace, 498 namespace,
446 Origin(from_json=True)) 499 Origin(from_json=True))
447 functions[function.name] = function 500 functions[function.name] = function
448 return functions 501 return functions
449 502
503
450 def _GetEvents(parent, json, namespace): 504 def _GetEvents(parent, json, namespace):
451 """Creates Function objects generated from the events in |json|. 505 """Creates Function objects generated from the events in |json|.
452 """ 506 """
453 events = OrderedDict() 507 events = OrderedDict()
454 for event_json in json.get('events', []): 508 for event_json in json.get('events', []):
455 event = Function(parent, 509 event = Function(parent,
456 event_json['name'], 510 event_json['name'],
457 event_json, 511 event_json,
458 namespace, 512 namespace,
459 Origin(from_client=True)) 513 Origin(from_client=True))
460 events[event.name] = event 514 events[event.name] = event
461 return events 515 return events
462 516
517
463 def _GetProperties(parent, json, namespace, origin): 518 def _GetProperties(parent, json, namespace, origin):
464 """Generates Property objects extracted from |json|. 519 """Generates Property objects extracted from |json|.
465 """ 520 """
466 properties = OrderedDict() 521 properties = OrderedDict()
467 for name, property_json in json.get('properties', {}).items(): 522 for name, property_json in json.get('properties', {}).items():
468 properties[name] = Property(parent, name, property_json, namespace, origin) 523 properties[name] = Property(parent, name, property_json, namespace, origin)
469 return properties 524 return properties
470 525
526
471 class _PlatformInfo(_Enum): 527 class _PlatformInfo(_Enum):
472 def __init__(self, name): 528 def __init__(self, name):
473 _Enum.__init__(self, name) 529 _Enum.__init__(self, name)
474 530
531
475 class Platforms(object): 532 class Platforms(object):
476 """Enum of the possible platforms. 533 """Enum of the possible platforms.
477 """ 534 """
478 CHROMEOS = _PlatformInfo("chromeos") 535 CHROMEOS = _PlatformInfo("chromeos")
479 CHROMEOS_TOUCH = _PlatformInfo("chromeos_touch") 536 CHROMEOS_TOUCH = _PlatformInfo("chromeos_touch")
480 LINUX = _PlatformInfo("linux") 537 LINUX = _PlatformInfo("linux")
481 MAC = _PlatformInfo("mac") 538 MAC = _PlatformInfo("mac")
482 WIN = _PlatformInfo("win") 539 WIN = _PlatformInfo("win")
483 540
541
484 def _GetPlatforms(json): 542 def _GetPlatforms(json):
485 if 'platforms' not in json: 543 if 'platforms' not in json:
486 return None 544 return None
487 platforms = [] 545 platforms = []
488 for platform_name in json['platforms']: 546 for platform_name in json['platforms']:
489 for platform_enum in _Enum.GetAll(Platforms): 547 for platform_enum in _Enum.GetAll(Platforms):
490 if platform_name == platform_enum.name: 548 if platform_name == platform_enum.name:
491 platforms.append(platform_enum) 549 platforms.append(platform_enum)
492 break 550 break
493 return platforms 551 return platforms
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698