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. |
9 """ | 10 """ |
10 def __init__(self): | 11 def __init__(self): |
11 self.namespaces = {} | 12 self.namespaces = {} |
12 | 13 |
13 def AddNamespace(self, json, source_file): | 14 def AddNamespace(self, json, source_file): |
14 """Add a namespace's json to the model if it has a "compile" property set | 15 """Add a namespace's json to the model if it has a "compile" property set |
15 to true. Returns the new namespace or None if a namespace wasn't added. | 16 to true. Returns the new namespace or None if a namespace wasn't added. |
16 """ | 17 """ |
17 if not json.get('compile'): | 18 if not json.get('compile', False): |
18 return None | 19 return None |
19 namespace = Namespace(json, source_file) | 20 namespace = Namespace(json, source_file) |
20 self.namespaces[namespace.name] = namespace | 21 self.namespaces[namespace.name] = namespace |
21 return namespace | 22 return namespace |
22 | 23 |
23 class Namespace(object): | 24 class Namespace(object): |
24 """An API namespace. | 25 """An API namespace. |
25 """ | 26 """ |
26 def __init__(self, json, source_file): | 27 def __init__(self, json, source_file): |
27 self.name = json['namespace'] | 28 self.name = json['namespace'] |
28 self.source_file = source_file | 29 self.source_file = source_file |
29 self.source_file_dir, self.source_file_filename = os.path.split(source_file) | 30 self.source_file_dir, self.source_file_filename = os.path.split(source_file) |
30 self.type_dependencies = {} | 31 self.type_dependencies = {} |
31 self.types = {} | 32 self.types = {} |
32 self.functions = {} | 33 self.functions = {} |
33 for type_json in json['types']: | 34 for type_json in json['types']: |
34 type_ = Type(type_json) | 35 type_ = Type(type_json) |
35 self.types[type_.name] = type_ | 36 self.types[type_.name] = type_ |
36 for function_json in json['functions']: | 37 for function_json in json['functions']: |
37 if not function_json.get('nocompile'): | 38 if function_json.get('compile', True): |
38 function = Function(function_json) | 39 function = Function(function_json) |
39 self.functions[function.name] = function | 40 self.functions[function.name] = function |
40 | 41 |
41 class Type(object): | 42 class Type(object): |
42 """A Type defined in the json. | 43 """A Type defined in the json. |
43 """ | 44 """ |
44 def __init__(self, json): | 45 def __init__(self, json): |
45 self.name = json['id'] | 46 self.name = json['id'] |
46 self.description = json.get('description') | 47 self.description = json.get('description') |
47 self.properties = {} | 48 self.properties = {} |
48 for prop_name, prop_json in json['properties'].items(): | 49 for prop_name, prop_json in json['properties'].items(): |
49 self.properties[prop_name] = Property(prop_name, prop_json) | 50 self.properties[prop_name] = Property(prop_name, prop_json) |
50 | 51 |
51 class Callback(object): | 52 class Callback(object): |
52 """A callback parameter to a Function. | 53 """A callback parameter to a Function. |
53 """ | 54 """ |
54 def __init__(self, json): | 55 def __init__(self, json): |
55 params = json['parameters'] | 56 params = json['parameters'] |
57 self.params = [] | |
56 if len(params) == 0: | 58 if len(params) == 0: |
57 self.param = None | 59 return |
58 elif len(params) == 1: | 60 elif len(params) == 1: |
59 param = params[0] | 61 param = params[0] |
60 self.param = Property(param['name'], param) | 62 if param.get('choices'): |
not at google - send to devlin
2012/02/05 23:42:12
I think you need to document what the available pr
calamity
2012/02/06 11:51:18
Done.
| |
63 for choice in param['choices']: | |
64 self.params.append(_Choice('callback', choice)) | |
65 else: | |
66 self.params.append(Property(param['name'], param)) | |
61 else: | 67 else: |
62 raise AssertionError("Callbacks can have at most a single parameter") | 68 raise AssertionError("Callbacks can have at most a single parameter") |
63 | 69 |
64 class Function(object): | 70 class Function(object): |
65 """A Function defined in the API. | 71 """A Function defined in the API. |
66 """ | 72 """ |
67 def __init__(self, json): | 73 def __init__(self, json): |
68 self.name = json['name'] | 74 self.name = json['name'] |
69 self.params = [] | 75 self.params = [] |
70 self.description = json['description'] | 76 self.description = json['description'] |
71 self.callback = None | 77 self.callback = None |
72 self.type_dependencies = {} | 78 self.type_dependencies = {} |
73 for param in json['parameters']: | 79 for param in json['parameters']: |
74 if param.get('type') == 'function': | 80 if param.get('type') == 'function': |
75 assert (not self.callback), "Function has more than one callback" | 81 assert (not self.callback), self.name + " has more than one callback" |
76 self.callback = Callback(param) | 82 self.callback = Callback(param) |
83 elif param.get('choices'): | |
84 for choice in param['choices']: | |
85 self.params.append(_Choice(self.name, choice)) | |
not at google - send to devlin
2012/02/05 23:42:12
(ditto)
I see now why you are appending a differe
calamity
2012/02/06 11:51:18
Done.
| |
77 else: | 86 else: |
78 self.params.append(Property(param['name'], param)) | 87 self.params.append(Property(param['name'], param)) |
79 assert (self.callback), "Function does not support callback" | 88 assert (self.callback), self.name + " does not support callback" |
80 | 89 |
81 # TODO(calamity): handle Enum/choices | 90 # TODO(calamity): handle Enum/choices |
82 class Property(object): | 91 class Property(object): |
83 """A property of a type OR a parameter to a function. | 92 """A property of a type OR a parameter to a function. |
84 | 93 |
85 Members will change based on PropertyType. Check self.type_ to determine which | 94 Members will change based on PropertyType. Check self.type_ to determine which |
86 members actually exist. | 95 members actually exist. |
87 """ | 96 """ |
88 def __init__(self, name, json): | 97 def __init__(self, name, json, is_choice=False): |
not at google - send to devlin
2012/02/05 23:42:12
is_choice no longer needed
calamity
2012/02/06 11:51:18
I think I'll need something eventually in order to
not at google - send to devlin
2012/02/06 13:14:48
Yeah, see the other couple of comments I've made r
| |
89 self.name = name | 98 self.name = name |
not at google - send to devlin
2012/02/05 23:42:12
I think for self-documentation sake, this should s
calamity
2012/02/06 11:51:18
Done.
| |
99 self.unix_name = _UnixName(self.name) | |
90 self.optional = json.get('optional', False) | 100 self.optional = json.get('optional', False) |
91 self.description = json.get('description') | 101 self.description = json.get('description') |
92 # TODO(calamity) maybe check for circular refs? could that be a problem? | |
93 if '$ref' in json: | 102 if '$ref' in json: |
94 self.ref_type = json['$ref'] | 103 self.ref_type = json['$ref'] |
95 self.type_ = PropertyType.REF | 104 self.type_ = PropertyType.REF |
96 elif 'type' in json: | 105 elif 'type' in json: |
97 json_type = json['type'] | 106 json_type = json['type'] |
98 if json_type == 'string': | 107 if json_type == 'string': |
99 self.type_ = PropertyType.STRING | 108 self.type_ = PropertyType.STRING |
109 elif json_type == 'any': | |
not at google - send to devlin
2012/02/05 23:42:12
extra space
calamity
2012/02/06 11:51:18
Done.
| |
110 self.type_ = PropertyType.ANY | |
100 elif json_type == 'boolean': | 111 elif json_type == 'boolean': |
101 self.type_ = PropertyType.BOOLEAN | 112 self.type_ = PropertyType.BOOLEAN |
102 elif json_type == 'integer': | 113 elif json_type == 'integer': |
103 self.type_ = PropertyType.INTEGER | 114 self.type_ = PropertyType.INTEGER |
104 elif json_type == 'double': | 115 elif json_type == 'double' or json_type == 'number': |
not at google - send to devlin
2012/02/05 23:42:12
We should remove support for "double" and fix up t
calamity
2012/02/06 11:51:18
Done.
| |
105 self.type_ = PropertyType.DOUBLE | 116 self.type_ = PropertyType.DOUBLE |
106 elif json_type == 'array': | 117 elif json_type == 'array': |
107 self.item_type = Property(name + "_inner", json['items']) | 118 self.item_type = Property(name + "_inner", json['items']) |
108 self.type_ = PropertyType.ARRAY | 119 self.type_ = PropertyType.ARRAY |
109 elif json_type == 'object': | 120 elif json_type == 'object': |
110 self.properties = {} | 121 self.properties = {} |
111 self.type_ = PropertyType.OBJECT | 122 self.type_ = PropertyType.OBJECT |
112 for key, val in json['properties'].items(): | 123 for key, val in json['properties'].items(): |
113 self.properties[key] = Property(key, val) | 124 if 'choices' in val: |
125 for choice in val['choices']: | |
126 self.properties[key] = _Choice(self.name, choice) | |
not at google - send to devlin
2012/02/05 23:42:12
doesn't this overwrite the same (and incorrect) ke
calamity
2012/02/06 11:51:18
Done.
| |
127 else: | |
128 self.properties[key] = Property(key, val) | |
114 else: | 129 else: |
115 raise NotImplementedError(json_type) | 130 raise NotImplementedError(json_type) |
116 elif 'choices' in json: | 131 else: |
117 self.type_ = PropertyType.CHOICES | 132 raise NotImplementedError(json) |
118 self.choices = {} | |
119 | 133 |
120 class PropertyType(object): | 134 class PropertyType(object): |
121 """Enum of different types of properties/parameters. | 135 """Enum of different types of properties/parameters. |
122 """ | 136 """ |
123 class _Info(object): | 137 class _Info(object): |
124 def __init__(self, is_fundamental): | 138 def __init__(self, is_fundamental, name): |
125 self.is_fundamental = is_fundamental | 139 self.is_fundamental = is_fundamental |
140 self.name = name | |
126 | 141 |
127 INTEGER = _Info(True) | 142 def __repr__(self): |
128 DOUBLE = _Info(True) | 143 return self.name |
129 BOOLEAN = _Info(True) | 144 |
130 STRING = _Info(True) | 145 INTEGER = _Info(True, "INTEGER") |
131 ARRAY = _Info(False) | 146 DOUBLE = _Info(True, "DOUBLE") |
132 REF = _Info(False) | 147 BOOLEAN = _Info(True, "BOOLEAN") |
133 CHOICES = _Info(False) | 148 STRING = _Info(True, "STRING") |
134 OBJECT = _Info(False) | 149 ARRAY = _Info(False, "ARRAY") |
150 REF = _Info(False, "REF") | |
151 CHOICE = _Info(False, "CHOICE") | |
152 OBJECT = _Info(False, "OBJECT") | |
153 ANY = _Info(False, "ANY") | |
154 | |
155 def _UnixName(name): | |
not at google - send to devlin
2012/02/05 23:42:12
document
calamity
2012/02/06 11:51:18
Done.
| |
156 return '_'.join([x.lower() | |
157 for x in re.findall('[A-Z][a-z_]*', name[0].upper() + name[1:])]) | |
not at google - send to devlin
2012/02/05 23:42:12
i'll just assume that works...
| |
158 | |
159 def _Choice(name, json): | |
not at google - send to devlin
2012/02/05 23:42:12
document
calamity
2012/02/06 11:51:18
Done.
| |
160 prop_type = json.get('type') | |
161 if not prop_type: | |
162 prop_type = json['$ref'].lower() | |
not at google - send to devlin
2012/02/05 23:42:12
I think should structure this slightly differently
| |
163 prop = Property(name, json) | |
164 prop.unix_name = _UnixName('%s_%s' % (name, prop_type)) | |
165 return prop | |
OLD | NEW |