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

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

Issue 9491002: json_schema_compiler: any, additionalProperties, functions on types (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: rework, better errors Created 8 years, 9 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 import re
7 import copy 7 import copy
8 8
9 class Model(object): 9 class Model(object):
10 """Model of all namespaces that comprise an API. 10 """Model of all namespaces that comprise an API.
(...skipping 22 matching lines...) Expand all
33 - |name| the name of the namespace 33 - |name| the name of the namespace
34 - |unix_name| the unix_name of the namespace 34 - |unix_name| the unix_name of the namespace
35 - |source_file| the file that contained the namespace definition 35 - |source_file| the file that contained the namespace definition
36 - |source_file_dir| the directory component of |source_file| 36 - |source_file_dir| the directory component of |source_file|
37 - |source_file_filename| the filename component of |source_file| 37 - |source_file_filename| the filename component of |source_file|
38 - |types| a map of type names to their model.Type 38 - |types| a map of type names to their model.Type
39 - |functions| a map of function names to their model.Function 39 - |functions| a map of function names to their model.Function
40 """ 40 """
41 def __init__(self, json, source_file): 41 def __init__(self, json, source_file):
42 self.name = json['namespace'] 42 self.name = json['namespace']
43 self.unix_name = _UnixName(self.name) 43 self.unix_name = UnixName(self.name)
44 self.source_file = source_file 44 self.source_file = source_file
45 self.source_file_dir, self.source_file_filename = os.path.split(source_file) 45 self.source_file_dir, self.source_file_filename = os.path.split(source_file)
46 self.types = {} 46 self.types = {}
47 self.functions = {} 47 self.functions = {}
48 self.parent = None
49 # TODO(calamity): Implement properties on namespaces for shared structures
50 # or constants across a namespace (e.g Windows::WINDOW_ID_NONE).
51 for property_json in json.get('properties', []):
52 pass
48 for type_json in json.get('types', []): 53 for type_json in json.get('types', []):
49 type_ = Type(type_json) 54 type_ = Type(self, type_json['id'], type_json)
50 self.types[type_.name] = type_ 55 self.types[type_.name] = type_
51 for function_json in json.get('functions', []): 56 for function_json in json.get('functions', []):
52 if not function_json.get('nocompile', False): 57 if not function_json.get('nocompile', False):
53 function = Function(function_json) 58 function = Function(self, function_json)
54 self.functions[function.name] = function 59 self.functions[function.name] = function
55 60
56 class Type(object): 61 class Type(object):
57 """A Type defined in the json. 62 """A Type defined in the json.
58 63
59 Properties: 64 Properties:
60 - |name| the type name 65 - |name| the type name
61 - |description| the description of the type (if provided) 66 - |description| the description of the type (if provided)
62 - |properties| a map of property names to their model.Property 67 - |properties| a map of property unix_names to their model.Property
68 - |functions| a map of function names to their model.Function
63 - |from_client| indicates that instances of the Type can originate from the 69 - |from_client| indicates that instances of the Type can originate from the
64 users of generated code, such as top-level types and function results 70 users of generated code, such as top-level types and function results
65 - |from_json| indicates that instances of the Type can originate from the 71 - |from_json| indicates that instances of the Type can originate from the
66 JSON (as described by the schema), such as top-level types and function 72 JSON (as described by the schema), such as top-level types and function
67 parameters 73 parameters
68 """ 74 """
69 def __init__(self, json): 75 def __init__(self, parent, name, json):
70 self.name = json['id'] 76 if not (
77 'properties' in json or
78 'additionalProperties' in json or
79 'functions' in json):
80 raise ParseException(name + " has no properties or functions")
81 self.name = name
71 self.description = json.get('description') 82 self.description = json.get('description')
72 self.from_json = True 83 self.from_json = True
73 self.from_client = True 84 self.from_client = True
74 self.properties = {} 85 self.properties = {}
75 for prop_name, prop_json in json['properties'].items(): 86 self.functions = {}
76 self.properties[prop_name] = Property(prop_name, prop_json, 87 self.parent = parent
88 for function_json in json.get('functions', []):
89 if not function_json.get('nocompile', False):
90 function = Function(self, function_json)
91 self.functions[function.name] = function
92 props = []
93 for prop_name, prop_json in json.get('properties', {}).items():
94 # TODO(calamity): support functions (callbacks) as properties. The model
95 # doesn't support it yet because to h/cc generators don't -- this is
96 # because we'd need to hook it into a base::Callback or something.
97 #
98 # However, pragmatically it's not necessary to support them anyway, since
99 # the instances of functions-on-properties in the extension APIs are all
100 # handled in pure Javascript on the render process (and .: never reach
101 # C++ let alone the browser).
102 if prop_json.get('type') == 'function':
103 continue
104 props.append(Property(self, prop_name, prop_json,
77 from_json=True, 105 from_json=True,
78 from_client=True) 106 from_client=True))
107 additional_properties = json.get('additionalProperties')
108 if additional_properties:
109 props.append(Property(self, 'additionalProperties', additional_properties,
110 is_additional_properties=True))
111
112 for prop in props:
113 if prop.unix_name in self.properties:
114 raise ParseException(
115 self.properties[prop.unix_name].name + ' and ' + prop.name +
116 ' are both named ' + prop.unix_name)
117 self.properties[prop.unix_name] = prop
118 self.properties[prop.unix_name] = prop
not at google - send to devlin 2012/03/01 07:09:37 umm duplicated code?
calamity 2012/03/01 11:05:52 Done.
79 119
80 class Callback(object): 120 class Callback(object):
81 """A callback parameter to a Function. 121 """A callback parameter to a Function.
82 122
83 Properties: 123 Properties:
84 - |params| the parameters to this callback. 124 - |params| the parameters to this callback.
85 """ 125 """
86 def __init__(self, json): 126 def __init__(self, parent, json):
87 params = json['parameters'] 127 params = json['parameters']
128 self.parent = parent
88 self.params = [] 129 self.params = []
89 if len(params) == 0: 130 if len(params) == 0:
90 return 131 return
91 elif len(params) == 1: 132 elif len(params) == 1:
92 param = params[0] 133 param = params[0]
93 self.params.append(Property(param['name'], param, 134 self.params.append(Property(self, param['name'], param,
94 from_client=True)) 135 from_client=True))
95 else: 136 else:
96 raise AssertionError("Callbacks can have at most a single parameter") 137 raise ParseException("Callbacks can have at most a single parameter")
97 138
98 class Function(object): 139 class Function(object):
99 """A Function defined in the API. 140 """A Function defined in the API.
100 141
101 Properties: 142 Properties:
102 - |name| the function name 143 - |name| the function name
103 - |params| a list of parameters to the function (order matters). A separate 144 - |params| a list of parameters to the function (order matters). A separate
104 parameter is used for each choice of a 'choices' parameter. 145 parameter is used for each choice of a 'choices' parameter.
105 - |description| a description of the function (if provided) 146 - |description| a description of the function (if provided)
106 - |callback| the callback parameter to the function. There should be exactly 147 - |callback| the callback parameter to the function. There should be exactly
107 one 148 one
108 """ 149 """
109 def __init__(self, json): 150 def __init__(self, parent, json):
110 self.name = json['name'] 151 self.name = json['name']
111 self.params = [] 152 self.params = []
112 self.description = json['description'] 153 self.description = json.get('description')
113 self.callback = None 154 self.callback = None
155 self.parent = parent
114 for param in json['parameters']: 156 for param in json['parameters']:
115 if param.get('type') == 'function': 157 if param.get('type') == 'function':
116 assert (not self.callback), self.name + " has more than one callback" 158 if self.callback:
117 self.callback = Callback(param) 159 raise ParseException(self.name + " has more than one callback")
160 self.callback = Callback(self, param)
118 else: 161 else:
119 self.params.append(Property(param['name'], param, 162 self.params.append(Property(self, param['name'], param,
120 from_json=True)) 163 from_json=True))
121 164
122 class Property(object): 165 class Property(object):
123 """A property of a type OR a parameter to a function. 166 """A property of a type OR a parameter to a function.
124 167
125 Properties: 168 Properties:
126 - |name| name of the property as in the json. This shouldn't change since 169 - |name| name of the property as in the json. This shouldn't change since
127 it is the key used to access DictionaryValues 170 it is the key used to access DictionaryValues
128 - |unix_name| the unix_style_name of the property. Used as variable name 171 - |unix_name| the unix_style_name of the property. Used as variable name
129 - |optional| a boolean representing whether the property is optional 172 - |optional| a boolean representing whether the property is optional
130 - |description| a description of the property (if provided) 173 - |description| a description of the property (if provided)
131 - |type_| the model.PropertyType of this property 174 - |type_| the model.PropertyType of this property
132 - |ref_type| the type that the REF property is referencing. Can be used to 175 - |ref_type| the type that the REF property is referencing. Can be used to
133 map to its model.Type 176 map to its model.Type
134 - |item_type| a model.Property representing the type of each element in an 177 - |item_type| a model.Property representing the type of each element in an
135 ARRAY 178 ARRAY
136 - |properties| the properties of an OBJECT parameter 179 - |properties| the properties of an OBJECT parameter
137 """ 180 """
138 def __init__(self, name, json, 181
139 from_json=False, 182 def __init__(self, parent, name, json, is_additional_properties=False,
140 from_client=False): 183 from_json=False, from_client=False):
141 """ 184 """
142 Parameters: 185 Parameters:
143 - |from_json| indicates that instances of the Type can originate from the 186 - |from_json| indicates that instances of the Type can originate from the
144 JSON (as described by the schema), such as top-level types and function 187 JSON (as described by the schema), such as top-level types and function
145 parameters 188 parameters
146 - |from_client| indicates that instances of the Type can originate from the 189 - |from_client| indicates that instances of the Type can originate from the
147 users of generated code, such as top-level types and function results 190 users of generated code, such as top-level types and function results
148 """ 191 """
149 self.name = name 192 self.name = name
150 self._unix_name = _UnixName(self.name) 193 self._unix_name = UnixName(self.name)
151 self._unix_name_used = False 194 self._unix_name_used = False
152 self.optional = json.get('optional', False) 195 self.optional = json.get('optional', False)
153 self.description = json.get('description') 196 self.description = json.get('description')
154 if '$ref' in json: 197 self.parent = parent
198 if is_additional_properties:
199 self.type_ = PropertyType.ADDITIONAL_PROPERTIES
200 elif '$ref' in json:
155 self.ref_type = json['$ref'] 201 self.ref_type = json['$ref']
156 self.type_ = PropertyType.REF 202 self.type_ = PropertyType.REF
157 elif 'enum' in json: 203 elif 'enum' in json:
158 self.enum_values = [] 204 self.enum_values = []
159 for value in json['enum']: 205 for value in json['enum']:
160 self.enum_values.append(value) 206 self.enum_values.append(value)
161 self.type_ = PropertyType.ENUM 207 self.type_ = PropertyType.ENUM
162 elif 'type' in json: 208 elif 'type' in json:
163 json_type = json['type'] 209 json_type = json['type']
164 if json_type == 'string': 210 if json_type == 'string':
165 self.type_ = PropertyType.STRING 211 self.type_ = PropertyType.STRING
166 elif json_type == 'any': 212 elif json_type == 'any':
167 self.type_ = PropertyType.ANY 213 self.type_ = PropertyType.ANY
168 elif json_type == 'boolean': 214 elif json_type == 'boolean':
169 self.type_ = PropertyType.BOOLEAN 215 self.type_ = PropertyType.BOOLEAN
170 elif json_type == 'integer': 216 elif json_type == 'integer':
171 self.type_ = PropertyType.INTEGER 217 self.type_ = PropertyType.INTEGER
172 elif json_type == 'number': 218 elif json_type == 'number':
173 self.type_ = PropertyType.DOUBLE 219 self.type_ = PropertyType.DOUBLE
174 elif json_type == 'array': 220 elif json_type == 'array':
175 self.item_type = Property(name + "Element", json['items'], 221 self.item_type = Property(self, name + "Element", json['items'],
176 from_json, 222 from_json=from_json,
177 from_client) 223 from_client=from_client)
178 self.type_ = PropertyType.ARRAY 224 self.type_ = PropertyType.ARRAY
179 elif json_type == 'object': 225 elif json_type == 'object':
180 self.type_ = PropertyType.OBJECT 226 self.type_ = PropertyType.OBJECT
181 # These members are read when this OBJECT Property is used as a Type 227 # These members are read when this OBJECT Property is used as a Type
182 self.properties = {} 228 self.properties = {}
183 self.from_json = from_json 229 self.from_json = from_json
184 self.from_client = from_client 230 self.from_client = from_client
185 for key, val in json.get('properties', {}).items(): 231 type_ = Type(self, self.name, json)
186 self.properties[key] = Property(key, val, 232 self.properties = type_.properties
187 from_json, 233 self.functions = type_.functions
188 from_client)
189 else: 234 else:
190 raise NotImplementedError(json_type) 235 raise ParseException(self, 'type ' + json_type + ' not recognized')
191 elif 'choices' in json: 236 elif 'choices' in json:
192 assert len(json['choices']), 'Choices has no choices\n%s' % json 237 if not json['choices']:
238 raise ParseException('Choices has no choices')
193 self.choices = {} 239 self.choices = {}
194 self.type_ = PropertyType.CHOICES 240 self.type_ = PropertyType.CHOICES
195 for choice_json in json['choices']: 241 for choice_json in json['choices']:
196 choice = Property(self.name, choice_json, 242 choice = Property(self, self.name, choice_json,
197 from_json, 243 from_json=from_json,
198 from_client) 244 from_client=from_client)
199 # A choice gets its unix_name set in 245 # A choice gets its unix_name set in
200 # cpp_type_generator.GetExpandedChoicesInParams 246 # cpp_type_generator.GetExpandedChoicesInParams
201 choice._unix_name = None 247 choice._unix_name = None
202 # The existence of any single choice is optional 248 # The existence of any single choice is optional
203 choice.optional = True 249 choice.optional = True
204 self.choices[choice.type_] = choice 250 self.choices[choice.type_] = choice
205 else: 251 else:
206 raise NotImplementedError(json) 252 raise ParseException('Property has no type, $ref or choices')
207 253
208 def GetUnixName(self): 254 def GetUnixName(self):
209 """Gets the property's unix_name. Raises AttributeError if not set. 255 """Gets the property's unix_name. Raises AttributeError if not set.
210 """ 256 """
211 if self._unix_name is None: 257 if self._unix_name is None:
212 raise AttributeError('No unix_name set on %s' % self.name) 258 raise AttributeError('No unix_name set on %s' % self.name)
213 self._unix_name_used = True 259 self._unix_name_used = True
214 return self._unix_name 260 return self._unix_name
215 261
216 def SetUnixName(self, unix_name): 262 def SetUnixName(self, unix_name):
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
250 INTEGER = _Info(True, "INTEGER") 296 INTEGER = _Info(True, "INTEGER")
251 DOUBLE = _Info(True, "DOUBLE") 297 DOUBLE = _Info(True, "DOUBLE")
252 BOOLEAN = _Info(True, "BOOLEAN") 298 BOOLEAN = _Info(True, "BOOLEAN")
253 STRING = _Info(True, "STRING") 299 STRING = _Info(True, "STRING")
254 ENUM = _Info(False, "ENUM") 300 ENUM = _Info(False, "ENUM")
255 ARRAY = _Info(False, "ARRAY") 301 ARRAY = _Info(False, "ARRAY")
256 REF = _Info(False, "REF") 302 REF = _Info(False, "REF")
257 CHOICES = _Info(False, "CHOICES") 303 CHOICES = _Info(False, "CHOICES")
258 OBJECT = _Info(False, "OBJECT") 304 OBJECT = _Info(False, "OBJECT")
259 ANY = _Info(False, "ANY") 305 ANY = _Info(False, "ANY")
306 ADDITIONAL_PROPERTIES = _Info(False, "ADDITIONAL_PROPERTIES")
260 307
261 def _UnixName(name): 308 def UnixName(name):
262 """Returns the unix_style name for a given lowerCamelCase string. 309 """Returns the unix_style name for a given lowerCamelCase string.
263 """ 310 """
264 return '_'.join([x.lower() 311 return '_'.join([x.lower()
265 for x in re.findall('[A-Z][a-z_]*', name[0].upper() + name[1:])]) 312 for x in re.findall('[A-Z][a-z_]*', name[0].upper() + name[1:])])
266 313
314 class ParseException(Exception):
not at google - send to devlin 2012/03/01 07:09:37 docs
calamity 2012/03/01 11:05:52 Done.
315 def __init__(self, parent, message):
316 hierarchy = HierarchyOf(parent)
317 hierarchy.append(message)
318 Exception.__init__(
319 self, 'Model parse exception at: \n' + '\n'.join(hierarchy))
not at google - send to devlin 2012/03/01 07:09:37 unnecessary space in the middle of "at: \n" cos n
calamity 2012/03/01 11:05:52 Done.
320
321 def HierarchyOf(prop):
not at google - send to devlin 2012/03/01 07:09:37 write ALL the docs call it GetModelHierarchy... w
calamity 2012/03/01 11:05:52 Done.
322 hierarchy = []
323 entity = prop
324 while entity:
325 try:
326 hierarchy.append(entity.name)
327 except AttributeError:
328 hierarchy.append(repr(entity))
329 entity = entity.parent
330 hierarchy.reverse()
331 return hierarchy
332
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698