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

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 #2 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 Feature(object):
41 """A Feature, as specified in files such as
42 chrome/common/extensions/api/_permission_features.json.
43
44 Properties:
45 - |name| the name of the feature
46 - |unix_name| the unix_name of the feature
47 - |source_file| the file that contained the namespace definition
48 - |channel| the channel where the feature is released
49 - |extension_types| the types which can use the permission feature
50 - |whitelist| a list of extensions allowed to use the feature
51 """
52 def __init__(self, feature_name, feature_def, source_file):
53 self.name = feature_name
54 self.unix_name = UnixName(self.name)
55 self.source_file = source_file
56 self.channel = feature_def['channel']
57 self.extension_types = feature_def['extension_types']
58 self.whitelist = feature_def.get('whitelist')
59
60
37 class Namespace(object): 61 class Namespace(object):
38 """An API namespace. 62 """An API namespace.
39 63
40 Properties: 64 Properties:
41 - |name| the name of the namespace 65 - |name| the name of the namespace
42 - |description| the description of the namespace 66 - |description| the description of the namespace
43 - |unix_name| the unix_name of the namespace 67 - |unix_name| the unix_name of the namespace
44 - |source_file| the file that contained the namespace definition 68 - |source_file| the file that contained the namespace definition
45 - |source_file_dir| the directory component of |source_file| 69 - |source_file_dir| the directory component of |source_file|
46 - |source_file_filename| the filename component of |source_file| 70 - |source_file_filename| the filename component of |source_file|
(...skipping 20 matching lines...) Expand all
67 self.parent = None 91 self.parent = None
68 self.platforms = _GetPlatforms(json) 92 self.platforms = _GetPlatforms(json)
69 toplevel_origin = Origin(from_client=True, from_json=True) 93 toplevel_origin = Origin(from_client=True, from_json=True)
70 self.types = _GetTypes(self, json, self, toplevel_origin) 94 self.types = _GetTypes(self, json, self, toplevel_origin)
71 self.functions = _GetFunctions(self, json, self) 95 self.functions = _GetFunctions(self, json, self)
72 self.events = _GetEvents(self, json, self) 96 self.events = _GetEvents(self, json, self)
73 self.properties = _GetProperties(self, json, self, toplevel_origin) 97 self.properties = _GetProperties(self, json, self, toplevel_origin)
74 self.compiler_options = (json.get('compiler_options', {}) 98 self.compiler_options = (json.get('compiler_options', {})
75 if include_compiler_options else {}) 99 if include_compiler_options else {})
76 100
101
77 class Origin(object): 102 class Origin(object):
78 """Stores the possible origin of model object as a pair of bools. These are: 103 """Stores the possible origin of model object as a pair of bools. These are:
79 104
80 |from_client| indicating that instances can originate from users of 105 |from_client| indicating that instances can originate from users of
81 generated code (for example, function results), or 106 generated code (for example, function results), or
82 |from_json| indicating that instances can originate from the JSON (for 107 |from_json| indicating that instances can originate from the JSON (for
83 example, function parameters) 108 example, function parameters)
84 109
85 It is possible for model objects to originate from both the client and json, 110 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 111 for example Types defined in the top-level schema, in which case both
87 |from_client| and |from_json| would be True. 112 |from_client| and |from_json| would be True.
88 """ 113 """
89 def __init__(self, from_client=False, from_json=False): 114 def __init__(self, from_client=False, from_json=False):
90 if not from_client and not from_json: 115 if not from_client and not from_json:
91 raise ValueError('One of from_client or from_json must be true') 116 raise ValueError('One of from_client or from_json must be true')
92 self.from_client = from_client 117 self.from_client = from_client
93 self.from_json = from_json 118 self.from_json = from_json
94 119
120
95 class Type(object): 121 class Type(object):
96 """A Type defined in the json. 122 """A Type defined in the json.
97 123
98 Properties: 124 Properties:
99 - |name| the type name 125 - |name| the type name
100 - |namespace| the Type's namespace 126 - |namespace| the Type's namespace
101 - |description| the description of the type (if provided) 127 - |description| the description of the type (if provided)
102 - |properties| a map of property unix_names to their model.Property 128 - |properties| a map of property unix_names to their model.Property
103 - |functions| a map of function names to their model.Function 129 - |functions| a map of function names to their model.Function
104 - |events| a map of event names to their model.Event 130 - |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 217 self.additional_properties = None
192 elif json_type == 'function': 218 elif json_type == 'function':
193 self.property_type = PropertyType.FUNCTION 219 self.property_type = PropertyType.FUNCTION
194 # Sometimes we might have an unnamed function, e.g. if it's a property 220 # 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. 221 # of an object. Use the name of the property in that case.
196 function_name = json.get('name', name) 222 function_name = json.get('name', name)
197 self.function = Function(self, function_name, json, namespace, origin) 223 self.function = Function(self, function_name, json, namespace, origin)
198 else: 224 else:
199 raise ParseException(self, 'Unsupported JSON type %s' % json_type) 225 raise ParseException(self, 'Unsupported JSON type %s' % json_type)
200 226
227
201 class Function(object): 228 class Function(object):
202 """A Function defined in the API. 229 """A Function defined in the API.
203 230
204 Properties: 231 Properties:
205 - |name| the function name 232 - |name| the function name
206 - |platforms| if not None, the list of platforms that the function is 233 - |platforms| if not None, the list of platforms that the function is
207 available to 234 available to
208 - |params| a list of parameters to the function (order matters). A separate 235 - |params| a list of parameters to the function (order matters). A separate
209 parameter is used for each choice of a 'choices' parameter 236 parameter is used for each choice of a 'choices' parameter
210 - |description| a description of the function (if provided) 237 - |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)) 288 Origin(from_client=True))
262 289
263 self.returns = None 290 self.returns = None
264 if 'returns' in json: 291 if 'returns' in json:
265 self.returns = Type(self, 292 self.returns = Type(self,
266 '%sReturnType' % name, 293 '%sReturnType' % name,
267 json['returns'], 294 json['returns'],
268 namespace, 295 namespace,
269 origin) 296 origin)
270 297
298
271 class Property(object): 299 class Property(object):
272 """A property of a type OR a parameter to a function. 300 """A property of a type OR a parameter to a function.
273 Properties: 301 Properties:
274 - |name| name of the property as in the json. This shouldn't change since 302 - |name| name of the property as in the json. This shouldn't change since
275 it is the key used to access DictionaryValues 303 it is the key used to access DictionaryValues
276 - |unix_name| the unix_style_name of the property. Used as variable name 304 - |unix_name| the unix_style_name of the property. Used as variable name
277 - |optional| a boolean representing whether the property is optional 305 - |optional| a boolean representing whether the property is optional
278 - |description| a description of the property (if provided) 306 - |description| a description of the property (if provided)
279 - |type_| the model.Type of this property 307 - |type_| the model.Type of this property
280 - |simple_name| the name of this Property without a namespace 308 - |simple_name| the name of this Property without a namespace
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
332 return 360 return
333 if self._unix_name_used: 361 if self._unix_name_used:
334 raise AttributeError( 362 raise AttributeError(
335 'Cannot set the unix_name on %s; ' 363 'Cannot set the unix_name on %s; '
336 'it is already used elsewhere as %s' % 364 'it is already used elsewhere as %s' %
337 (self.name, self._unix_name)) 365 (self.name, self._unix_name))
338 self._unix_name = unix_name 366 self._unix_name = unix_name
339 367
340 unix_name = property(GetUnixName, SetUnixName) 368 unix_name = property(GetUnixName, SetUnixName)
341 369
370
342 class _Enum(object): 371 class _Enum(object):
343 """Superclass for enum types with a "name" field, setting up repr/eq/ne. 372 """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. 373 Enums need to do this so that equality/non-equality work over pickling.
345 """ 374 """
346 @staticmethod 375 @staticmethod
347 def GetAll(cls): 376 def GetAll(cls):
348 """Yields all _Enum objects declared in |cls|. 377 """Yields all _Enum objects declared in |cls|.
349 """ 378 """
350 for prop_key in dir(cls): 379 for prop_key in dir(cls):
351 prop_value = getattr(cls, prop_key) 380 prop_value = getattr(cls, prop_key)
352 if isinstance(prop_value, _Enum): 381 if isinstance(prop_value, _Enum):
353 yield prop_value 382 yield prop_value
354 383
355 def __init__(self, name): 384 def __init__(self, name):
356 self.name = name 385 self.name = name
357 386
358 def __eq__(self, other): 387 def __eq__(self, other):
359 return type(other) == type(self) and other.name == self.name 388 return type(other) == type(self) and other.name == self.name
360 def __ne__(self, other): 389 def __ne__(self, other):
361 return not (self == other) 390 return not (self == other)
362 391
363 def __repr__(self): 392 def __repr__(self):
364 return self.name 393 return self.name
365 394
366 def __str__(self): 395 def __str__(self):
367 return repr(self) 396 return repr(self)
368 397
398
369 class _PropertyTypeInfo(_Enum): 399 class _PropertyTypeInfo(_Enum):
370 def __init__(self, is_fundamental, name): 400 def __init__(self, is_fundamental, name):
371 _Enum.__init__(self, name) 401 _Enum.__init__(self, name)
372 self.is_fundamental = is_fundamental 402 self.is_fundamental = is_fundamental
373 403
404
374 class PropertyType(object): 405 class PropertyType(object):
375 """Enum of different types of properties/parameters. 406 """Enum of different types of properties/parameters.
376 """ 407 """
377 ANY = _PropertyTypeInfo(False, "any") 408 ANY = _PropertyTypeInfo(False, "any")
378 ARRAY = _PropertyTypeInfo(False, "array") 409 ARRAY = _PropertyTypeInfo(False, "array")
379 BINARY = _PropertyTypeInfo(False, "binary") 410 BINARY = _PropertyTypeInfo(False, "binary")
380 BOOLEAN = _PropertyTypeInfo(True, "boolean") 411 BOOLEAN = _PropertyTypeInfo(True, "boolean")
381 CHOICES = _PropertyTypeInfo(False, "choices") 412 CHOICES = _PropertyTypeInfo(False, "choices")
382 DOUBLE = _PropertyTypeInfo(True, "double") 413 DOUBLE = _PropertyTypeInfo(True, "double")
383 ENUM = _PropertyTypeInfo(False, "enum") 414 ENUM = _PropertyTypeInfo(False, "enum")
384 FUNCTION = _PropertyTypeInfo(False, "function") 415 FUNCTION = _PropertyTypeInfo(False, "function")
385 INT64 = _PropertyTypeInfo(True, "int64") 416 INT64 = _PropertyTypeInfo(True, "int64")
386 INTEGER = _PropertyTypeInfo(True, "integer") 417 INTEGER = _PropertyTypeInfo(True, "integer")
387 OBJECT = _PropertyTypeInfo(False, "object") 418 OBJECT = _PropertyTypeInfo(False, "object")
388 REF = _PropertyTypeInfo(False, "ref") 419 REF = _PropertyTypeInfo(False, "ref")
389 STRING = _PropertyTypeInfo(True, "string") 420 STRING = _PropertyTypeInfo(True, "string")
390 421
422
391 @memoize 423 @memoize
392 def UnixName(name): 424 def UnixName(name):
393 '''Returns the unix_style name for a given lowerCamelCase string. 425 '''Returns the unix_style name for a given lowerCamelCase string.
394 ''' 426 '''
395 unix_name = [] 427 unix_name = []
396 for i, c in enumerate(name): 428 for i, c in enumerate(name):
397 if c.isupper() and i > 0 and name[i - 1] != '_': 429 if c.isupper() and i > 0 and name[i - 1] != '_':
398 # Replace lowerUpper with lower_Upper. 430 # Replace lowerUpper with lower_Upper.
399 if name[i - 1].islower(): 431 if name[i - 1].islower():
400 unix_name.append('_') 432 unix_name.append('_')
401 # Replace ACMEWidgets with ACME_Widgets 433 # Replace ACMEWidgets with ACME_Widgets
402 elif i + 1 < len(name) and name[i + 1].islower(): 434 elif i + 1 < len(name) and name[i + 1].islower():
403 unix_name.append('_') 435 unix_name.append('_')
404 if c == '.': 436 if c == '.':
405 # Replace hello.world with hello_world. 437 # Replace hello.world with hello_world.
406 unix_name.append('_') 438 unix_name.append('_')
407 else: 439 else:
408 # Everything is lowercase. 440 # Everything is lowercase.
409 unix_name.append(c.lower()) 441 unix_name.append(c.lower())
410 return ''.join(unix_name) 442 return ''.join(unix_name)
411 443
444
412 def _StripNamespace(name, namespace): 445 def _StripNamespace(name, namespace):
413 if name.startswith(namespace.name + '.'): 446 if name.startswith(namespace.name + '.'):
414 return name[len(namespace.name + '.'):] 447 return name[len(namespace.name + '.'):]
415 return name 448 return name
416 449
450
417 def _GetModelHierarchy(entity): 451 def _GetModelHierarchy(entity):
418 """Returns the hierarchy of the given model entity.""" 452 """Returns the hierarchy of the given model entity."""
419 hierarchy = [] 453 hierarchy = []
420 while entity is not None: 454 while entity is not None:
421 hierarchy.append(getattr(entity, 'name', repr(entity))) 455 hierarchy.append(getattr(entity, 'name', repr(entity)))
422 if isinstance(entity, Namespace): 456 if isinstance(entity, Namespace):
423 hierarchy.insert(0, ' in %s' % entity.source_file) 457 hierarchy.insert(0, ' in %s' % entity.source_file)
424 entity = getattr(entity, 'parent', None) 458 entity = getattr(entity, 'parent', None)
425 hierarchy.reverse() 459 hierarchy.reverse()
426 return hierarchy 460 return hierarchy
427 461
462
428 def _GetTypes(parent, json, namespace, origin): 463 def _GetTypes(parent, json, namespace, origin):
429 """Creates Type objects extracted from |json|. 464 """Creates Type objects extracted from |json|.
430 """ 465 """
431 types = OrderedDict() 466 types = OrderedDict()
432 for type_json in json.get('types', []): 467 for type_json in json.get('types', []):
433 type_ = Type(parent, type_json['id'], type_json, namespace, origin) 468 type_ = Type(parent, type_json['id'], type_json, namespace, origin)
434 types[type_.name] = type_ 469 types[type_.name] = type_
435 return types 470 return types
436 471
472
437 def _GetFunctions(parent, json, namespace): 473 def _GetFunctions(parent, json, namespace):
438 """Creates Function objects extracted from |json|. 474 """Creates Function objects extracted from |json|.
439 """ 475 """
440 functions = OrderedDict() 476 functions = OrderedDict()
441 for function_json in json.get('functions', []): 477 for function_json in json.get('functions', []):
442 function = Function(parent, 478 function = Function(parent,
443 function_json['name'], 479 function_json['name'],
444 function_json, 480 function_json,
445 namespace, 481 namespace,
446 Origin(from_json=True)) 482 Origin(from_json=True))
447 functions[function.name] = function 483 functions[function.name] = function
448 return functions 484 return functions
449 485
486
450 def _GetEvents(parent, json, namespace): 487 def _GetEvents(parent, json, namespace):
451 """Creates Function objects generated from the events in |json|. 488 """Creates Function objects generated from the events in |json|.
452 """ 489 """
453 events = OrderedDict() 490 events = OrderedDict()
454 for event_json in json.get('events', []): 491 for event_json in json.get('events', []):
455 event = Function(parent, 492 event = Function(parent,
456 event_json['name'], 493 event_json['name'],
457 event_json, 494 event_json,
458 namespace, 495 namespace,
459 Origin(from_client=True)) 496 Origin(from_client=True))
460 events[event.name] = event 497 events[event.name] = event
461 return events 498 return events
462 499
500
463 def _GetProperties(parent, json, namespace, origin): 501 def _GetProperties(parent, json, namespace, origin):
464 """Generates Property objects extracted from |json|. 502 """Generates Property objects extracted from |json|.
465 """ 503 """
466 properties = OrderedDict() 504 properties = OrderedDict()
467 for name, property_json in json.get('properties', {}).items(): 505 for name, property_json in json.get('properties', {}).items():
468 properties[name] = Property(parent, name, property_json, namespace, origin) 506 properties[name] = Property(parent, name, property_json, namespace, origin)
469 return properties 507 return properties
470 508
509
471 class _PlatformInfo(_Enum): 510 class _PlatformInfo(_Enum):
472 def __init__(self, name): 511 def __init__(self, name):
473 _Enum.__init__(self, name) 512 _Enum.__init__(self, name)
474 513
514
475 class Platforms(object): 515 class Platforms(object):
476 """Enum of the possible platforms. 516 """Enum of the possible platforms.
477 """ 517 """
478 CHROMEOS = _PlatformInfo("chromeos") 518 CHROMEOS = _PlatformInfo("chromeos")
479 CHROMEOS_TOUCH = _PlatformInfo("chromeos_touch") 519 CHROMEOS_TOUCH = _PlatformInfo("chromeos_touch")
480 LINUX = _PlatformInfo("linux") 520 LINUX = _PlatformInfo("linux")
481 MAC = _PlatformInfo("mac") 521 MAC = _PlatformInfo("mac")
482 WIN = _PlatformInfo("win") 522 WIN = _PlatformInfo("win")
483 523
524
484 def _GetPlatforms(json): 525 def _GetPlatforms(json):
485 if 'platforms' not in json: 526 if 'platforms' not in json:
486 return None 527 return None
487 platforms = [] 528 platforms = []
488 for platform_name in json['platforms']: 529 for platform_name in json['platforms']:
489 for platform_enum in _Enum.GetAll(Platforms): 530 for platform_enum in _Enum.GetAll(Platforms):
490 if platform_name == platform_enum.name: 531 if platform_name == platform_enum.name:
491 platforms.append(platform_enum) 532 platforms.append(platform_enum)
492 break 533 break
493 return platforms 534 return platforms
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698