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

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

Powered by Google App Engine
This is Rietveld 408576698