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

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 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):
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 '''
not at google - send to devlin 2014/07/01 16:40:57 nit: blank line here
91 def __init__(self, availability_finder, namespace_name):
92 # The cursor begins life at the root.
93 self._lookup_path = [namespace_name]
not at google - send to devlin 2014/07/01 16:40:57 rather than doing this could you include |namespac
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.).
not at google - send to devlin 2014/07/01 16:40:57 could you define the set of acceptable categories
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.).
not at google - send to devlin 2014/07/01 16:40:57 ditto
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
not at google - send to devlin 2014/07/01 16:40:58 I'd rather you always restored this, and in a try.
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:
not at google - send to devlin 2014/07/01 16:40:57 so if there's an 'events' there *must* be a 'callb
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
not at google - send to devlin 2014/07/01 16:40:57 you should be able to do this without copy'ing; us
150 return node_availability
151 return None
152
153 def GetAvailability(self):
154 '''Returns availability information for this node.
155 '''
156 for lookup in (self._LookupNodeAvailability,
157 self._CheckEventCallback,
158 self._CheckNamespacePrefix):
159 node_availability = lookup()
160 if node_availability is not None:
161 break
162
163 if node_availability is None:
164 logging.warning('No availability found for: %s' % ' > '.join(
165 self._lookup_path))
166 return None
167
168 # Only render this node's availability if it differs from the parent
169 # node's availability.
170 if node_availability == self._LookupParentNodeAvailability():
171 return None
172 return node_availability
173
174 def Descend(self, *path):
175 '''Moves down the APISchemaGraph, following |path|.
176 '''
177 class scope(object):
178 def __enter__(self2):
179 self._lookup_path.extend(path)
not at google - send to devlin 2014/07/01 16:40:58 actually this is fine
180 def __exit__(self2, _, __, ___):
181 self._lookup_path = self._lookup_path[:-len(path)]
not at google - send to devlin 2014/07/01 16:40:57 nit: slightly better to be consistent and mutate |
182 return scope()
183
184
68 class _JSCModel(object): 185 class _JSCModel(object):
69 '''Uses a Model from the JSON Schema Compiler and generates a dict that 186 '''Uses a Model from the JSON Schema Compiler and generates a dict that
70 a Handlebar template can use for a data source. 187 a Handlebar template can use for a data source.
71 ''' 188 '''
72 189
73 def __init__(self, 190 def __init__(self,
74 namespace, 191 namespace,
75 availability_finder, 192 availability_finder,
76 json_cache, 193 json_cache,
77 template_cache, 194 template_cache,
78 features_bundle, 195 features_bundle,
79 event_byname_future): 196 event_byname_future):
80 self._availability = availability_finder.GetAPIAvailability(namespace.name) 197 self._availability = availability_finder.GetAPIAvailability(namespace.name)
198 self._current_node = _APINodeCursor(availability_finder, namespace.name)
81 self._api_availabilities = json_cache.GetFromFile( 199 self._api_availabilities = json_cache.GetFromFile(
82 posixpath.join(JSON_TEMPLATES, 'api_availabilities.json')) 200 posixpath.join(JSON_TEMPLATES, 'api_availabilities.json'))
83 self._intro_tables = json_cache.GetFromFile( 201 self._intro_tables = json_cache.GetFromFile(
84 posixpath.join(JSON_TEMPLATES, 'intro_tables.json')) 202 posixpath.join(JSON_TEMPLATES, 'intro_tables.json'))
85 self._api_features = features_bundle.GetAPIFeatures() 203 self._api_features = features_bundle.GetAPIFeatures()
86 self._template_cache = template_cache 204 self._template_cache = template_cache
87 self._event_byname_future = event_byname_future 205 self._event_byname_future = event_byname_future
88 self._namespace = namespace 206 self._namespace = namespace
89 207
90 def _GetLink(self, link): 208 def _GetLink(self, link):
(...skipping 29 matching lines...) Expand all
120 if not self._IsExperimental(): 238 if not self._IsExperimental():
121 return { 239 return {
122 self._availability.channel_info.channel: True 240 self._availability.channel_info.channel: True
123 } 241 }
124 return None 242 return None
125 243
126 def _IsExperimental(self): 244 def _IsExperimental(self):
127 return self._namespace.name.startswith('experimental') 245 return self._namespace.name.startswith('experimental')
128 246
129 def _GenerateTypes(self, types): 247 def _GenerateTypes(self, types):
130 return [self._GenerateType(t) for t in types] 248 with self._current_node.Descend('types'):
not at google - send to devlin 2014/07/01 16:40:58 nice!
249 return [self._GenerateType(t) for t in types]
131 250
132 def _GenerateType(self, type_): 251 def _GenerateType(self, type_):
133 type_dict = { 252 with self._current_node.Descend(type_.simple_name):
134 'name': type_.simple_name, 253 type_dict = {
135 'description': type_.description, 254 'name': type_.simple_name,
136 'properties': self._GenerateProperties(type_.properties), 255 'description': type_.description,
137 'functions': self._GenerateFunctions(type_.functions), 256 'properties': self._GenerateProperties(type_.properties),
138 'events': self._GenerateEvents(type_.events), 257 'functions': self._GenerateFunctions(type_.functions),
139 'id': _CreateId(type_, 'type') 258 'events': self._GenerateEvents(type_.events),
140 } 259 'id': _CreateId(type_, 'type'),
141 self._RenderTypeInformation(type_, type_dict) 260 }
142 return type_dict 261 self._RenderTypeInformation(type_, type_dict)
262 return type_dict
143 263
144 def _GenerateFunctions(self, functions): 264 def _GenerateFunctions(self, functions):
145 return [self._GenerateFunction(f) for f in functions.values()] 265 with self._current_node.Descend('functions'):
266 return [self._GenerateFunction(f) for f in functions.values()]
146 267
147 def _GenerateFunction(self, function): 268 def _GenerateFunction(self, function):
148 function_dict = { 269 with self._current_node.Descend(function.simple_name):
149 'name': function.simple_name, 270 function_dict = {
150 'description': function.description, 271 'name': function.simple_name,
151 'callback': self._GenerateCallback(function.callback), 272 'description': function.description,
152 'parameters': [], 273 'callback': self._GenerateCallback(function.callback),
153 'returns': None, 274 'parameters': [],
154 'id': _CreateId(function, 'method') 275 'returns': None,
155 } 276 'id': _CreateId(function, 'method'),
156 self._AddCommonProperties(function_dict, function) 277 'availability': self._GetAvailabilityTemplate()
157 if function.returns: 278 }
158 function_dict['returns'] = self._GenerateType(function.returns) 279 self._AddCommonProperties(function_dict, function)
159 for param in function.params: 280 if function.returns:
160 function_dict['parameters'].append(self._GenerateProperty(param)) 281 function_dict['returns'] = self._GenerateType(function.returns)
161 if function.callback is not None: 282 for param in function.params:
162 # Show the callback as an extra parameter. 283 function_dict['parameters'].append(self._GenerateProperty(param))
163 function_dict['parameters'].append( 284 if function.callback is not None:
164 self._GenerateCallbackProperty(function.callback)) 285 # Show the callback as an extra parameter.
165 if len(function_dict['parameters']) > 0: 286 function_dict['parameters'].append(
166 function_dict['parameters'][-1]['last'] = True 287 self._GenerateCallbackProperty(function.callback))
167 return function_dict 288 if len(function_dict['parameters']) > 0:
289 function_dict['parameters'][-1]['last'] = True
290 return function_dict
168 291
169 def _GenerateEvents(self, events): 292 def _GenerateEvents(self, events):
170 return [self._GenerateEvent(e) for e in events.values() 293 with self._current_node.Descend('events'):
171 if not e.supports_dom] 294 return [self._GenerateEvent(e) for e in events.values()
295 if not e.supports_dom]
172 296
173 def _GenerateDomEvents(self, events): 297 def _GenerateDomEvents(self, events):
174 return [self._GenerateEvent(e) for e in events.values() 298 with self._current_node.Descend('events'):
175 if e.supports_dom] 299 return [self._GenerateEvent(e) for e in events.values()
300 if e.supports_dom]
176 301
177 def _GenerateEvent(self, event): 302 def _GenerateEvent(self, event):
178 event_dict = { 303 with self._current_node.Descend(event.simple_name):
179 'name': event.simple_name, 304 event_dict = {
180 'description': event.description, 305 'name': event.simple_name,
181 'filters': [self._GenerateProperty(f) for f in event.filters], 306 'description': event.description,
182 'conditions': [self._GetLink(condition) 307 'filters': [self._GenerateProperty(f) for f in event.filters],
183 for condition in event.conditions], 308 'conditions': [self._GetLink(condition)
184 'actions': [self._GetLink(action) for action in event.actions], 309 for condition in event.conditions],
185 'supportsRules': event.supports_rules, 310 'actions': [self._GetLink(action) for action in event.actions],
186 'supportsListeners': event.supports_listeners, 311 'supportsRules': event.supports_rules,
187 'properties': [], 312 'supportsListeners': event.supports_listeners,
188 'id': _CreateId(event, 'event'), 313 'properties': [],
189 'byName': {}, 314 'id': _CreateId(event, 'event'),
190 } 315 'byName': {},
191 self._AddCommonProperties(event_dict, event)
192 # Add the Event members to each event in this object.
193 if self._event_byname_future:
194 event_dict['byName'].update(self._event_byname_future.Get())
195 # We need to create the method description for addListener based on the
196 # information stored in |event|.
197 if event.supports_listeners:
198 callback_object = model.Function(parent=event,
199 name='callback',
200 json={},
201 namespace=event.parent,
202 origin='')
203 callback_object.params = event.params
204 if event.callback:
205 callback_object.callback = event.callback
206 callback_parameters = self._GenerateCallbackProperty(callback_object)
207 callback_parameters['last'] = True
208 event_dict['byName']['addListener'] = {
209 'name': 'addListener',
210 'callback': self._GenerateFunction(callback_object),
211 'parameters': [callback_parameters]
212 } 316 }
213 if event.supports_dom: 317 self._AddCommonProperties(event_dict, event)
214 # Treat params as properties of the custom Event object associated with 318 # Add the Event members to each event in this object.
215 # this DOM Event. 319 if self._event_byname_future:
216 event_dict['properties'] += [self._GenerateProperty(param) 320 event_dict['byName'].update(self._event_byname_future.Get())
217 for param in event.params] 321 # We need to create the method description for addListener based on the
218 return event_dict 322 # information stored in |event|.
323 if event.supports_listeners:
324 callback_object = model.Function(parent=event,
325 name='callback',
326 json={},
327 namespace=event.parent,
328 origin='')
329 callback_object.params = event.params
330 if event.callback:
331 callback_object.callback = event.callback
332 callback_parameters = self._GenerateCallbackProperty(callback_object)
333 callback_parameters['last'] = True
334 event_dict['byName']['addListener'] = {
335 'name': 'addListener',
336 'callback': self._GenerateFunction(callback_object),
337 'parameters': [callback_parameters]
338 }
339 if event.supports_dom:
340 # Treat params as properties of the custom Event object associated with
341 # this DOM Event.
342 event_dict['properties'] += [self._GenerateProperty(param)
343 for param in event.params]
344 return event_dict
219 345
220 def _GenerateCallback(self, callback): 346 def _GenerateCallback(self, callback):
221 if not callback: 347 if not callback:
222 return None 348 return None
223 callback_dict = { 349 callback_dict = {
224 'name': callback.simple_name, 350 'name': callback.simple_name,
225 'simple_type': {'simple_type': 'function'}, 351 'simple_type': {'simple_type': 'function'},
226 'optional': callback.optional, 352 'optional': callback.optional,
227 'parameters': [] 353 'parameters': []
228 } 354 }
(...skipping 21 matching lines...) Expand all
250 properties = type_.properties 376 properties = type_.properties
251 377
252 property_dict = { 378 property_dict = {
253 'name': property_.simple_name, 379 'name': property_.simple_name,
254 'optional': property_.optional, 380 'optional': property_.optional,
255 'description': property_.description, 381 'description': property_.description,
256 'properties': self._GenerateProperties(type_.properties), 382 'properties': self._GenerateProperties(type_.properties),
257 'functions': self._GenerateFunctions(type_.functions), 383 'functions': self._GenerateFunctions(type_.functions),
258 'parameters': [], 384 'parameters': [],
259 'returns': None, 385 'returns': None,
260 'id': _CreateId(property_, 'property') 386 'id': _CreateId(property_, 'property'),
261 } 387 }
262 self._AddCommonProperties(property_dict, property_) 388 self._AddCommonProperties(property_dict, property_)
263 389
264 if type_.property_type == model.PropertyType.FUNCTION: 390 if type_.property_type == model.PropertyType.FUNCTION:
265 function = type_.function 391 function = type_.function
266 for param in function.params: 392 for param in function.params:
267 property_dict['parameters'].append(self._GenerateProperty(param)) 393 property_dict['parameters'].append(self._GenerateProperty(param))
268 if function.returns: 394 if function.returns:
269 property_dict['returns'] = self._GenerateType(function.returns) 395 property_dict['returns'] = self._GenerateType(function.returns)
270 396
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
329 # if they share the same 'title' attribute. 455 # if they share the same 'title' attribute.
330 row_titles = [row['title'] for row in intro_rows] 456 row_titles = [row['title'] for row in intro_rows]
331 for misc_row in self._GetMiscIntroRows(): 457 for misc_row in self._GetMiscIntroRows():
332 if misc_row['title'] in row_titles: 458 if misc_row['title'] in row_titles:
333 intro_rows[row_titles.index(misc_row['title'])] = misc_row 459 intro_rows[row_titles.index(misc_row['title'])] = misc_row
334 else: 460 else:
335 intro_rows.append(misc_row) 461 intro_rows.append(misc_row)
336 462
337 return intro_rows 463 return intro_rows
338 464
465 def _GetAvailabilityTemplate(self, status=None, version=None, scheduled=None):
466 '''Returns an object that the templates use to display availability
467 information.
468 '''
469 if status is None:
470 availability_info = self._current_node.GetAvailability()
471 if availability_info is None:
472 return None
473 status = availability_info.channel
474 version = availability_info.version
475 return {
476 'partial': self._template_cache.GetFromFile(
not at google - send to devlin 2014/07/01 16:40:57 nit: indent -=2 on these
477 '%sintro_tables/%s_message.html' % (PRIVATE_TEMPLATES, status)).Get(),
not at google - send to devlin 2014/07/01 16:40:58 nit: indent += 2 (relative to the previous line) h
478 'version': version,
479 'scheduled': scheduled
not at google - send to devlin 2014/07/01 16:40:58 nit: I'm going to try making these objects alphabe
480 }
481
339 def _GetIntroDescriptionRow(self): 482 def _GetIntroDescriptionRow(self):
340 ''' Generates the 'Description' row data for an API intro table. 483 ''' Generates the 'Description' row data for an API intro table.
341 ''' 484 '''
342 return { 485 return {
343 'title': 'Description', 486 'title': 'Description',
344 'content': [ 487 'content': [
345 { 'text': self._namespace.description } 488 { 'text': self._namespace.description }
346 ] 489 ]
347 } 490 }
348 491
349 def _GetIntroAvailabilityRow(self): 492 def _GetIntroAvailabilityRow(self):
350 ''' Generates the 'Availability' row data for an API intro table. 493 ''' Generates the 'Availability' row data for an API intro table.
351 ''' 494 '''
352 if self._IsExperimental(): 495 if self._IsExperimental():
353 status = 'experimental' 496 status = 'experimental'
354 version = None 497 version = None
355 scheduled = None 498 scheduled = None
356 else: 499 else:
357 status = self._availability.channel_info.channel 500 status = self._availability.channel_info.channel
358 version = self._availability.channel_info.version 501 version = self._availability.channel_info.version
359 scheduled = self._availability.scheduled 502 scheduled = self._availability.scheduled
360 return { 503 return {
361 'title': 'Availability', 504 'title': 'Availability',
362 'content': [{ 505 'content': [
363 'partial': self._template_cache.GetFromFile( 506 self._GetAvailabilityTemplate(status=status,
364 posixpath.join(PRIVATE_TEMPLATES, 507 version=version,
365 'intro_tables', 508 scheduled=scheduled)
366 '%s_message.html' % status)).Get(), 509 ]
367 'version': version,
368 'scheduled': scheduled
369 }]
370 } 510 }
371 511
372 def _GetIntroDependencyRows(self): 512 def _GetIntroDependencyRows(self):
373 # Devtools aren't in _api_features. If we're dealing with devtools, bail. 513 # Devtools aren't in _api_features. If we're dealing with devtools, bail.
374 if 'devtools' in self._namespace.name: 514 if 'devtools' in self._namespace.name:
375 return [] 515 return []
376 516
377 api_feature = self._api_features.Get().get(self._namespace.name) 517 api_feature = self._api_features.Get().get(self._namespace.name)
378 if not api_feature: 518 if not api_feature:
379 logging.error('"%s" not found in _api_features.json' % 519 logging.error('"%s" not found in _api_features.json' %
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after
535 getter = lambda: 0 675 getter = lambda: 0
536 getter.get = lambda api_name: self._GetImpl(platform, api_name).Get() 676 getter.get = lambda api_name: self._GetImpl(platform, api_name).Get()
537 return getter 677 return getter
538 678
539 def Cron(self): 679 def Cron(self):
540 futures = [] 680 futures = []
541 for platform in GetPlatforms(): 681 for platform in GetPlatforms():
542 futures += [self._GetImpl(platform, name) 682 futures += [self._GetImpl(platform, name)
543 for name in self._platform_bundle.GetAPIModels(platform).GetNames()] 683 for name in self._platform_bundle.GetAPIModels(platform).GetNames()]
544 return Collect(futures, except_pass=FileNotFoundError) 684 return Collect(futures, except_pass=FileNotFoundError)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698