Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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) |
| OLD | NEW |