Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 | |
| 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 = {} |
| 65 properties = json.get('properties') | |
| 48 for prop_name, prop_json in json['properties'].items(): | 66 for prop_name, prop_json in json['properties'].items(): |
| 49 self.properties[prop_name] = Property(prop_name, prop_json) | 67 self.properties[prop_name] = Property(prop_name, prop_json) |
| 50 | 68 |
| 51 class Callback(object): | 69 class Callback(object): |
| 52 """A callback parameter to a Function. | 70 """A callback parameter to a Function. |
| 71 | |
| 72 Properties: | |
| 73 - |params| the parameters to this callback. There should only be one unless | |
| 74 the parameter is of type 'choices' in which case each choice is processed | |
| 75 as a separate parameter | |
| 53 """ | 76 """ |
| 54 def __init__(self, json): | 77 def __init__(self, json): |
| 55 params = json['parameters'] | 78 params = json['parameters'] |
| 79 self.params = [] | |
| 56 if len(params) == 0: | 80 if len(params) == 0: |
| 57 self.param = None | 81 return |
| 58 elif len(params) == 1: | 82 elif len(params) == 1: |
| 59 param = params[0] | 83 param = params[0] |
| 60 self.param = Property(param['name'], param) | 84 if param.get('choices'): |
| 85 for choice in param['choices']: | |
| 86 self.params.append(_Choice('callback', choice)) | |
| 87 else: | |
| 88 self.params.append(Property(param['name'], param)) | |
| 61 else: | 89 else: |
| 62 raise AssertionError("Callbacks can have at most a single parameter") | 90 raise AssertionError("Callbacks can have at most a single parameter") |
| 63 | 91 |
| 64 class Function(object): | 92 class Function(object): |
| 65 """A Function defined in the API. | 93 """A Function defined in the API. |
| 94 | |
| 95 Properties: | |
| 96 - |name| the function name | |
| 97 - |params| a list of parameters to the function (order matters). A separate | |
| 98 parameter is used for each choice of a 'choices' parameter. | |
| 99 - |description| a description of the function (if provided) | |
| 100 - |callback| the callback parameter to the function. There should be exactly | |
| 101 one | |
| 66 """ | 102 """ |
| 67 def __init__(self, json): | 103 def __init__(self, json): |
| 68 self.name = json['name'] | 104 self.name = json['name'] |
| 69 self.params = [] | 105 self.params = [] |
| 70 self.description = json['description'] | 106 self.description = json['description'] |
| 71 self.callback = None | 107 self.callback = None |
| 72 self.type_dependencies = {} | |
| 73 for param in json['parameters']: | 108 for param in json['parameters']: |
| 74 if param.get('type') == 'function': | 109 if param.get('type') == 'function': |
| 75 assert (not self.callback), "Function has more than one callback" | 110 assert (not self.callback), self.name + " has more than one callback" |
| 76 self.callback = Callback(param) | 111 self.callback = Callback(param) |
| 112 elif param.get('choices'): | |
| 113 for choice in param['choices']: | |
| 114 self.params.append(_Choice(self.name, choice)) | |
| 77 else: | 115 else: |
| 78 self.params.append(Property(param['name'], param)) | 116 self.params.append(Property(param['name'], param)) |
| 79 assert (self.callback), "Function does not support callback" | 117 assert (self.callback), self.name + " does not support callback" |
| 80 | 118 |
| 81 # TODO(calamity): handle Enum/choices | 119 # TODO(calamity): handle Enum |
| 82 class Property(object): | 120 class Property(object): |
| 83 """A property of a type OR a parameter to a function. | 121 """A property of a type OR a parameter to a function. |
| 84 | 122 |
| 85 Members will change based on PropertyType. Check self.type_ to determine which | 123 Properties: |
| 86 members actually exist. | 124 - |name| name of the property as in the json |
| 125 - |unix_name| the unix_style_name of the property | |
| 126 - |optional| a boolean representing whether the property is optional | |
| 127 - |description| a description of the property (if provided) | |
| 128 - |type_| the model.PropertyType of this property | |
| 129 - |ref_type| the type that the REF property is referencing. Can be used to map to | |
| 130 its model.Type | |
|
not at google - send to devlin
2012/02/06 13:14:48
line wrap
calamity
2012/02/08 00:52:31
Done.
| |
| 131 - |item_type| a model.Property representing the type of each element in an | |
| 132 ARRAY | |
| 133 - |properties| the properties of an OBJECT parameter | |
| 87 """ | 134 """ |
| 88 def __init__(self, name, json): | 135 def __init__(self, name, json, unix_name=None, is_choice=False): |
| 136 if not re.match('^[a-z][a-zA-Z0-9]*$', name): | |
| 137 raise AssertionError('Name %s must be lowerCamelCase' % name) | |
| 89 self.name = name | 138 self.name = name |
| 139 self.unix_name = unix_name | |
| 140 self.is_choice = is_choice | |
| 141 if not self.unix_name: | |
| 142 self.unix_name = _UnixName(self.name) | |
| 90 self.optional = json.get('optional', False) | 143 self.optional = json.get('optional', False) |
| 91 self.description = json.get('description') | 144 self.description = json.get('description') |
| 92 # TODO(calamity) maybe check for circular refs? could that be a problem? | |
| 93 if '$ref' in json: | 145 if '$ref' in json: |
| 94 self.ref_type = json['$ref'] | 146 self.ref_type = json['$ref'] |
| 95 self.type_ = PropertyType.REF | 147 self.type_ = PropertyType.REF |
| 96 elif 'type' in json: | 148 elif 'type' in json: |
| 97 json_type = json['type'] | 149 json_type = json['type'] |
| 98 if json_type == 'string': | 150 if json_type == 'string': |
| 99 self.type_ = PropertyType.STRING | 151 self.type_ = PropertyType.STRING |
| 100 elif json_type == 'boolean': | 152 elif json_type == 'any': |
| 153 self.type_ = PropertyType.ANY | |
| 154 elif json_type == 'boolean': | |
| 101 self.type_ = PropertyType.BOOLEAN | 155 self.type_ = PropertyType.BOOLEAN |
| 102 elif json_type == 'integer': | 156 elif json_type == 'integer': |
| 103 self.type_ = PropertyType.INTEGER | 157 self.type_ = PropertyType.INTEGER |
| 104 elif json_type == 'double': | 158 elif json_type == 'number': |
| 105 self.type_ = PropertyType.DOUBLE | 159 self.type_ = PropertyType.DOUBLE |
| 106 elif json_type == 'array': | 160 elif json_type == 'array': |
| 107 self.item_type = Property(name + "_inner", json['items']) | 161 self.item_type = Property(name + "Element", json['items']) |
| 108 self.type_ = PropertyType.ARRAY | 162 self.type_ = PropertyType.ARRAY |
| 109 elif json_type == 'object': | 163 elif json_type == 'object': |
| 110 self.properties = {} | 164 self.properties = {} |
| 111 self.type_ = PropertyType.OBJECT | 165 self.type_ = PropertyType.OBJECT |
| 112 for key, val in json['properties'].items(): | 166 for key, val in json['properties'].items(): |
| 113 self.properties[key] = Property(key, val) | 167 if 'choices' in val: |
| 168 for choice in val['choices']: | |
| 169 choice_property = _Choice(self.name, choice) | |
| 170 self.properties[choice_property.name] = choice_property | |
| 171 else: | |
| 172 self.properties[key] = Property(key, val) | |
| 114 else: | 173 else: |
| 115 raise NotImplementedError(json_type) | 174 raise NotImplementedError(json_type) |
| 116 elif 'choices' in json: | 175 else: |
| 117 self.type_ = PropertyType.CHOICES | 176 raise NotImplementedError(json) |
| 118 self.choices = {} | |
| 119 | 177 |
| 120 class PropertyType(object): | 178 class PropertyType(object): |
| 121 """Enum of different types of properties/parameters. | 179 """Enum of different types of properties/parameters. |
| 122 """ | 180 """ |
| 123 class _Info(object): | 181 class _Info(object): |
| 124 def __init__(self, is_fundamental): | 182 def __init__(self, is_fundamental, name): |
| 125 self.is_fundamental = is_fundamental | 183 self.is_fundamental = is_fundamental |
| 184 self.name = name | |
| 126 | 185 |
| 127 INTEGER = _Info(True) | 186 def __repr__(self): |
| 128 DOUBLE = _Info(True) | 187 return self.name |
| 129 BOOLEAN = _Info(True) | 188 |
| 130 STRING = _Info(True) | 189 INTEGER = _Info(True, "INTEGER") |
| 131 ARRAY = _Info(False) | 190 DOUBLE = _Info(True, "DOUBLE") |
| 132 REF = _Info(False) | 191 BOOLEAN = _Info(True, "BOOLEAN") |
| 133 CHOICES = _Info(False) | 192 STRING = _Info(True, "STRING") |
| 134 OBJECT = _Info(False) | 193 ARRAY = _Info(False, "ARRAY") |
| 194 REF = _Info(False, "REF") | |
| 195 CHOICE = _Info(False, "CHOICE") | |
| 196 OBJECT = _Info(False, "OBJECT") | |
| 197 ANY = _Info(False, "ANY") | |
| 198 | |
| 199 def _UnixName(name): | |
| 200 """Returns the unix_style name for a given string. | |
| 201 """ | |
| 202 return '_'.join([x.lower() | |
| 203 for x in re.findall('[A-Z][a-z_]*', name[0].upper() + name[1:])]) | |
| 204 | |
| 205 def _Choice(name, json): | |
|
not at google - send to devlin
2012/02/06 13:14:48
I made a comment here earlier about structuring th
not at google - send to devlin
2012/02/06 13:25:34
Sorry, I should have said an array of possible Pro
calamity
2012/02/08 00:52:31
Done.
| |
| 206 """Creates a Property for an item under 'choices' in the json. | |
| 207 """ | |
| 208 raise NotImplementedError('%s\nChoices is not fully implemented' % json) | |
|
not at google - send to devlin
2012/02/06 13:14:48
Choices is implemented for return values right? O
calamity
2012/02/08 00:52:31
It's done now, but I thought it made sense to disa
| |
| 209 property_type = json.get('type') | |
| 210 if not property_type: | |
| 211 property_type = json['$ref'].lower() | |
| 212 if not property_type: | |
| 213 raise NotImplementedError(json) | |
| 214 # Append choice type to the unix_name | |
| 215 return Property(name, json, is_choice=True, | |
| 216 unix_name=_UnixName('%s%s' % | |
| 217 (name, property_type[0].upper() + property_type[1:])) | |
| 218 ) | |
| OLD | NEW |