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

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
« no previous file with comments | « no previous file | chrome/common/extensions/docs/server2/api_data_source_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
58 type Event. Returns a dictionary mapping the name of a member to that 58 type Event. Returns a dictionary mapping the name of a member to that
59 member's definition. 59 member's definition.
60 ''' 60 '''
61 assert 'types' in events, \ 61 assert 'types' in events, \
62 'The dictionary |events| must contain the key "types".' 62 'The dictionary |events| must contain the key "types".'
63 event_list = [t for t in events['types'] if t.get('name') == 'Event'] 63 event_list = [t for t in events['types'] if t.get('name') == 'Event']
64 assert len(event_list) == 1, 'Exactly one type must be called "Event".' 64 assert len(event_list) == 1, 'Exactly one type must be called "Event".'
65 return _GetByNameDict(event_list[0]) 65 return _GetByNameDict(event_list[0])
66 66
67 67
68 class _APINodeCursor(object):
not at google - send to devlin 2014/06/28 00:21:02 use of this class would be nicer if you used the w
69 '''An abstract representation of a node in an APISchemaGraph.
70 The current position in the graph is represented by a path into the
71 underlying dictionary. So if the APISchemaGraph is:
72
73 {
74 'tabs': {
75 'types': {
76 'Tab': {
77 'properties': {
78 'url': {
79 ...
80 }
81 }
82 }
83 }
84 }
85 }
86
87 then the 'url' property would be represented by:
88
89 ['tabs', 'types', 'Tab', 'properties', 'url']
90 '''
91 def __init__(self, availability_finder, namespace_name):
92 # The cursor begins life at the root.
93 self._lookup_path = [namespace_name]
94 self._node_availabilities = availability_finder.GetAPINodeAvailability(
95 namespace_name)
96 self._namespace_name = namespace_name
97
98 def _GetParentPath(self):
99 '''Returns the path pointing to this node's parent.
100 '''
101 assert len(self._lookup_path) > 2, \
102 'Tried to look up parent for the top-level node.'
103
104 # lookup_path[-1] is the name of the current node.
105 # lookup_path[-2] is this node's category (e.g. types, events, etc.).
106 # Thus, the parent node is described by lookup_path[:-2].
107 return self._lookup_path[:-2]
108
109 def _LookupNodeAvailability(self):
110 '''Returns the ChannelInfo object for this node.
111 '''
112 return self._node_availabilities.Lookup(*self._lookup_path).annotation
113
114 def _LookupParentNodeAvailability(self):
115 '''Returns the ChannelInfo object for this node's parent.
116 '''
117 return self._node_availabilities.Lookup(*self._GetParentPath()).annotation
118
119 def _CheckNamespacePrefix(self):
120 '''API schemas may prepend the namespace name to top-level types
121 (e.g. declarativeWebRequest > types > declarativeWebRequest.IgnoreRules),
122 but just the base name (here, 'IgnoreRules') will be in the |lookup_path|.
123 Try creating an alternate |lookup_path| by adding the namespace name.
124 '''
125 # lookup_path[0] is always the API namespace, and
126 # lookup_path[1] is always the node category (e.g. types, functions, etc.).
127 # Thus, lookup_path[2] is always the top-level node name.
128 base_name = self._lookup_path[2]
129 self._lookup_path[2] = '%s.%s' % (self._namespace_name, base_name)
130 node_availability = self._LookupNodeAvailability()
131 if node_availability is not None:
132 return node_availability
133 # We want to maintain a working lookup_path, so only restore it
134 # if modifying the lookup_path did not work.
135 self._lookup_path[2] = base_name
136 return None
137
138 def _CheckEventCallback(self):
139 '''Within API schemas, an event has a list of 'properties' that the event's
140 callback expects. The callback itself is not explicitly represented in the
141 schema. However, when creating an event node in _JSCModel, a callback node
142 is generated and acts as the parent for the event's properties.
143 Modify |lookup_path| to check the original schema format.
144 '''
145 if 'events' in self._lookup_path and 'callback' in self._lookup_path:
146 lookup_path_copy = copy(self._lookup_path)
147 self._lookup_path.remove('callback')
148 node_availability = self._LookupNodeAvailability()
149 self._lookup_path = lookup_path_copy
150 return node_availability
151 return None
152
153 def GetAvailability(self):
154 '''Returns availability information for this node.
155 '''
156 if self._lookup_path[0] != self._namespace_name:
157 # |lookup_path| won't be lookup up if it doesn't start with the API name.
158 return None
159
160 for lookup in (self._LookupNodeAvailability,
161 self._CheckEventCallback,
162 self._CheckNamespacePrefix):
163 node_availability = lookup()
164 if node_availability is not None:
165 break
166
167 if node_availability is None:
168 logging.warning('No availability found for: %s' % ' > '.join(
169 self._lookup_path))
170 return None
171
172 # Only render this node's availability if it differs from the parent
173 # node's availability.
174 if node_availability == self._LookupParentNodeAvailability():
175 return None
176 return node_availability
177
178 def DescendTo(self, *path):
179 '''Moves down the APISchemaGraph, following |path|.
180 '''
181 self._lookup_path.extend(path)
182
183 def Ascend(self):
184 '''Moves up one node.
185 '''
186 self._lookup_path = self._lookup_path[:-1]
187
188
68 class _JSCModel(object): 189 class _JSCModel(object):
69 '''Uses a Model from the JSON Schema Compiler and generates a dict that 190 '''Uses a Model from the JSON Schema Compiler and generates a dict that
70 a Handlebar template can use for a data source. 191 a Handlebar template can use for a data source.
71 ''' 192 '''
72 193
73 def __init__(self, 194 def __init__(self,
74 namespace, 195 namespace,
75 availability, 196 availability_finder,
76 json_cache, 197 json_cache,
77 template_cache, 198 template_cache,
78 features_bundle, 199 features_bundle,
79 event_byname_future): 200 event_byname_future):
80 self._availability = availability 201 self._availability = availability_finder.GetAPIAvailability(namespace.name)
202 self._current_node = _APINodeCursor(availability_finder, namespace.name)
81 self._api_availabilities = json_cache.GetFromFile( 203 self._api_availabilities = json_cache.GetFromFile(
82 posixpath.join(JSON_TEMPLATES, 'api_availabilities.json')) 204 posixpath.join(JSON_TEMPLATES, 'api_availabilities.json'))
83 self._intro_tables = json_cache.GetFromFile( 205 self._intro_tables = json_cache.GetFromFile(
84 posixpath.join(JSON_TEMPLATES, 'intro_tables.json')) 206 posixpath.join(JSON_TEMPLATES, 'intro_tables.json'))
85 self._api_features = features_bundle.GetAPIFeatures() 207 self._api_features = features_bundle.GetAPIFeatures()
86 self._template_cache = template_cache 208 self._template_cache = template_cache
87 self._event_byname_future = event_byname_future 209 self._event_byname_future = event_byname_future
88 self._namespace = namespace 210 self._namespace = namespace
89 211
90 def _GetLink(self, link): 212 def _GetLink(self, link):
(...skipping 29 matching lines...) Expand all
120 if not self._IsExperimental(): 242 if not self._IsExperimental():
121 return { 243 return {
122 self._availability.channel_info.channel: True 244 self._availability.channel_info.channel: True
123 } 245 }
124 return None 246 return None
125 247
126 def _IsExperimental(self): 248 def _IsExperimental(self):
127 return self._namespace.name.startswith('experimental') 249 return self._namespace.name.startswith('experimental')
128 250
129 def _GenerateTypes(self, types): 251 def _GenerateTypes(self, types):
130 return [self._GenerateType(t) for t in types] 252 self._current_node.DescendTo('types')
253 types_ = [self._GenerateType(t) for t in types]
254 self._current_node.Ascend()
255 return types_
131 256
132 def _GenerateType(self, type_): 257 def _GenerateType(self, type_):
258 self._current_node.DescendTo(type_.simple_name)
133 type_dict = { 259 type_dict = {
134 'name': type_.simple_name, 260 'name': type_.simple_name,
135 'description': type_.description, 261 'description': type_.description,
136 'properties': self._GenerateProperties(type_.properties), 262 'properties': self._GenerateProperties(type_.properties),
137 'functions': self._GenerateFunctions(type_.functions), 263 'functions': self._GenerateFunctions(type_.functions),
138 'events': self._GenerateEvents(type_.events), 264 'events': self._GenerateEvents(type_.events),
139 'id': _CreateId(type_, 'type') 265 'id': _CreateId(type_, 'type'),
140 } 266 }
141 self._RenderTypeInformation(type_, type_dict) 267 self._RenderTypeInformation(type_, type_dict)
268 self._current_node.Ascend()
142 return type_dict 269 return type_dict
143 270
144 def _GenerateFunctions(self, functions): 271 def _GenerateFunctions(self, functions):
145 return [self._GenerateFunction(f) for f in functions.values()] 272 self._current_node.DescendTo('functions')
273 fns = [self._GenerateFunction(f) for f in functions.values()]
274 self._current_node.Ascend()
275 return fns
146 276
147 def _GenerateFunction(self, function): 277 def _GenerateFunction(self, function):
278 self._current_node.DescendTo(function.simple_name)
148 function_dict = { 279 function_dict = {
149 'name': function.simple_name, 280 'name': function.simple_name,
150 'description': function.description, 281 'description': function.description,
151 'callback': self._GenerateCallback(function.callback), 282 'callback': self._GenerateCallback(function.callback),
152 'parameters': [], 283 'parameters': [],
153 'returns': None, 284 'returns': None,
154 'id': _CreateId(function, 'method') 285 'id': _CreateId(function, 'method'),
286 'availability': self._GetAvailabilityTemplate()
155 } 287 }
156 self._AddCommonProperties(function_dict, function) 288 self._AddCommonProperties(function_dict, function)
157 if function.returns: 289 if function.returns:
158 function_dict['returns'] = self._GenerateType(function.returns) 290 function_dict['returns'] = self._GenerateType(function.returns)
159 for param in function.params: 291 for param in function.params:
160 function_dict['parameters'].append(self._GenerateProperty(param)) 292 function_dict['parameters'].append(self._GenerateProperty(param))
161 if function.callback is not None: 293 if function.callback is not None:
162 # Show the callback as an extra parameter. 294 # Show the callback as an extra parameter.
163 function_dict['parameters'].append( 295 function_dict['parameters'].append(
164 self._GenerateCallbackProperty(function.callback)) 296 self._GenerateCallbackProperty(function.callback))
165 if len(function_dict['parameters']) > 0: 297 if len(function_dict['parameters']) > 0:
166 function_dict['parameters'][-1]['last'] = True 298 function_dict['parameters'][-1]['last'] = True
299 self._current_node.Ascend()
167 return function_dict 300 return function_dict
168 301
169 def _GenerateEvents(self, events): 302 def _GenerateEvents(self, events):
170 return [self._GenerateEvent(e) for e in events.values() 303 self._current_node.DescendTo('events')
171 if not e.supports_dom] 304 events_ = [self._GenerateEvent(e) for e in events.values()
305 if not e.supports_dom]
306 self._current_node.Ascend()
307 return events_
172 308
173 def _GenerateDomEvents(self, events): 309 def _GenerateDomEvents(self, events):
174 return [self._GenerateEvent(e) for e in events.values() 310 self._current_node.DescendTo('events')
175 if e.supports_dom] 311 events_ = [self._GenerateEvent(e) for e in events.values()
312 if e.supports_dom]
313 self._current_node.Ascend()
314 return events_
176 315
177 def _GenerateEvent(self, event): 316 def _GenerateEvent(self, event):
317 self._current_node.DescendTo(event.simple_name)
178 event_dict = { 318 event_dict = {
179 'name': event.simple_name, 319 'name': event.simple_name,
180 'description': event.description, 320 'description': event.description,
181 'filters': [self._GenerateProperty(f) for f in event.filters], 321 'filters': [self._GenerateProperty(f) for f in event.filters],
182 'conditions': [self._GetLink(condition) 322 'conditions': [self._GetLink(condition)
183 for condition in event.conditions], 323 for condition in event.conditions],
184 'actions': [self._GetLink(action) for action in event.actions], 324 'actions': [self._GetLink(action) for action in event.actions],
185 'supportsRules': event.supports_rules, 325 'supportsRules': event.supports_rules,
186 'supportsListeners': event.supports_listeners, 326 'supportsListeners': event.supports_listeners,
187 'properties': [], 327 'properties': [],
(...skipping 20 matching lines...) Expand all
208 event_dict['byName']['addListener'] = { 348 event_dict['byName']['addListener'] = {
209 'name': 'addListener', 349 'name': 'addListener',
210 'callback': self._GenerateFunction(callback_object), 350 'callback': self._GenerateFunction(callback_object),
211 'parameters': [callback_parameters] 351 'parameters': [callback_parameters]
212 } 352 }
213 if event.supports_dom: 353 if event.supports_dom:
214 # Treat params as properties of the custom Event object associated with 354 # Treat params as properties of the custom Event object associated with
215 # this DOM Event. 355 # this DOM Event.
216 event_dict['properties'] += [self._GenerateProperty(param) 356 event_dict['properties'] += [self._GenerateProperty(param)
217 for param in event.params] 357 for param in event.params]
358 self._current_node.Ascend()
218 return event_dict 359 return event_dict
219 360
220 def _GenerateCallback(self, callback): 361 def _GenerateCallback(self, callback):
221 if not callback: 362 if not callback:
222 return None 363 return None
223 callback_dict = { 364 callback_dict = {
224 'name': callback.simple_name, 365 'name': callback.simple_name,
225 'simple_type': {'simple_type': 'function'}, 366 'simple_type': {'simple_type': 'function'},
226 'optional': callback.optional, 367 'optional': callback.optional,
227 'parameters': [] 368 'parameters': []
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
329 # if they share the same 'title' attribute. 470 # if they share the same 'title' attribute.
330 row_titles = [row['title'] for row in intro_rows] 471 row_titles = [row['title'] for row in intro_rows]
331 for misc_row in self._GetMiscIntroRows(): 472 for misc_row in self._GetMiscIntroRows():
332 if misc_row['title'] in row_titles: 473 if misc_row['title'] in row_titles:
333 intro_rows[row_titles.index(misc_row['title'])] = misc_row 474 intro_rows[row_titles.index(misc_row['title'])] = misc_row
334 else: 475 else:
335 intro_rows.append(misc_row) 476 intro_rows.append(misc_row)
336 477
337 return intro_rows 478 return intro_rows
338 479
480 def _GetAvailabilityTemplate(self, status=None, version=None, scheduled=None):
481 '''Returns an object that the templates use to display availability
482 information.
483 '''
484 if status is None:
485 availability_info = self._current_node.GetAvailability()
486 if availability_info is None:
487 return None
488 status = availability_info.channel
489 version = availability_info.version
490 return {
491 'partial': self._template_cache.GetFromFile(
492 '%sintro_tables/%s_message.html' % (PRIVATE_TEMPLATES, status)).Get(),
493 'version': version,
494 'scheduled': scheduled
495 }
496
339 def _GetIntroDescriptionRow(self): 497 def _GetIntroDescriptionRow(self):
340 ''' Generates the 'Description' row data for an API intro table. 498 ''' Generates the 'Description' row data for an API intro table.
341 ''' 499 '''
342 return { 500 return {
343 'title': 'Description', 501 'title': 'Description',
344 'content': [ 502 'content': [
345 { 'text': self._namespace.description } 503 { 'text': self._namespace.description }
346 ] 504 ]
347 } 505 }
348 506
349 def _GetIntroAvailabilityRow(self): 507 def _GetIntroAvailabilityRow(self):
350 ''' Generates the 'Availability' row data for an API intro table. 508 ''' Generates the 'Availability' row data for an API intro table.
351 ''' 509 '''
352 if self._IsExperimental(): 510 if self._IsExperimental():
353 status = 'experimental' 511 status = 'experimental'
354 version = None 512 version = None
355 scheduled = None 513 scheduled = None
356 else: 514 else:
357 status = self._availability.channel_info.channel 515 status = self._availability.channel_info.channel
358 version = self._availability.channel_info.version 516 version = self._availability.channel_info.version
359 scheduled = self._availability.scheduled 517 scheduled = self._availability.scheduled
360 return { 518 return {
361 'title': 'Availability', 519 'title': 'Availability',
362 'content': [{ 520 'content': [
363 'partial': self._template_cache.GetFromFile( 521 self._GetAvailabilityTemplate(status=status,
364 posixpath.join(PRIVATE_TEMPLATES, 522 version=version,
365 'intro_tables', 523 scheduled=scheduled)
366 '%s_message.html' % status)).Get(), 524 ]
367 'version': version,
368 'scheduled': scheduled
369 }]
370 } 525 }
371 526
372 def _GetIntroDependencyRows(self): 527 def _GetIntroDependencyRows(self):
373 # Devtools aren't in _api_features. If we're dealing with devtools, bail. 528 # Devtools aren't in _api_features. If we're dealing with devtools, bail.
374 if 'devtools' in self._namespace.name: 529 if 'devtools' in self._namespace.name:
375 return [] 530 return []
376 531
377 api_feature = self._api_features.Get().get(self._namespace.name) 532 api_feature = self._api_features.Get().get(self._namespace.name)
378 if not api_feature: 533 if not api_feature:
379 logging.error('"%s" not found in _api_features.json' % 534 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): 654 def _GetSchemaModel(self, platform, api_name):
500 object_store_key = '/'.join((platform, api_name)) 655 object_store_key = '/'.join((platform, api_name))
501 jsc_model_future = self._model_cache.Get(object_store_key) 656 jsc_model_future = self._model_cache.Get(object_store_key)
502 model_future = self._platform_bundle.GetAPIModels(platform).GetModel( 657 model_future = self._platform_bundle.GetAPIModels(platform).GetModel(
503 api_name) 658 api_name)
504 def resolve(): 659 def resolve():
505 jsc_model = jsc_model_future.Get() 660 jsc_model = jsc_model_future.Get()
506 if jsc_model is None: 661 if jsc_model is None:
507 jsc_model = _JSCModel( 662 jsc_model = _JSCModel(
508 model_future.Get(), 663 model_future.Get(),
509 self._platform_bundle.GetAvailabilityFinder( 664 self._platform_bundle.GetAvailabilityFinder(platform),
510 platform).GetAPIAvailability(api_name),
511 self._json_cache, 665 self._json_cache,
512 self._template_cache, 666 self._template_cache,
513 self._platform_bundle.GetFeaturesBundle(platform), 667 self._platform_bundle.GetFeaturesBundle(platform),
514 self._LoadEventByName(platform)).ToDict() 668 self._LoadEventByName(platform)).ToDict()
515 self._model_cache.Set(object_store_key, jsc_model) 669 self._model_cache.Set(object_store_key, jsc_model)
516 return jsc_model 670 return jsc_model
517 return Future(callback=resolve) 671 return Future(callback=resolve)
518 672
519 def _GetImpl(self, platform, api_name): 673 def _GetImpl(self, platform, api_name):
520 handlebar_dict_future = self._GetSchemaModel(platform, api_name) 674 handlebar_dict_future = self._GetSchemaModel(platform, api_name)
(...skipping 15 matching lines...) Expand all
536 getter = lambda: 0 690 getter = lambda: 0
537 getter.get = lambda api_name: self._GetImpl(platform, api_name).Get() 691 getter.get = lambda api_name: self._GetImpl(platform, api_name).Get()
538 return getter 692 return getter
539 693
540 def Cron(self): 694 def Cron(self):
541 futures = [] 695 futures = []
542 for platform in GetPlatforms(): 696 for platform in GetPlatforms():
543 futures += [self._GetImpl(platform, name) 697 futures += [self._GetImpl(platform, name)
544 for name in self._platform_bundle.GetAPIModels(platform).GetNames()] 698 for name in self._platform_bundle.GetAPIModels(platform).GetNames()]
545 return Collect(futures, except_pass=FileNotFoundError) 699 return Collect(futures, except_pass=FileNotFoundError)
OLDNEW
« no previous file with comments | « no previous file | chrome/common/extensions/docs/server2/api_data_source_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698