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

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

Issue 12996003: Dynamically generate a heading for Extension Docs API pages (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: AvailabilityFinder Overhaul; Removing ConfigureFakeFetchers() calls Created 7 years, 6 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 import collections
5 import copy 6 import copy
7 import json as json_library
6 import logging 8 import logging
7 import os 9 import os
8 from collections import defaultdict, Mapping 10 from collections import defaultdict, Mapping
9 11
12 import svn_constants
10 import third_party.json_schema_compiler.json_parse as json_parse 13 import third_party.json_schema_compiler.json_parse as json_parse
11 import third_party.json_schema_compiler.model as model 14 import third_party.json_schema_compiler.model as model
12 import third_party.json_schema_compiler.idl_schema as idl_schema 15 import third_party.json_schema_compiler.idl_schema as idl_schema
13 import third_party.json_schema_compiler.idl_parser as idl_parser 16 import third_party.json_schema_compiler.idl_parser as idl_parser
14 17
15 def _RemoveNoDocs(item): 18 def _RemoveNoDocs(item):
16 if json_parse.IsDict(item): 19 if json_parse.IsDict(item):
17 if item.get('nodoc', False): 20 if item.get('nodoc', False):
18 return True 21 return True
19 for key, value in item.items(): 22 for key, value in item.items():
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
96 def _FormatValue(value): 99 def _FormatValue(value):
97 """Inserts commas every three digits for integer values. It is magic. 100 """Inserts commas every three digits for integer values. It is magic.
98 """ 101 """
99 s = str(value) 102 s = str(value)
100 return ','.join([s[max(0, i - 3):i] for i in range(len(s), 0, -3)][::-1]) 103 return ','.join([s[max(0, i - 3):i] for i in range(len(s), 0, -3)][::-1])
101 104
102 class _JSCModel(object): 105 class _JSCModel(object):
103 """Uses a Model from the JSON Schema Compiler and generates a dict that 106 """Uses a Model from the JSON Schema Compiler and generates a dict that
104 a Handlebar template can use for a data source. 107 a Handlebar template can use for a data source.
105 """ 108 """
106 def __init__(self, json, ref_resolver, disable_refs, idl=False): 109 def __init__(self,
110 json,
111 ref_resolver,
112 disable_refs,
113 availability_finder,
114 intro_cache,
115 idl=False):
107 self._ref_resolver = ref_resolver 116 self._ref_resolver = ref_resolver
108 self._disable_refs = disable_refs 117 self._disable_refs = disable_refs
118 self._availability_finder = availability_finder
119 self._intro_tables = intro_cache.GetFromFile(
120 '%s/intro_tables.json' % svn_constants.JSON_PATH)
109 clean_json = copy.deepcopy(json) 121 clean_json = copy.deepcopy(json)
110 if _RemoveNoDocs(clean_json): 122 if _RemoveNoDocs(clean_json):
111 self._namespace = None 123 self._namespace = None
112 else: 124 else:
113 if idl: 125 if idl:
114 _DetectInlineableTypes(clean_json) 126 _DetectInlineableTypes(clean_json)
115 _InlineDocs(clean_json) 127 _InlineDocs(clean_json)
116 self._namespace = model.Namespace(clean_json, clean_json['namespace']) 128 self._namespace = model.Namespace(clean_json, clean_json['namespace'])
117 129
118 def _FormatDescription(self, description): 130 def _FormatDescription(self, description):
119 if self._disable_refs: 131 if self._disable_refs:
120 return description 132 return description
121 return self._ref_resolver.ResolveAllLinks(description, 133 return self._ref_resolver.ResolveAllLinks(description,
122 namespace=self._namespace.name) 134 namespace=self._namespace.name)
123 135
124 def _GetLink(self, link): 136 def _GetLink(self, link):
125 if self._disable_refs: 137 if self._disable_refs:
126 type_name = link.split('.', 1)[-1] 138 type_name = link.split('.', 1)[-1]
127 return { 'href': '#type-%s' % type_name, 'text': link, 'name': link } 139 return { 'href': '#type-%s' % type_name, 'text': link, 'name': link }
128 return self._ref_resolver.SafeGetLink(link, namespace=self._namespace.name) 140 return self._ref_resolver.SafeGetLink(link, namespace=self._namespace.name)
129 141
130 def ToDict(self): 142 def ToDict(self):
131 if self._namespace is None: 143 if self._namespace is None:
132 return {} 144 return {}
133 return { 145 return {
134 'name': self._namespace.name, 146 'name': self._namespace.name,
147 'description': self._namespace.description,
148 'availability': self._GetApiAvailability(),
149 'experimental': self._CheckExperimental(),
150 'api_permissions': self._GetPermissions(),
151 'learn_more': self._GetMoreLearning(),
135 'types': self._GenerateTypes(self._namespace.types.values()), 152 'types': self._GenerateTypes(self._namespace.types.values()),
136 'functions': self._GenerateFunctions(self._namespace.functions), 153 'functions': self._GenerateFunctions(self._namespace.functions),
137 'events': self._GenerateEvents(self._namespace.events), 154 'events': self._GenerateEvents(self._namespace.events),
138 'properties': self._GenerateProperties(self._namespace.properties) 155 'properties': self._GenerateProperties(self._namespace.properties)
139 } 156 }
140 157
158 def _GetApiAvailability(self):
159 """Finds the earliest stable version of chrome where an API was available
160 using AvailabilityFinder. If an API hasn't been released to stable yet,
161 then the development channel that the API is currently available on will
162 be returned instead.
163 """
164 if not self._CheckExperimental():
165 availability = self._availability_finder.GetApiAvailability(
166 self._namespace.name)
167 if availability:
168 if availability.channel == 'stable':
169 return availability.version
170 return availability.channel
171 # A special message for experimental APIs' availability will be displayed.
172 return None
173
174 def _CheckExperimental(self):
175 return self._namespace.name.startswith('experimental')
176
177 def _GetPermissions(self):
178 """This is a temporary fix for generating API intro tables automatically.
179 Information on an API's permissions is pulled from a json file so that it
180 can be sent to the templates along with other relevant intro data.
181 """
182 table_info = self._intro_tables.get(self._namespace.name)
183 return table_info.get('Permissions') if table_info else None
184
185 def _GetMoreLearning(self):
186 """We can't automatically generate the "Learn more" field in an API's.
187 intro table; instead, we pull it from a json file so that it can be sent to
188 the templates along with other relevant intro data.
189 """
190 table_info = self._intro_tables.get(self._namespace.name)
191 return table_info.get('LearnMore') if table_info else None
192
141 def _GenerateTypes(self, types): 193 def _GenerateTypes(self, types):
142 return [self._GenerateType(t) for t in types] 194 return [self._GenerateType(t) for t in types]
143 195
144 def _GenerateType(self, type_): 196 def _GenerateType(self, type_):
145 type_dict = { 197 type_dict = {
146 'name': type_.simple_name, 198 'name': type_.simple_name,
147 'description': self._FormatDescription(type_.description), 199 'description': self._FormatDescription(type_.description),
148 'properties': self._GenerateProperties(type_.properties), 200 'properties': self._GenerateProperties(type_.properties),
149 'functions': self._GenerateFunctions(type_.functions), 201 'functions': self._GenerateFunctions(type_.functions),
150 'events': self._GenerateEvents(type_.events), 202 'events': self._GenerateEvents(type_.events),
(...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after
317 self._samples = samples 369 self._samples = samples
318 370
319 def get(self, key): 371 def get(self, key):
320 return self._samples.FilterSamples(key, self._api_name) 372 return self._samples.FilterSamples(key, self._api_name)
321 373
322 class APIDataSource(object): 374 class APIDataSource(object):
323 """This class fetches and loads JSON APIs from the FileSystem passed in with 375 """This class fetches and loads JSON APIs from the FileSystem passed in with
324 |compiled_fs_factory|, so the APIs can be plugged into templates. 376 |compiled_fs_factory|, so the APIs can be plugged into templates.
325 """ 377 """
326 class Factory(object): 378 class Factory(object):
327 def __init__(self, compiled_fs_factory, base_path): 379 def __init__(self,
380 compiled_fs_factory,
381 base_path,
382 availability_finder_factory):
328 def create_compiled_fs(fn, category): 383 def create_compiled_fs(fn, category):
329 return compiled_fs_factory.Create(fn, APIDataSource, category=category) 384 return compiled_fs_factory.Create(fn, APIDataSource, category=category)
330 385
331 self._permissions_cache = create_compiled_fs(self._LoadPermissions, 386 self._permissions_cache = create_compiled_fs(self._LoadPermissions,
332 'permissions') 387 'permissions')
333 388
334 self._json_cache = create_compiled_fs( 389 self._json_cache = create_compiled_fs(
335 lambda api_name, api: self._LoadJsonAPI(api, False), 390 lambda api_name, api: self._LoadJsonAPI(api, False),
336 'json') 391 'json')
337 self._idl_cache = create_compiled_fs( 392 self._idl_cache = create_compiled_fs(
338 lambda api_name, api: self._LoadIdlAPI(api, False), 393 lambda api_name, api: self._LoadIdlAPI(api, False),
339 'idl') 394 'idl')
340 395
341 # These caches are used if an APIDataSource does not want to resolve the 396 # These caches are used if an APIDataSource does not want to resolve the
342 # $refs in an API. This is needed to prevent infinite recursion in 397 # $refs in an API. This is needed to prevent infinite recursion in
343 # ReferenceResolver. 398 # ReferenceResolver.
344 self._json_cache_no_refs = create_compiled_fs( 399 self._json_cache_no_refs = create_compiled_fs(
345 lambda api_name, api: self._LoadJsonAPI(api, True), 400 lambda api_name, api: self._LoadJsonAPI(api, True),
346 'json-no-refs') 401 'json-no-refs')
347 self._idl_cache_no_refs = create_compiled_fs( 402 self._idl_cache_no_refs = create_compiled_fs(
348 lambda api_name, api: self._LoadIdlAPI(api, True), 403 lambda api_name, api: self._LoadIdlAPI(api, True),
349 'idl-no-refs') 404 'idl-no-refs')
350 405
351 self._idl_names_cache = create_compiled_fs(self._GetIDLNames, 'idl-names') 406 self._idl_names_cache = create_compiled_fs(self._GetIDLNames, 'idl-names')
352 self._names_cache = create_compiled_fs(self._GetAllNames, 'names') 407 self._names_cache = create_compiled_fs(self._GetAllNames, 'names')
353 408
354 self._base_path = base_path 409 self._base_path = base_path
355 410 self._availability_finder = availability_finder_factory.Create()
411 def compiled_fs_parse(path, json):
412 return json_parse.Parse(json)
413 self._intro_cache = create_compiled_fs(compiled_fs_parse, 'intro-cache')
not at google - send to devlin 2013/06/05 00:24:09 ah right. yeah, using a lambda here would be bette
epeterson 2013/06/17 20:05:49 Done.
356 # These must be set later via the SetFooDataSourceFactory methods. 414 # These must be set later via the SetFooDataSourceFactory methods.
357 self._ref_resolver_factory = None 415 self._ref_resolver_factory = None
358 self._samples_data_source_factory = None 416 self._samples_data_source_factory = None
359 417
360 def SetSamplesDataSourceFactory(self, samples_data_source_factory): 418 def SetSamplesDataSourceFactory(self, samples_data_source_factory):
361 self._samples_data_source_factory = samples_data_source_factory 419 self._samples_data_source_factory = samples_data_source_factory
362 420
363 def SetReferenceResolverFactory(self, ref_resolver_factory): 421 def SetReferenceResolverFactory(self, ref_resolver_factory):
364 self._ref_resolver_factory = ref_resolver_factory 422 self._ref_resolver_factory = ref_resolver_factory
365 423
(...skipping 25 matching lines...) Expand all
391 samples, 449 samples,
392 disable_refs) 450 disable_refs)
393 451
394 def _LoadPermissions(self, file_name, json_str): 452 def _LoadPermissions(self, file_name, json_str):
395 return json_parse.Parse(json_str) 453 return json_parse.Parse(json_str)
396 454
397 def _LoadJsonAPI(self, api, disable_refs): 455 def _LoadJsonAPI(self, api, disable_refs):
398 return _JSCModel( 456 return _JSCModel(
399 json_parse.Parse(api)[0], 457 json_parse.Parse(api)[0],
400 self._ref_resolver_factory.Create() if not disable_refs else None, 458 self._ref_resolver_factory.Create() if not disable_refs else None,
401 disable_refs).ToDict() 459 disable_refs,
460 self._availability_finder,
461 self._intro_cache).ToDict()
402 462
403 def _LoadIdlAPI(self, api, disable_refs): 463 def _LoadIdlAPI(self, api, disable_refs):
404 idl = idl_parser.IDLParser().ParseData(api) 464 idl = idl_parser.IDLParser().ParseData(api)
405 return _JSCModel( 465 return _JSCModel(
406 idl_schema.IDLSchema(idl).process()[0], 466 idl_schema.IDLSchema(idl).process()[0],
407 self._ref_resolver_factory.Create() if not disable_refs else None, 467 self._ref_resolver_factory.Create() if not disable_refs else None,
408 disable_refs, 468 disable_refs,
469 self._availability_finder,
470 self._intro_cache,
409 idl=True).ToDict() 471 idl=True).ToDict()
410 472
411 def _GetIDLNames(self, base_dir, apis): 473 def _GetIDLNames(self, base_dir, apis):
412 return self._GetExtNames(apis, ['idl']) 474 return self._GetExtNames(apis, ['idl'])
413 475
414 def _GetAllNames(self, base_dir, apis): 476 def _GetAllNames(self, base_dir, apis):
415 return self._GetExtNames(apis, ['json', 'idl']) 477 return self._GetExtNames(apis, ['json', 'idl'])
416 478
417 def _GetExtNames(self, apis, exts): 479 def _GetExtNames(self, apis, exts):
418 return [model.UnixName(os.path.splitext(api)[0]) for api in apis 480 return [model.UnixName(os.path.splitext(api)[0]) for api in apis
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
476 return feature_data 538 return feature_data
477 539
478 def _GenerateHandlebarContext(self, handlebar_dict, path): 540 def _GenerateHandlebarContext(self, handlebar_dict, path):
479 handlebar_dict['permissions'] = self._GetFeatureData(path) 541 handlebar_dict['permissions'] = self._GetFeatureData(path)
480 handlebar_dict['samples'] = _LazySamplesGetter(path, self._samples) 542 handlebar_dict['samples'] = _LazySamplesGetter(path, self._samples)
481 return handlebar_dict 543 return handlebar_dict
482 544
483 def _GetAsSubdirectory(self, name): 545 def _GetAsSubdirectory(self, name):
484 if name.startswith('experimental_'): 546 if name.startswith('experimental_'):
485 parts = name[len('experimental_'):].split('_', 1) 547 parts = name[len('experimental_'):].split('_', 1)
486 parts[1] = 'experimental_%s' % parts[1] 548 if len(parts) > 1:
487 return '/'.join(parts) 549 parts[1] = 'experimental_%s' % parts[1]
550 return '/'.join(parts)
551 else:
not at google - send to devlin 2013/06/05 00:24:09 no else after a return statement
epeterson 2013/06/17 20:05:49 Done.
552 return '%s/%s' % (parts[0], name)
488 return name.replace('_', '/', 1) 553 return name.replace('_', '/', 1)
489 554
490 def get(self, key): 555 def _DetermineNamesForGet(self, key):
491 if key.endswith('.html') or key.endswith('.json') or key.endswith('.idl'): 556 if key.endswith('.html') or key.endswith('.json') or key.endswith('.idl'):
492 path, ext = os.path.splitext(key) 557 path, ext = os.path.splitext(key)
493 else: 558 else:
494 path = key 559 path = key
495 unix_name = model.UnixName(path) 560 unix_name = model.UnixName(path)
496 idl_names = self._idl_names_cache.GetFromFileListing(self._base_path)
497 names = self._names_cache.GetFromFileListing(self._base_path) 561 names = self._names_cache.GetFromFileListing(self._base_path)
498 if unix_name not in names and self._GetAsSubdirectory(unix_name) in names: 562 if unix_name not in names and self._GetAsSubdirectory(unix_name) in names:
499 unix_name = self._GetAsSubdirectory(unix_name) 563 unix_name = self._GetAsSubdirectory(unix_name)
564 return (path, unix_name)
500 565
566 def _DetermineCacheForGet(self, unix_name):
567 idl_names = self._idl_names_cache.GetFromFileListing(self._base_path)
501 if self._disable_refs: 568 if self._disable_refs:
502 cache, ext = ( 569 cache, ext = (
503 (self._idl_cache_no_refs, '.idl') if (unix_name in idl_names) else 570 (self._idl_cache_no_refs, '.idl') if (unix_name in idl_names) else
504 (self._json_cache_no_refs, '.json')) 571 (self._json_cache_no_refs, '.json'))
505 else: 572 else:
506 cache, ext = ((self._idl_cache, '.idl') if (unix_name in idl_names) else 573 cache, ext = ((self._idl_cache, '.idl') if (unix_name in idl_names) else
507 (self._json_cache, '.json')) 574 (self._json_cache, '.json'))
575 return (cache, ext)
576
577 def get(self, key):
578 path, unix_name = self._DetermineNamesForGet(key)
579 cache, ext = self._DetermineCacheForGet(unix_name)
not at google - send to devlin 2013/06/05 00:24:09 any particular reason why you split this out?
epeterson 2013/06/17 20:05:49 When I was using that old "get2" method here, the
508 return self._GenerateHandlebarContext( 580 return self._GenerateHandlebarContext(
509 cache.GetFromFile('%s/%s%s' % (self._base_path, unix_name, ext)), 581 cache.GetFromFile('%s/%s%s' % (self._base_path, unix_name, ext)),
510 path) 582 path)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698