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

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

Powered by Google App Engine
This is Rietveld 408576698