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

Side by Side Diff: chrome/common/extensions/docs/server2/api_data_source.py

Issue 354073004: Docserver: Add template support for object level availability (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 5 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 from copy import copy 5 from copy import copy
6 import logging 6 import logging
7 import os 7 import os
8 import posixpath 8 import posixpath
9 9
10 from data_source import DataSource 10 from data_source import DataSource
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
65 return _GetByNameDict(event_list[0]) 65 return _GetByNameDict(event_list[0])
66 66
67 67
68 class _JSCModel(object): 68 class _JSCModel(object):
69 '''Uses a Model from the JSON Schema Compiler and generates a dict that 69 '''Uses a Model from the JSON Schema Compiler and generates a dict that
70 a Handlebar template can use for a data source. 70 a Handlebar template can use for a data source.
71 ''' 71 '''
72 72
73 def __init__(self, 73 def __init__(self,
74 namespace, 74 namespace,
75 availability, 75 availability_finder,
76 json_cache, 76 json_cache,
77 template_cache, 77 template_cache,
78 features_bundle, 78 features_bundle,
79 event_byname_future): 79 event_byname_future):
80 self._availability = availability 80 self._availability = availability_finder.GetAPIAvailability(namespace.name)
81 self._node_availabilities = availability_finder.GetAPINodeAvailability(
82 namespace.name)
81 self._api_availabilities = json_cache.GetFromFile( 83 self._api_availabilities = json_cache.GetFromFile(
82 posixpath.join(JSON_TEMPLATES, 'api_availabilities.json')) 84 posixpath.join(JSON_TEMPLATES, 'api_availabilities.json'))
83 self._intro_tables = json_cache.GetFromFile( 85 self._intro_tables = json_cache.GetFromFile(
84 posixpath.join(JSON_TEMPLATES, 'intro_tables.json')) 86 posixpath.join(JSON_TEMPLATES, 'intro_tables.json'))
85 self._api_features = features_bundle.GetAPIFeatures() 87 self._api_features = features_bundle.GetAPIFeatures()
86 self._template_cache = template_cache 88 self._template_cache = template_cache
87 self._event_byname_future = event_byname_future 89 self._event_byname_future = event_byname_future
88 self._namespace = namespace 90 self._namespace = namespace
89 91
90 def _GetLink(self, link): 92 def _GetLink(self, link):
91 ref = link if '.' in link else (self._namespace.name + '.' + link) 93 ref = link if '.' in link else (self._namespace.name + '.' + link)
92 return { 'ref': ref, 'text': link, 'name': link } 94 return { 'ref': ref, 'text': link, 'name': link }
93 95
94 def ToDict(self): 96 def ToDict(self):
95 if self._namespace is None: 97 if self._namespace is None:
96 return {} 98 return {}
97 chrome_dot_name = 'chrome.%s' % self._namespace.name 99 chrome_dot_name = 'chrome.%s' % self._namespace.name
100 lookup_path = [self._namespace.name]
ahernandez 2014/06/26 23:53:31 On the previous patch you mentioned an issue with
not at google - send to devlin 2014/06/27 00:22:54 The issues is that when you put the "inline_doc" a
98 as_dict = { 101 as_dict = {
99 'name': self._namespace.name, 102 'name': self._namespace.name,
100 'namespace': self._namespace.documentation_options.get('namespace', 103 'namespace': self._namespace.documentation_options.get('namespace',
101 chrome_dot_name), 104 chrome_dot_name),
102 'title': self._namespace.documentation_options.get('title', 105 'title': self._namespace.documentation_options.get('title',
103 chrome_dot_name), 106 chrome_dot_name),
104 'documentationOptions': self._namespace.documentation_options, 107 'documentationOptions': self._namespace.documentation_options,
105 'types': self._GenerateTypes(self._namespace.types.values()), 108 'types': self._GenerateTypes(self._namespace.types.values(), lookup_path),
ahernandez 2014/06/26 23:53:31 I'm only generating object level availability for
not at google - send to devlin 2014/06/27 00:22:54 thanks! I wonder if there's a way to avoid passin
106 'functions': self._GenerateFunctions(self._namespace.functions), 109 'functions': self._GenerateFunctions(self._namespace.functions),
107 'events': self._GenerateEvents(self._namespace.events), 110 'events': self._GenerateEvents(self._namespace.events),
108 'domEvents': self._GenerateDomEvents(self._namespace.events), 111 'domEvents': self._GenerateDomEvents(self._namespace.events),
109 'properties': self._GenerateProperties(self._namespace.properties), 112 'properties': self._GenerateProperties(self._namespace.properties),
110 'introList': self._GetIntroTableList(), 113 'introList': self._GetIntroTableList(),
111 'channelWarning': self._GetChannelWarning(), 114 'channelWarning': self._GetChannelWarning(),
112 } 115 }
113 if self._namespace.deprecated: 116 if self._namespace.deprecated:
114 as_dict['deprecated'] = self._namespace.deprecated 117 as_dict['deprecated'] = self._namespace.deprecated
115 118
116 as_dict['byName'] = _GetByNameDict(as_dict) 119 as_dict['byName'] = _GetByNameDict(as_dict)
117 return as_dict 120 return as_dict
118 121
119 def _GetChannelWarning(self): 122 def _GetChannelWarning(self):
120 if not self._IsExperimental(): 123 if not self._IsExperimental():
121 return { 124 return {
122 self._availability.channel_info.channel: True 125 self._availability.channel_info.channel: True
123 } 126 }
124 return None 127 return None
125 128
126 def _IsExperimental(self): 129 def _IsExperimental(self):
127 return self._namespace.name.startswith('experimental') 130 return self._namespace.name.startswith('experimental')
128 131
129 def _GenerateTypes(self, types): 132 def _LookupNodeAvailability(self, lookup_path):
130 return [self._GenerateType(t) for t in types] 133 '''Returns the ChannelInfo object for the node represented by
134 |lookup_path|.
135 '''
136 return self._node_availabilities.Lookup(*lookup_path).annotation
not at google - send to devlin 2014/06/27 00:22:54 I think we should make |lookup_path| a little clas
131 137
132 def _GenerateType(self, type_): 138 def _CheckNamespacePrefix(self, lookup_path):
ahernandez 2014/06/26 23:53:31 Looks like Rietveld got confused. Is there a way t
not at google - send to devlin 2014/06/27 00:22:54 why? what happened?
139 '''API schemas may prepend the namespace name to top-level types
140 (e.g. declarativeWebRequest > types > declarativeWebRequest.IgnoreRules),
141 but just the base name (here, 'IgnoreRules') will be in the |lookup_path|.
142 Try creating an alternate |lookup_path| by adding the namespace name.
143 '''
144 # lookup_path[0] is always the API namespace, and
145 # lookup_path[1] is always the node category (e.g. types, functions, etc.).
146 # Thus, lookup_path[2] is always the top-level node name.
147 base_name = lookup_path[2]
148 lookup_path[2] = '%s.%s' % (self._namespace.name, base_name)
149 node_availability = self._LookupNodeAvailability(lookup_path)
150 if node_availability is not None:
151 return node_availability
152 # We want to maintain a working lookup_path, so only restore it
153 # if modifying the lookup_path did not work.
154 lookup_path[2] = base_name
155 return None
156
157 def _CheckEventCallback(self, lookup_path):
158 '''Within API schemas, an event has a list of 'properties' that the event's
159 callback expects. The callback itself is not explicitly represented in the
160 schema. However, when creating an event node in _JSCModel, a callback node
161 is generated and acts as the parent for the event's properties.
162 Modify |lookup_path| to check the original schema format.
163 '''
164 if 'events' in lookup_path and 'callback' in lookup_path:
165 lookup_path_copy = copy(lookup_path)
166 lookup_path_copy.remove('callback')
167 return self._LookupNodeAvailability(lookup_path_copy)
168 return None
169
170 def _GetNodeAvailability(self, lookup_path):
171 '''Uses an APISchemaGraph instance to find availability information for
172 a node pointed to by |lookup_path|.
173 '''
174 if lookup_path[0] != self._namespace.name:
ahernandez 2014/06/26 23:53:31 There are many instances where a blank lookup path
ahernandez 2014/06/27 03:00:01 This comment is more for me to remember, but this
175 # |lookup_path| won't be lookup up if it doesn't start with the API name.
176 return None
177
178 assert len(lookup_path) > 2, \
179 'Tried to look up parent availability for the top-level node.'
180
181 for lookup in (self._LookupNodeAvailability,
182 self._CheckEventCallback,
183 self._CheckNamespacePrefix):
184 node_availability = lookup(lookup_path)
185 if node_availability is not None:
186 break
187
188 if node_availability is None:
189 logging.warning('No availability found for: %s' % ' > '.join(lookup_path))
190 return None
191
192 # lookup_path[-1] is the node we found availability for.
193 # lookup_path[-2] is the node category (e.g. types, events, etc.).
194 # Thus, the parent node is described by lookup_path[:-2].
195 if node_availability == self._LookupNodeAvailability(lookup_path[:-2]):
196 # Only render this node's availability if it differs from the parent
197 # node's availability.
198 return None
199 return self._GetAvailabilityTemplate(node_availability.channel,
200 node_availability.version)
201
202 def _GenerateTypes(self, types, lookup_path):
203 return [self._GenerateType(t, lookup_path + ['types']) for t in types]
204
205 def _GenerateType(self, type_, lookup_path):
206 next_lookup_path = lookup_path + [type_.simple_name]
133 type_dict = { 207 type_dict = {
134 'name': type_.simple_name, 208 'name': type_.simple_name,
135 'description': type_.description, 209 'description': type_.description,
136 'properties': self._GenerateProperties(type_.properties), 210 'properties': self._GenerateProperties(type_.properties),
137 'functions': self._GenerateFunctions(type_.functions), 211 'functions': self._GenerateFunctions(type_.functions),
138 'events': self._GenerateEvents(type_.events), 212 'events': self._GenerateEvents(type_.events),
139 'id': _CreateId(type_, 'type') 213 'id': _CreateId(type_, 'type'),
214 'availability': self._GetNodeAvailability(next_lookup_path)
140 } 215 }
141 self._RenderTypeInformation(type_, type_dict) 216 self._RenderTypeInformation(type_, type_dict)
142 return type_dict 217 return type_dict
143 218
144 def _GenerateFunctions(self, functions): 219 def _GenerateFunctions(self, functions):
145 return [self._GenerateFunction(f) for f in functions.values()] 220 return [self._GenerateFunction(f) for f in functions.values()]
146 221
147 def _GenerateFunction(self, function): 222 def _GenerateFunction(self, function):
148 function_dict = { 223 function_dict = {
149 'name': function.simple_name, 224 'name': function.simple_name,
150 'description': function.description, 225 'description': function.description,
151 'callback': self._GenerateCallback(function.callback), 226 'callback': self._GenerateCallback(function.callback),
152 'parameters': [], 227 'parameters': [],
153 'returns': None, 228 'returns': None,
154 'id': _CreateId(function, 'method') 229 'id': _CreateId(function, 'method')
155 } 230 }
156 self._AddCommonProperties(function_dict, function) 231 self._AddCommonProperties(function_dict, function)
157 if function.returns: 232 if function.returns:
158 function_dict['returns'] = self._GenerateType(function.returns) 233 function_dict['returns'] = self._GenerateType(function.returns, [])
ahernandez 2014/06/26 23:53:31 Here is an example of the blank lookup path. I thi
not at google - send to devlin 2014/06/27 00:22:54 ok :)
159 for param in function.params: 234 for param in function.params:
160 function_dict['parameters'].append(self._GenerateProperty(param)) 235 function_dict['parameters'].append(self._GenerateProperty(param))
161 if function.callback is not None: 236 if function.callback is not None:
162 # Show the callback as an extra parameter. 237 # Show the callback as an extra parameter.
163 function_dict['parameters'].append( 238 function_dict['parameters'].append(
164 self._GenerateCallbackProperty(function.callback)) 239 self._GenerateCallbackProperty(function.callback))
165 if len(function_dict['parameters']) > 0: 240 if len(function_dict['parameters']) > 0:
166 function_dict['parameters'][-1]['last'] = True 241 function_dict['parameters'][-1]['last'] = True
167 return function_dict 242 return function_dict
168 243
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after
259 'returns': None, 334 'returns': None,
260 'id': _CreateId(property_, 'property') 335 'id': _CreateId(property_, 'property')
261 } 336 }
262 self._AddCommonProperties(property_dict, property_) 337 self._AddCommonProperties(property_dict, property_)
263 338
264 if type_.property_type == model.PropertyType.FUNCTION: 339 if type_.property_type == model.PropertyType.FUNCTION:
265 function = type_.function 340 function = type_.function
266 for param in function.params: 341 for param in function.params:
267 property_dict['parameters'].append(self._GenerateProperty(param)) 342 property_dict['parameters'].append(self._GenerateProperty(param))
268 if function.returns: 343 if function.returns:
269 property_dict['returns'] = self._GenerateType(function.returns) 344 property_dict['returns'] = self._GenerateType(function.returns, [])
270 345
271 value = property_.value 346 value = property_.value
272 if value is not None: 347 if value is not None:
273 if isinstance(value, int): 348 if isinstance(value, int):
274 property_dict['value'] = _FormatValue(value) 349 property_dict['value'] = _FormatValue(value)
275 else: 350 else:
276 property_dict['value'] = value 351 property_dict['value'] = value
277 else: 352 else:
278 self._RenderTypeInformation(type_, property_dict) 353 self._RenderTypeInformation(type_, property_dict)
279 354
280 return property_dict 355 return property_dict
281 356
282 def _GenerateCallbackProperty(self, callback): 357 def _GenerateCallbackProperty(self, callback):
283 property_dict = { 358 property_dict = {
284 'name': callback.simple_name, 359 'name': callback.simple_name,
285 'description': callback.description, 360 'description': callback.description,
286 'optional': callback.optional, 361 'optional': callback.optional,
287 'is_callback': True, 362 'is_callback': True,
288 'id': _CreateId(callback, 'property'), 363 'id': _CreateId(callback, 'property'),
289 'simple_type': 'function', 364 'simple_type': 'function',
290 } 365 }
291 if (callback.parent is not None and 366 if (callback.parent is not None and
292 not isinstance(callback.parent, model.Namespace)): 367 not isinstance(callback.parent, model.Namespace)):
293 property_dict['parentName'] = callback.parent.simple_name 368 property_dict['parentName'] = callback.parent.simple_name
294 return property_dict 369 return property_dict
295 370
296 def _RenderTypeInformation(self, type_, dst_dict): 371 def _RenderTypeInformation(self, type_, dst_dict):
297 dst_dict['is_object'] = type_.property_type == model.PropertyType.OBJECT 372 dst_dict['is_object'] = type_.property_type == model.PropertyType.OBJECT
298 if type_.property_type == model.PropertyType.CHOICES: 373 if type_.property_type == model.PropertyType.CHOICES:
299 dst_dict['choices'] = self._GenerateTypes(type_.choices) 374 dst_dict['choices'] = self._GenerateTypes(type_.choices, [])
300 # We keep track of which == last for knowing when to add "or" between 375 # We keep track of which == last for knowing when to add "or" between
301 # choices in templates. 376 # choices in templates.
302 if len(dst_dict['choices']) > 0: 377 if len(dst_dict['choices']) > 0:
303 dst_dict['choices'][-1]['last'] = True 378 dst_dict['choices'][-1]['last'] = True
304 elif type_.property_type == model.PropertyType.REF: 379 elif type_.property_type == model.PropertyType.REF:
305 dst_dict['link'] = self._GetLink(type_.ref_type) 380 dst_dict['link'] = self._GetLink(type_.ref_type)
306 elif type_.property_type == model.PropertyType.ARRAY: 381 elif type_.property_type == model.PropertyType.ARRAY:
307 dst_dict['array'] = self._GenerateType(type_.item_type) 382 dst_dict['array'] = self._GenerateType(type_.item_type, [])
308 elif type_.property_type == model.PropertyType.ENUM: 383 elif type_.property_type == model.PropertyType.ENUM:
309 dst_dict['enum_values'] = [ 384 dst_dict['enum_values'] = [
310 {'name': value.name, 'description': value.description} 385 {'name': value.name, 'description': value.description}
311 for value in type_.enum_values] 386 for value in type_.enum_values]
312 if len(dst_dict['enum_values']) > 0: 387 if len(dst_dict['enum_values']) > 0:
313 dst_dict['enum_values'][-1]['last'] = True 388 dst_dict['enum_values'][-1]['last'] = True
314 elif type_.instance_of is not None: 389 elif type_.instance_of is not None:
315 dst_dict['simple_type'] = type_.instance_of 390 dst_dict['simple_type'] = type_.instance_of
316 else: 391 else:
317 dst_dict['simple_type'] = type_.property_type.name 392 dst_dict['simple_type'] = type_.property_type.name
(...skipping 11 matching lines...) Expand all
329 # if they share the same 'title' attribute. 404 # if they share the same 'title' attribute.
330 row_titles = [row['title'] for row in intro_rows] 405 row_titles = [row['title'] for row in intro_rows]
331 for misc_row in self._GetMiscIntroRows(): 406 for misc_row in self._GetMiscIntroRows():
332 if misc_row['title'] in row_titles: 407 if misc_row['title'] in row_titles:
333 intro_rows[row_titles.index(misc_row['title'])] = misc_row 408 intro_rows[row_titles.index(misc_row['title'])] = misc_row
334 else: 409 else:
335 intro_rows.append(misc_row) 410 intro_rows.append(misc_row)
336 411
337 return intro_rows 412 return intro_rows
338 413
414 def _GetAvailabilityTemplate(self, status, version=None, scheduled=None):
415 '''Returns an object that the templates use to display availability
416 information.
417 '''
418 return {
419 'partial': self._template_cache.GetFromFile(
420 '%sintro_tables/%s_message.html' % (PRIVATE_TEMPLATES, status)).Get(),
421 'version': version,
422 'scheduled': scheduled
423 }
424
339 def _GetIntroDescriptionRow(self): 425 def _GetIntroDescriptionRow(self):
340 ''' Generates the 'Description' row data for an API intro table. 426 ''' Generates the 'Description' row data for an API intro table.
341 ''' 427 '''
342 return { 428 return {
343 'title': 'Description', 429 'title': 'Description',
344 'content': [ 430 'content': [
345 { 'text': self._namespace.description } 431 { 'text': self._namespace.description }
346 ] 432 ]
347 } 433 }
348 434
349 def _GetIntroAvailabilityRow(self): 435 def _GetIntroAvailabilityRow(self):
350 ''' Generates the 'Availability' row data for an API intro table. 436 ''' Generates the 'Availability' row data for an API intro table.
351 ''' 437 '''
352 if self._IsExperimental(): 438 if self._IsExperimental():
353 status = 'experimental' 439 status = 'experimental'
354 version = None 440 version = None
355 scheduled = None 441 scheduled = None
356 else: 442 else:
357 status = self._availability.channel_info.channel 443 status = self._availability.channel_info.channel
358 version = self._availability.channel_info.version 444 version = self._availability.channel_info.version
359 scheduled = self._availability.scheduled 445 scheduled = self._availability.scheduled
360 return { 446 return {
361 'title': 'Availability', 447 'title': 'Availability',
362 'content': [{ 448 'content': [
363 'partial': self._template_cache.GetFromFile( 449 self._GetAvailabilityTemplate(status,
364 posixpath.join(PRIVATE_TEMPLATES, 450 version=version,
365 'intro_tables', 451 scheduled=scheduled)
366 '%s_message.html' % status)).Get(), 452 ]
367 'version': version,
368 'scheduled': scheduled
369 }]
370 } 453 }
371 454
372 def _GetIntroDependencyRows(self): 455 def _GetIntroDependencyRows(self):
373 # Devtools aren't in _api_features. If we're dealing with devtools, bail. 456 # Devtools aren't in _api_features. If we're dealing with devtools, bail.
374 if 'devtools' in self._namespace.name: 457 if 'devtools' in self._namespace.name:
375 return [] 458 return []
376 459
377 api_feature = self._api_features.Get().get(self._namespace.name) 460 api_feature = self._api_features.Get().get(self._namespace.name)
378 if not api_feature: 461 if not api_feature:
379 logging.error('"%s" not found in _api_features.json' % 462 logging.error('"%s" not found in _api_features.json' %
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after
499 def _GetSchemaModel(self, platform, api_name): 582 def _GetSchemaModel(self, platform, api_name):
500 object_store_key = '/'.join((platform, api_name)) 583 object_store_key = '/'.join((platform, api_name))
501 jsc_model_future = self._model_cache.Get(object_store_key) 584 jsc_model_future = self._model_cache.Get(object_store_key)
502 model_future = self._platform_bundle.GetAPIModels(platform).GetModel( 585 model_future = self._platform_bundle.GetAPIModels(platform).GetModel(
503 api_name) 586 api_name)
504 def resolve(): 587 def resolve():
505 jsc_model = jsc_model_future.Get() 588 jsc_model = jsc_model_future.Get()
506 if jsc_model is None: 589 if jsc_model is None:
507 jsc_model = _JSCModel( 590 jsc_model = _JSCModel(
508 model_future.Get(), 591 model_future.Get(),
509 self._platform_bundle.GetAvailabilityFinder( 592 self._platform_bundle.GetAvailabilityFinder(platform),
510 platform).GetAPIAvailability(api_name),
511 self._json_cache, 593 self._json_cache,
512 self._template_cache, 594 self._template_cache,
513 self._platform_bundle.GetFeaturesBundle(platform), 595 self._platform_bundle.GetFeaturesBundle(platform),
514 self._LoadEventByName(platform)).ToDict() 596 self._LoadEventByName(platform)).ToDict()
515 self._model_cache.Set(object_store_key, jsc_model) 597 self._model_cache.Set(object_store_key, jsc_model)
516 return jsc_model 598 return jsc_model
517 return Future(callback=resolve) 599 return Future(callback=resolve)
518 600
519 def _GetImpl(self, platform, api_name): 601 def _GetImpl(self, platform, api_name):
520 handlebar_dict_future = self._GetSchemaModel(platform, api_name) 602 handlebar_dict_future = self._GetSchemaModel(platform, api_name)
(...skipping 15 matching lines...) Expand all
536 getter = lambda: 0 618 getter = lambda: 0
537 getter.get = lambda api_name: self._GetImpl(platform, api_name).Get() 619 getter.get = lambda api_name: self._GetImpl(platform, api_name).Get()
538 return getter 620 return getter
539 621
540 def Cron(self): 622 def Cron(self):
541 futures = [] 623 futures = []
542 for platform in GetPlatforms(): 624 for platform in GetPlatforms():
543 futures += [self._GetImpl(platform, name) 625 futures += [self._GetImpl(platform, name)
544 for name in self._platform_bundle.GetAPIModels(platform).GetNames()] 626 for name in self._platform_bundle.GetAPIModels(platform).GetNames()]
545 return Collect(futures, except_pass=FileNotFoundError) 627 return Collect(futures, except_pass=FileNotFoundError)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698