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

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

Issue 9309044: Supporting more APIs with json_schema_compiler (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: rework, updated base Created 8 years, 10 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 import re
6 7
7 class Model(object): 8 class Model(object):
8 """Model of all namespaces that comprise an API. 9 """Model of all namespaces that comprise an API.
10
11 Properties:
12 - |namespaces| a map of a namespace name to its model.Namespace
9 """ 13 """
10 def __init__(self): 14 def __init__(self):
11 self.namespaces = {} 15 self.namespaces = {}
12 16
13 def AddNamespace(self, json, source_file): 17 def AddNamespace(self, json, source_file):
14 """Add a namespace's json to the model if it has a "compile" property set 18 """Add a namespace's json to the model if it doesn't have "nocompile"
15 to true. Returns the new namespace or None if a namespace wasn't added. 19 property set to true. Returns the new namespace or None if a namespace
20 wasn't added.
16 """ 21 """
17 if not json.get('compile'): 22 if json.get('nocompile', False):
18 return None 23 return None
19 namespace = Namespace(json, source_file) 24 namespace = Namespace(json, source_file)
20 self.namespaces[namespace.name] = namespace 25 self.namespaces[namespace.name] = namespace
21 return namespace 26 return namespace
22 27
23 class Namespace(object): 28 class Namespace(object):
24 """An API namespace. 29 """An API namespace.
30
31 Properties:
32 - |name| the name of the namespace
33 - |source_file_dir| the directory component of the file that contained the nam espace definition
34 - |source_file_filename| the filename component of the file that contained the namespace definition
35 - |types| a map of type names to their model.Type
36 - |functions| a map of function names to their model.Function
25 """ 37 """
26 def __init__(self, json, source_file): 38 def __init__(self, json, source_file):
27 self.name = json['namespace'] 39 self.name = json['namespace']
28 self.source_file = source_file 40 self.source_file = source_file
29 self.source_file_dir, self.source_file_filename = os.path.split(source_file) 41 self.source_file_dir, self.source_file_filename = os.path.split(source_file)
30 self.type_dependencies = {}
31 self.types = {} 42 self.types = {}
32 self.functions = {} 43 self.functions = {}
33 for type_json in json['types']: 44 for type_json in json['types']:
34 type_ = Type(type_json) 45 type_ = Type(type_json)
35 self.types[type_.name] = type_ 46 self.types[type_.name] = type_
36 for function_json in json['functions']: 47 for function_json in json['functions']:
37 if not function_json.get('nocompile'): 48 if not function_json.get('nocompile', False):
38 function = Function(function_json) 49 function = Function(function_json)
39 self.functions[function.name] = function 50 self.functions[function.name] = function
40 51
41 class Type(object): 52 class Type(object):
42 """A Type defined in the json. 53 """A Type defined in the json.
54
55 Properties:
56 - |name| the type name
57 - |description| the description of the type (if provided)
58 - |properties| a map of property names to their model.Property
43 """ 59 """
44 def __init__(self, json): 60 def __init__(self, json):
45 self.name = json['id'] 61 self.name = json['id']
46 self.description = json.get('description') 62 self.description = json.get('description')
47 self.properties = {} 63 self.properties = {}
48 for prop_name, prop_json in json['properties'].items(): 64 for prop_name, prop_json in json['properties'].items():
49 self.properties[prop_name] = Property(prop_name, prop_json) 65 self.properties[prop_name] = Property(prop_name, prop_json)
50 66
51 class Callback(object): 67 class Callback(object):
52 """A callback parameter to a Function. 68 """A callback parameter to a Function.
69
70 Properties:
71 - |params| the parameters to this callback.
53 """ 72 """
54 def __init__(self, json): 73 def __init__(self, json):
55 params = json['parameters'] 74 params = json['parameters']
75 self.params = []
56 if len(params) == 0: 76 if len(params) == 0:
57 self.param = None 77 return
58 elif len(params) == 1: 78 elif len(params) == 1:
59 param = params[0] 79 param = params[0]
60 self.param = Property(param['name'], param) 80 self.params.append(Property(param['name'], param))
61 else: 81 else:
62 raise AssertionError("Callbacks can have at most a single parameter") 82 raise AssertionError("Callbacks can have at most a single parameter")
63 83
64 class Function(object): 84 class Function(object):
65 """A Function defined in the API. 85 """A Function defined in the API.
86
87 Properties:
88 - |name| the function name
89 - |params| a list of parameters to the function (order matters). A separate
90 parameter is used for each choice of a 'choices' parameter.
91 - |description| a description of the function (if provided)
92 - |callback| the callback parameter to the function. There should be exactly
93 one
66 """ 94 """
67 def __init__(self, json): 95 def __init__(self, json):
68 self.name = json['name'] 96 self.name = json['name']
69 self.params = [] 97 self.params = []
70 self.description = json['description'] 98 self.description = json['description']
71 self.callback = None 99 self.callback = None
72 self.type_dependencies = {}
73 for param in json['parameters']: 100 for param in json['parameters']:
74 if param.get('type') == 'function': 101 if param.get('type') == 'function':
75 assert (not self.callback), "Function has more than one callback" 102 assert (not self.callback), self.name + " has more than one callback"
76 self.callback = Callback(param) 103 self.callback = Callback(param)
77 else: 104 else:
78 self.params.append(Property(param['name'], param)) 105 self.params.append(Property(param['name'], param))
79 assert (self.callback), "Function does not support callback" 106 assert (self.callback), self.name + " does not support callback"
80 107
81 # TODO(calamity): handle Enum/choices 108 # TODO(calamity): handle Enum
82 class Property(object): 109 class Property(object):
83 """A property of a type OR a parameter to a function. 110 """A property of a type OR a parameter to a function.
84 111
85 Members will change based on PropertyType. Check self.type_ to determine which 112 Properties:
86 members actually exist. 113 - |name| name of the property as in the json. This shouldn't change since
114 it is the key used to access DictionaryValues
115 - |unix_name| the unix_style_name of the property. Used as variable name
116 - |optional| a boolean representing whether the property is optional
117 - |description| a description of the property (if provided)
118 - |type_| the model.PropertyType of this property
119 - |ref_type| the type that the REF property is referencing. Can be used to
120 map to its model.Type
121 - |item_type| a model.Property representing the type of each element in an
122 ARRAY
123 - |properties| the properties of an OBJECT parameter
87 """ 124 """
88 def __init__(self, name, json): 125 def __init__(self, name, json):
126 if not re.match('^[a-z][a-zA-Z0-9]*$', name):
127 raise AssertionError('Name %s must be lowerCamelCase' % name)
89 self.name = name 128 self.name = name
129 self._unix_name = _UnixName(self.name)
130 self._unix_name_used = False
90 self.optional = json.get('optional', False) 131 self.optional = json.get('optional', False)
91 self.description = json.get('description') 132 self.description = json.get('description')
92 # TODO(calamity) maybe check for circular refs? could that be a problem?
93 if '$ref' in json: 133 if '$ref' in json:
94 self.ref_type = json['$ref'] 134 self.ref_type = json['$ref']
95 self.type_ = PropertyType.REF 135 self.type_ = PropertyType.REF
96 elif 'type' in json: 136 elif 'type' in json:
97 json_type = json['type'] 137 json_type = json['type']
98 if json_type == 'string': 138 if json_type == 'string':
99 self.type_ = PropertyType.STRING 139 self.type_ = PropertyType.STRING
100 elif json_type == 'boolean': 140 elif json_type == 'any':
141 self.type_ = PropertyType.ANY
142 elif json_type == 'boolean':
101 self.type_ = PropertyType.BOOLEAN 143 self.type_ = PropertyType.BOOLEAN
102 elif json_type == 'integer': 144 elif json_type == 'integer':
103 self.type_ = PropertyType.INTEGER 145 self.type_ = PropertyType.INTEGER
104 elif json_type == 'double': 146 elif json_type == 'number':
105 self.type_ = PropertyType.DOUBLE 147 self.type_ = PropertyType.DOUBLE
106 elif json_type == 'array': 148 elif json_type == 'array':
107 self.item_type = Property(name + "_inner", json['items']) 149 self.item_type = Property(name + "Element", json['items'])
108 self.type_ = PropertyType.ARRAY 150 self.type_ = PropertyType.ARRAY
109 elif json_type == 'object': 151 elif json_type == 'object':
110 self.properties = {} 152 self.properties = {}
111 self.type_ = PropertyType.OBJECT 153 self.type_ = PropertyType.OBJECT
112 for key, val in json['properties'].items(): 154 for key, val in json['properties'].items():
113 self.properties[key] = Property(key, val) 155 self.properties[key] = Property(key, val)
114 else: 156 else:
115 raise NotImplementedError(json_type) 157 raise NotImplementedError(json_type)
116 elif 'choices' in json: 158 elif 'choices' in json:
159 assert len(json['choices']), 'Choices has no choices\n%s' % json
160 self.choices = {}
117 self.type_ = PropertyType.CHOICES 161 self.type_ = PropertyType.CHOICES
118 self.choices = {} 162 for choice_json in json['choices']:
163 choice = Property(self.name, choice_json)
164 # A choice needs to have its unix_name set elsewhere
Yoyo Zhou 2012/02/10 01:49:33 Ok. What I meant was "elsewhere" is vague. I foun
calamity 2012/02/10 03:52:50 Done.
165 choice._unix_name = None
166 # The existence of any single choice is optional
167 choice.optional = True
168 self.choices[choice.type_] = choice
169 else:
170 raise NotImplementedError(json)
171
172 def GetUnixName(self):
173 """Gets the property's unix_name. Raises AttributeError if not set.
174 """
175 if self._unix_name is None:
176 raise AttributeError('No unix_name set on %s' % self.name)
177 self._unix_name_used = True
178 return self._unix_name
179
180 def SetUnixName(self, unix_name):
181 """Set the property's unix_name. Raises AttributeError if the unix_name has
182 already been used (GetUnixName has been called).
183 """
184 if unix_name == self._unix_name:
185 return
186 if self._unix_name_used:
187 raise AttributeError(
188 'Cannot set the unix_name on %s; '
189 'it is already used elsewhere as %s' %
190 (self.name, self._unix_name))
191 self._unix_name = unix_name
192 unix_name = property(GetUnixName, SetUnixName)
Yoyo Zhou 2012/02/10 01:49:33 Ah, this line (the python magic) was hiding here.
calamity 2012/02/10 03:52:50 Done.
119 193
120 class PropertyType(object): 194 class PropertyType(object):
121 """Enum of different types of properties/parameters. 195 """Enum of different types of properties/parameters.
122 """ 196 """
123 class _Info(object): 197 class _Info(object):
124 def __init__(self, is_fundamental): 198 def __init__(self, is_fundamental, name):
125 self.is_fundamental = is_fundamental 199 self.is_fundamental = is_fundamental
200 self.name = name
126 201
127 INTEGER = _Info(True) 202 def __repr__(self):
128 DOUBLE = _Info(True) 203 return self.name
129 BOOLEAN = _Info(True) 204
130 STRING = _Info(True) 205 INTEGER = _Info(True, "INTEGER")
131 ARRAY = _Info(False) 206 DOUBLE = _Info(True, "DOUBLE")
132 REF = _Info(False) 207 BOOLEAN = _Info(True, "BOOLEAN")
133 CHOICES = _Info(False) 208 STRING = _Info(True, "STRING")
134 OBJECT = _Info(False) 209 ARRAY = _Info(False, "ARRAY")
210 REF = _Info(False, "REF")
211 CHOICES = _Info(False, "CHOICES")
212 OBJECT = _Info(False, "OBJECT")
213 ANY = _Info(False, "ANY")
214
215 def _UnixName(name):
216 """Returns the unix_style name for a given string in lowerCamelCase format.
217 """
218 return '_'.join([x.lower()
219 for x in re.findall('[A-Z][a-z_]*', name[0].upper() + name[1:])])
220
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698