blob: d49c18f2145560b55947dc5b29fd1f7d70c3c818 [file] [log] [blame]
[email protected]15f08dd2012-01-27 07:29:481# 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
3# found in the LICENSE file.
4
5import os.path
6
[email protected]b2fad3f2012-11-20 02:13:247from json_parse import OrderedDict
[email protected]cb5670c2013-04-10 06:31:088from memoize import memoize
[email protected]b2fad3f2012-11-20 02:13:249
[email protected]116f0a3a2012-04-19 04:22:3810class ParseException(Exception):
11 """Thrown when data in the model is invalid.
12 """
13 def __init__(self, parent, message):
14 hierarchy = _GetModelHierarchy(parent)
15 hierarchy.append(message)
16 Exception.__init__(
17 self, 'Model parse exception at:\n' + '\n'.join(hierarchy))
18
[email protected]15f08dd2012-01-27 07:29:4819class Model(object):
20 """Model of all namespaces that comprise an API.
[email protected]cfe484902012-02-15 14:52:3221
22 Properties:
23 - |namespaces| a map of a namespace name to its model.Namespace
[email protected]15f08dd2012-01-27 07:29:4824 """
25 def __init__(self):
26 self.namespaces = {}
27
[email protected]4636c832013-01-11 02:10:1128 def AddNamespace(self, json, source_file, include_compiler_options=False):
[email protected]a1f774972012-04-17 02:11:0929 """Add a namespace's json to the model and returns the namespace.
[email protected]15f08dd2012-01-27 07:29:4830 """
[email protected]4636c832013-01-11 02:10:1131 namespace = Namespace(json,
32 source_file,
33 include_compiler_options=include_compiler_options)
[email protected]15f08dd2012-01-27 07:29:4834 self.namespaces[namespace.name] = namespace
35 return namespace
36
37class Namespace(object):
38 """An API namespace.
[email protected]cfe484902012-02-15 14:52:3239
40 Properties:
41 - |name| the name of the namespace
[email protected]8426f8d72013-06-23 04:24:5542 - |description| the description of the namespace
[email protected]712eca0f2012-02-21 01:13:0743 - |unix_name| the unix_name of the namespace
44 - |source_file| the file that contained the namespace definition
45 - |source_file_dir| the directory component of |source_file|
46 - |source_file_filename| the filename component of |source_file|
[email protected]4636c832013-01-11 02:10:1147 - |platforms| if not None, the list of platforms that the namespace is
48 available to
[email protected]cfe484902012-02-15 14:52:3249 - |types| a map of type names to their model.Type
50 - |functions| a map of function names to their model.Function
[email protected]b741e8f2012-07-16 21:47:2451 - |events| a map of event names to their model.Function
[email protected]116f0a3a2012-04-19 04:22:3852 - |properties| a map of property names to their model.Property
[email protected]4636c832013-01-11 02:10:1153 - |compiler_options| the compiler_options dict, only present if
54 |include_compiler_options| is True
[email protected]15f08dd2012-01-27 07:29:4855 """
[email protected]4636c832013-01-11 02:10:1156 def __init__(self, json, source_file, include_compiler_options=False):
[email protected]15f08dd2012-01-27 07:29:4857 self.name = json['namespace']
[email protected]8426f8d72013-06-23 04:24:5558 if 'description' not in json:
59 raise ValueError('%s must have a "description" field. This will appear '
60 'on the API summary page.' % self.name)
61 self.description = json.get('description', None)
[email protected]4ba6bdc2012-06-18 20:40:1462 self.unix_name = UnixName(self.name)
[email protected]15f08dd2012-01-27 07:29:4863 self.source_file = source_file
64 self.source_file_dir, self.source_file_filename = os.path.split(source_file)
[email protected]feba21e2012-03-02 15:05:2765 self.parent = None
[email protected]4636c832013-01-11 02:10:1166 self.platforms = _GetPlatforms(json)
[email protected]242d5e7a2013-01-17 06:50:3167 toplevel_origin = Origin(from_client=True, from_json=True)
68 self.types = _GetTypes(self, json, self, toplevel_origin)
69 self.functions = _GetFunctions(self, json, self)
70 self.events = _GetEvents(self, json, self)
71 self.properties = _GetProperties(self, json, self, toplevel_origin)
[email protected]4636c832013-01-11 02:10:1172 if include_compiler_options:
73 self.compiler_options = json.get('compiler_options', {})
[email protected]15f08dd2012-01-27 07:29:4874
[email protected]242d5e7a2013-01-17 06:50:3175class Origin(object):
76 """Stores the possible origin of model object as a pair of bools. These are:
77
78 |from_client| indicating that instances can originate from users of
79 generated code (for example, function results), or
80 |from_json| indicating that instances can originate from the JSON (for
81 example, function parameters)
82
83 It is possible for model objects to originate from both the client and json,
84 for example Types defined in the top-level schema, in which case both
85 |from_client| and |from_json| would be True.
86 """
87 def __init__(self, from_client=False, from_json=False):
88 if not from_client and not from_json:
89 raise ValueError('One of from_client or from_json must be true')
90 self.from_client = from_client
91 self.from_json = from_json
92
[email protected]15f08dd2012-01-27 07:29:4893class Type(object):
94 """A Type defined in the json.
[email protected]cfe484902012-02-15 14:52:3295
96 Properties:
97 - |name| the type name
[email protected]242d5e7a2013-01-17 06:50:3198 - |namespace| the Type's namespace
[email protected]cfe484902012-02-15 14:52:3299 - |description| the description of the type (if provided)
[email protected]feba21e2012-03-02 15:05:27100 - |properties| a map of property unix_names to their model.Property
101 - |functions| a map of function names to their model.Function
[email protected]d32b8e52012-07-23 18:40:08102 - |events| a map of event names to their model.Event
[email protected]242d5e7a2013-01-17 06:50:31103 - |origin| the Origin of the type
104 - |property_type| the PropertyType of this Type
[email protected]cf6d0b32012-04-12 04:30:22105 - |item_type| if this is an array, the type of items in the array
[email protected]0b255f002012-10-05 01:58:47106 - |simple_name| the name of this Type without a namespace
[email protected]242d5e7a2013-01-17 06:50:31107 - |additional_properties| the type of the additional properties, if any is
108 specified
[email protected]15f08dd2012-01-27 07:29:48109 """
[email protected]242d5e7a2013-01-17 06:50:31110 def __init__(self,
111 parent,
112 name,
113 json,
114 namespace,
115 origin):
116 self.name = name
117 self.namespace = namespace
118 self.simple_name = _StripNamespace(self.name, namespace)
119 self.unix_name = UnixName(self.name)
120 self.description = json.get('description', None)
121 self.origin = origin
122 self.parent = parent
123 self.instance_of = json.get('isInstanceOf', None)
124
125 # TODO(kalman): Only objects need functions/events/properties, but callers
126 # assume that all types have them. Fix this.
127 self.functions = _GetFunctions(self, json, namespace)
128 self.events = _GetEvents(self, json, namespace)
129 self.properties = _GetProperties(self, json, namespace, origin)
130
131 json_type = json.get('type', None)
132 if json_type == 'array':
133 self.property_type = PropertyType.ARRAY
134 self.item_type = Type(
135 self, '%sType' % name, json['items'], namespace, origin)
136 elif '$ref' in json:
137 self.property_type = PropertyType.REF
138 self.ref_type = json['$ref']
139 elif 'enum' in json and json_type == 'string':
140 self.property_type = PropertyType.ENUM
141 self.enum_values = [value for value in json['enum']]
142 elif json_type == 'any':
143 self.property_type = PropertyType.ANY
144 elif json_type == 'binary':
145 self.property_type = PropertyType.BINARY
146 elif json_type == 'boolean':
147 self.property_type = PropertyType.BOOLEAN
148 elif json_type == 'integer':
149 self.property_type = PropertyType.INTEGER
150 elif (json_type == 'double' or
151 json_type == 'number'):
152 self.property_type = PropertyType.DOUBLE
153 elif json_type == 'string':
154 self.property_type = PropertyType.STRING
155 elif 'choices' in json:
156 self.property_type = PropertyType.CHOICES
157 self.choices = [Type(self,
158 # The name of the choice type - there had better be
159 # either a type or a $ref specified for the choice.
160 json.get('type', json.get('$ref')),
161 json,
162 namespace,
163 origin)
164 for json in json['choices']]
165 elif json_type == 'object':
[email protected]cf6d0b32012-04-12 04:30:22166 if not (
167 'properties' in json or
168 'additionalProperties' in json or
[email protected]e975d112012-07-31 22:12:43169 'functions' in json or
170 'events' in json):
[email protected]116f0a3a2012-04-19 04:22:38171 raise ParseException(self, name + " has no properties or functions")
[email protected]242d5e7a2013-01-17 06:50:31172 self.property_type = PropertyType.OBJECT
173 additional_properties_json = json.get('additionalProperties', None)
174 if additional_properties_json is not None:
175 self.additional_properties = Type(self,
176 'additionalProperties',
177 additional_properties_json,
178 namespace,
179 origin)
180 else:
181 self.additional_properties = None
182 elif json_type == 'function':
183 self.property_type = PropertyType.FUNCTION
184 # Sometimes we might have an unnamed function, e.g. if it's a property
185 # of an object. Use the name of the property in that case.
186 function_name = json.get('name', name)
187 self.function = Function(self, function_name, json, namespace, origin)
188 else:
189 raise ParseException(self, 'Unsupported JSON type %s' % json_type)
[email protected]15f08dd2012-01-27 07:29:48190
[email protected]15f08dd2012-01-27 07:29:48191class Function(object):
192 """A Function defined in the API.
[email protected]cfe484902012-02-15 14:52:32193
194 Properties:
195 - |name| the function name
[email protected]4636c832013-01-11 02:10:11196 - |platforms| if not None, the list of platforms that the function is
197 available to
[email protected]cfe484902012-02-15 14:52:32198 - |params| a list of parameters to the function (order matters). A separate
[email protected]4636c832013-01-11 02:10:11199 parameter is used for each choice of a 'choices' parameter
[email protected]cfe484902012-02-15 14:52:32200 - |description| a description of the function (if provided)
201 - |callback| the callback parameter to the function. There should be exactly
[email protected]4636c832013-01-11 02:10:11202 one
[email protected]4b3f7852012-07-17 06:33:30203 - |optional| whether the Function is "optional"; this only makes sense to be
[email protected]4636c832013-01-11 02:10:11204 present when the Function is representing a callback property
[email protected]0b255f002012-10-05 01:58:47205 - |simple_name| the name of this Function without a namespace
[email protected]32096af2013-02-06 01:29:31206 - |returns| the return type of the function; None if the function does not
207 return a value
[email protected]15f08dd2012-01-27 07:29:48208 """
[email protected]0b255f002012-10-05 01:58:47209 def __init__(self,
210 parent,
[email protected]242d5e7a2013-01-17 06:50:31211 name,
[email protected]0b255f002012-10-05 01:58:47212 json,
213 namespace,
[email protected]242d5e7a2013-01-17 06:50:31214 origin):
215 self.name = name
[email protected]0b255f002012-10-05 01:58:47216 self.simple_name = _StripNamespace(self.name, namespace)
[email protected]4636c832013-01-11 02:10:11217 self.platforms = _GetPlatforms(json)
[email protected]15f08dd2012-01-27 07:29:48218 self.params = []
[email protected]feba21e2012-03-02 15:05:27219 self.description = json.get('description')
[email protected]15f08dd2012-01-27 07:29:48220 self.callback = None
[email protected]4b3f7852012-07-17 06:33:30221 self.optional = json.get('optional', False)
[email protected]feba21e2012-03-02 15:05:27222 self.parent = parent
[email protected]a9ead752012-05-24 02:08:45223 self.nocompile = json.get('nocompile')
[email protected]c0718352012-08-21 02:34:24224 options = json.get('options', {})
225 self.conditions = options.get('conditions', [])
226 self.actions = options.get('actions', [])
227 self.supports_listeners = options.get('supportsListeners', True)
228 self.supports_rules = options.get('supportsRules', False)
[email protected]242d5e7a2013-01-17 06:50:31229
[email protected]c0718352012-08-21 02:34:24230 def GeneratePropertyFromParam(p):
[email protected]627b54d2013-02-04 23:53:59231 return Property(self, p['name'], p, namespace, origin)
[email protected]db943992012-08-02 14:02:54232
[email protected]c0718352012-08-21 02:34:24233 self.filters = [GeneratePropertyFromParam(filter)
234 for filter in json.get('filters', [])]
[email protected]db943992012-08-02 14:02:54235 callback_param = None
[email protected]b741e8f2012-07-16 21:47:24236 for param in json.get('parameters', []):
[email protected]15f08dd2012-01-27 07:29:48237 if param.get('type') == 'function':
[email protected]db943992012-08-02 14:02:54238 if callback_param:
239 # No ParseException because the webstore has this.
240 # Instead, pretend all intermediate callbacks are properties.
241 self.params.append(GeneratePropertyFromParam(callback_param))
242 callback_param = param
[email protected]15f08dd2012-01-27 07:29:48243 else:
[email protected]db943992012-08-02 14:02:54244 self.params.append(GeneratePropertyFromParam(param))
245
246 if callback_param:
[email protected]0b255f002012-10-05 01:58:47247 self.callback = Function(self,
[email protected]242d5e7a2013-01-17 06:50:31248 callback_param['name'],
[email protected]0b255f002012-10-05 01:58:47249 callback_param,
250 namespace,
[email protected]242d5e7a2013-01-17 06:50:31251 Origin(from_client=True))
[email protected]db943992012-08-02 14:02:54252
[email protected]491e60d32012-07-20 01:03:13253 self.returns = None
254 if 'returns' in json:
[email protected]627b54d2013-02-04 23:53:59255 self.returns = Type(self,
256 '%sReturnType' % name,
257 json['returns'],
258 namespace,
259 origin)
[email protected]15f08dd2012-01-27 07:29:48260
[email protected]15f08dd2012-01-27 07:29:48261class Property(object):
262 """A property of a type OR a parameter to a function.
[email protected]cfe484902012-02-15 14:52:32263 Properties:
264 - |name| name of the property as in the json. This shouldn't change since
265 it is the key used to access DictionaryValues
266 - |unix_name| the unix_style_name of the property. Used as variable name
267 - |optional| a boolean representing whether the property is optional
268 - |description| a description of the property (if provided)
[email protected]242d5e7a2013-01-17 06:50:31269 - |type_| the model.Type of this property
[email protected]0b255f002012-10-05 01:58:47270 - |simple_name| the name of this Property without a namespace
[email protected]15f08dd2012-01-27 07:29:48271 """
[email protected]627b54d2013-02-04 23:53:59272 def __init__(self, parent, name, json, namespace, origin):
[email protected]242d5e7a2013-01-17 06:50:31273 """Creates a Property from JSON.
274 """
[email protected]627b54d2013-02-04 23:53:59275 self.parent = parent
276 self.name = name
277 self._unix_name = UnixName(self.name)
278 self._unix_name_used = False
279 self.origin = origin
280 self.simple_name = _StripNamespace(self.name, namespace)
281 self.description = json.get('description', None)
282 self.optional = json.get('optional', None)
283 self.instance_of = json.get('isInstanceOf', None)
[email protected]242d5e7a2013-01-17 06:50:31284
285 # HACK: only support very specific value types.
286 is_allowed_value = (
287 '$ref' not in json and
288 ('type' not in json or json['type'] == 'integer'
289 or json['type'] == 'string'))
290
[email protected]627b54d2013-02-04 23:53:59291 self.value = None
[email protected]242d5e7a2013-01-17 06:50:31292 if 'value' in json and is_allowed_value:
[email protected]627b54d2013-02-04 23:53:59293 self.value = json['value']
[email protected]242d5e7a2013-01-17 06:50:31294 if 'type' not in json:
295 # Sometimes the type of the value is left out, and we need to figure
296 # it out for ourselves.
[email protected]627b54d2013-02-04 23:53:59297 if isinstance(self.value, int):
[email protected]242d5e7a2013-01-17 06:50:31298 json['type'] = 'integer'
[email protected]627b54d2013-02-04 23:53:59299 elif isinstance(self.value, basestring):
[email protected]242d5e7a2013-01-17 06:50:31300 json['type'] = 'string'
301 else:
302 # TODO(kalman): support more types as necessary.
303 raise ParseException(
[email protected]627b54d2013-02-04 23:53:59304 parent,
305 '"%s" is not a supported type for "value"' % type(self.value))
[email protected]242d5e7a2013-01-17 06:50:31306
[email protected]627b54d2013-02-04 23:53:59307 self.type_ = Type(parent, name, json, namespace, origin)
[email protected]cfe484902012-02-15 14:52:32308
309 def GetUnixName(self):
310 """Gets the property's unix_name. Raises AttributeError if not set.
311 """
[email protected]116f0a3a2012-04-19 04:22:38312 if not self._unix_name:
[email protected]cfe484902012-02-15 14:52:32313 raise AttributeError('No unix_name set on %s' % self.name)
314 self._unix_name_used = True
315 return self._unix_name
316
317 def SetUnixName(self, unix_name):
318 """Set the property's unix_name. Raises AttributeError if the unix_name has
319 already been used (GetUnixName has been called).
320 """
321 if unix_name == self._unix_name:
322 return
323 if self._unix_name_used:
324 raise AttributeError(
325 'Cannot set the unix_name on %s; '
326 'it is already used elsewhere as %s' %
327 (self.name, self._unix_name))
328 self._unix_name = unix_name
329
330 unix_name = property(GetUnixName, SetUnixName)
[email protected]15f08dd2012-01-27 07:29:48331
[email protected]4636c832013-01-11 02:10:11332class _Enum(object):
333 """Superclass for enum types with a "name" field, setting up repr/eq/ne.
334 Enums need to do this so that equality/non-equality work over pickling.
[email protected]fe027682012-08-21 01:51:34335 """
[email protected]4636c832013-01-11 02:10:11336
337 @staticmethod
338 def GetAll(cls):
339 """Yields all _Enum objects declared in |cls|.
340 """
341 for prop_key in dir(cls):
342 prop_value = getattr(cls, prop_key)
343 if isinstance(prop_value, _Enum):
344 yield prop_value
345
346 def __init__(self, name):
[email protected]fe027682012-08-21 01:51:34347 self.name = name
348
[email protected]4636c832013-01-11 02:10:11349 def __repr(self):
[email protected]fe027682012-08-21 01:51:34350 return self.name
351
[email protected]dc5f2fecf2012-08-31 01:55:51352 def __eq__(self, other):
[email protected]4636c832013-01-11 02:10:11353 return type(other) == type(self) and other.name == self.name
[email protected]dc5f2fecf2012-08-31 01:55:51354
[email protected]e15dccb2012-10-05 06:02:25355 def __ne__(self, other):
[email protected]e15dccb2012-10-05 06:02:25356 return not (self == other)
357
[email protected]4636c832013-01-11 02:10:11358class _PropertyTypeInfo(_Enum):
359 def __init__(self, is_fundamental, name):
360 _Enum.__init__(self, name)
361 self.is_fundamental = is_fundamental
362
[email protected]15f08dd2012-01-27 07:29:48363class PropertyType(object):
364 """Enum of different types of properties/parameters.
365 """
[email protected]242d5e7a2013-01-17 06:50:31366 INTEGER = _PropertyTypeInfo(True, "integer")
367 INT64 = _PropertyTypeInfo(True, "int64")
368 DOUBLE = _PropertyTypeInfo(True, "double")
369 BOOLEAN = _PropertyTypeInfo(True, "boolean")
370 STRING = _PropertyTypeInfo(True, "string")
371 ENUM = _PropertyTypeInfo(False, "enum")
372 ARRAY = _PropertyTypeInfo(False, "array")
373 REF = _PropertyTypeInfo(False, "ref")
374 CHOICES = _PropertyTypeInfo(False, "choices")
375 OBJECT = _PropertyTypeInfo(False, "object")
376 FUNCTION = _PropertyTypeInfo(False, "function")
377 BINARY = _PropertyTypeInfo(False, "binary")
378 ANY = _PropertyTypeInfo(False, "any")
[email protected]cfe484902012-02-15 14:52:32379
[email protected]cb5670c2013-04-10 06:31:08380@memoize
[email protected]4ba6bdc2012-06-18 20:40:14381def UnixName(name):
[email protected]cb5670c2013-04-10 06:31:08382 '''Returns the unix_style name for a given lowerCamelCase string.
383 '''
384 unix_name = []
385 for i, c in enumerate(name):
386 if c.isupper() and i > 0:
387 # Replace lowerUpper with lower_Upper.
388 if name[i - 1].islower():
389 unix_name.append('_')
390 # Replace ACMEWidgets with ACME_Widgets
391 elif i + 1 < len(name) and name[i + 1].islower():
392 unix_name.append('_')
393 if c == '.':
394 # Replace hello.world with hello_world.
395 unix_name.append('_')
396 else:
397 # Everything is lowercase.
398 unix_name.append(c.lower())
399 return ''.join(unix_name)
[email protected]cfe484902012-02-15 14:52:32400
[email protected]0b255f002012-10-05 01:58:47401def _StripNamespace(name, namespace):
402 if name.startswith(namespace.name + '.'):
403 return name[len(namespace.name + '.'):]
404 return name
405
[email protected]116f0a3a2012-04-19 04:22:38406def _GetModelHierarchy(entity):
[email protected]feba21e2012-03-02 15:05:27407 """Returns the hierarchy of the given model entity."""
408 hierarchy = []
[email protected]242d5e7a2013-01-17 06:50:31409 while entity is not None:
410 hierarchy.append(getattr(entity, 'name', repr(entity)))
411 if isinstance(entity, Namespace):
412 hierarchy.insert(0, ' in %s' % entity.source_file)
413 entity = getattr(entity, 'parent', None)
[email protected]feba21e2012-03-02 15:05:27414 hierarchy.reverse()
415 return hierarchy
[email protected]116f0a3a2012-04-19 04:22:38416
[email protected]242d5e7a2013-01-17 06:50:31417def _GetTypes(parent, json, namespace, origin):
418 """Creates Type objects extracted from |json|.
[email protected]116f0a3a2012-04-19 04:22:38419 """
[email protected]242d5e7a2013-01-17 06:50:31420 types = OrderedDict()
[email protected]116f0a3a2012-04-19 04:22:38421 for type_json in json.get('types', []):
[email protected]242d5e7a2013-01-17 06:50:31422 type_ = Type(parent, type_json['id'], type_json, namespace, origin)
423 types[type_.name] = type_
424 return types
[email protected]116f0a3a2012-04-19 04:22:38425
[email protected]242d5e7a2013-01-17 06:50:31426def _GetFunctions(parent, json, namespace):
427 """Creates Function objects extracted from |json|.
[email protected]116f0a3a2012-04-19 04:22:38428 """
[email protected]242d5e7a2013-01-17 06:50:31429 functions = OrderedDict()
[email protected]116f0a3a2012-04-19 04:22:38430 for function_json in json.get('functions', []):
[email protected]242d5e7a2013-01-17 06:50:31431 function = Function(parent,
432 function_json['name'],
433 function_json,
434 namespace,
435 Origin(from_json=True))
436 functions[function.name] = function
437 return functions
[email protected]116f0a3a2012-04-19 04:22:38438
[email protected]242d5e7a2013-01-17 06:50:31439def _GetEvents(parent, json, namespace):
440 """Creates Function objects generated from the events in |json|.
[email protected]b741e8f2012-07-16 21:47:24441 """
[email protected]242d5e7a2013-01-17 06:50:31442 events = OrderedDict()
[email protected]b741e8f2012-07-16 21:47:24443 for event_json in json.get('events', []):
[email protected]242d5e7a2013-01-17 06:50:31444 event = Function(parent,
445 event_json['name'],
446 event_json,
447 namespace,
448 Origin(from_client=True))
449 events[event.name] = event
450 return events
[email protected]b741e8f2012-07-16 21:47:24451
[email protected]242d5e7a2013-01-17 06:50:31452def _GetProperties(parent, json, namespace, origin):
453 """Generates Property objects extracted from |json|.
[email protected]116f0a3a2012-04-19 04:22:38454 """
[email protected]242d5e7a2013-01-17 06:50:31455 properties = OrderedDict()
[email protected]116f0a3a2012-04-19 04:22:38456 for name, property_json in json.get('properties', {}).items():
[email protected]627b54d2013-02-04 23:53:59457 properties[name] = Property(parent, name, property_json, namespace, origin)
[email protected]242d5e7a2013-01-17 06:50:31458 return properties
[email protected]4636c832013-01-11 02:10:11459
460class _PlatformInfo(_Enum):
461 def __init__(self, name):
462 _Enum.__init__(self, name)
463
464class Platforms(object):
465 """Enum of the possible platforms.
466 """
467 CHROMEOS = _PlatformInfo("chromeos")
468 CHROMEOS_TOUCH = _PlatformInfo("chromeos_touch")
469 LINUX = _PlatformInfo("linux")
470 MAC = _PlatformInfo("mac")
471 WIN = _PlatformInfo("win")
472
473def _GetPlatforms(json):
474 if 'platforms' not in json:
475 return None
476 platforms = []
477 for platform_name in json['platforms']:
478 for platform_enum in _Enum.GetAll(Platforms):
479 if platform_name == platform_enum.name:
480 platforms.append(platform_enum)
481 break
482 return platforms