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

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: Fixing up Offline/Online Access - Attempting to rework availability algorithm Created 7 years, 7 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 import collections
9 10
11 import svn_constants
10 import third_party.json_schema_compiler.json_parse as json_parse 12 import third_party.json_schema_compiler.json_parse as json_parse
11 import third_party.json_schema_compiler.model as model 13 import third_party.json_schema_compiler.model as model
12 import third_party.json_schema_compiler.idl_schema as idl_schema 14 import third_party.json_schema_compiler.idl_schema as idl_schema
13 import third_party.json_schema_compiler.idl_parser as idl_parser 15 import third_party.json_schema_compiler.idl_parser as idl_parser
14 16
15 def _RemoveNoDocs(item): 17 def _RemoveNoDocs(item):
16 if json_parse.IsDict(item): 18 if json_parse.IsDict(item):
17 if item.get('nodoc', False): 19 if item.get('nodoc', False):
18 return True 20 return True
19 for key, value in item.items(): 21 for key, value in item.items():
20 if _RemoveNoDocs(value): 22 if _RemoveNoDocs(value):
21 del item[key] 23 del item[key]
22 elif type(item) == list: 24 elif type(item) == list:
23 to_remove = [] 25 to_remove = []
24 for i in item: 26 for i in item:
25 if _RemoveNoDocs(i): 27 if _RemoveNoDocs(i):
26 to_remove.append(i) 28 to_remove.append(i)
27 for i in to_remove: 29 for i in to_remove:
28 item.remove(i) 30 item.remove(i)
29 return False 31 return False
30 32
31
32 def _InlineDocs(schema): 33 def _InlineDocs(schema):
33 """Replace '$ref's that refer to inline_docs with the json for those docs. 34 """Replace '$ref's that refer to inline_docs with the json for those docs.
34 """ 35 """
35 types = schema.get('types') 36 types = schema.get('types')
36 if types is None: 37 if types is None:
37 return 38 return
38 39
39 inline_docs = {} 40 inline_docs = {}
40 types_without_inline_doc = [] 41 types_without_inline_doc = []
41 42
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
73 def _FormatValue(value): 74 def _FormatValue(value):
74 """Inserts commas every three digits for integer values. It is magic. 75 """Inserts commas every three digits for integer values. It is magic.
75 """ 76 """
76 s = str(value) 77 s = str(value)
77 return ','.join([s[max(0, i - 3):i] for i in range(len(s), 0, -3)][::-1]) 78 return ','.join([s[max(0, i - 3):i] for i in range(len(s), 0, -3)][::-1])
78 79
79 class _JSCModel(object): 80 class _JSCModel(object):
80 """Uses a Model from the JSON Schema Compiler and generates a dict that 81 """Uses a Model from the JSON Schema Compiler and generates a dict that
81 a Handlebar template can use for a data source. 82 a Handlebar template can use for a data source.
82 """ 83 """
83 def __init__(self, json, ref_resolver, disable_refs): 84 def __init__(self,
85 json,
86 ref_resolver,
87 disable_refs,
88 availability_finder,
89 intro_cache):
84 self._ref_resolver = ref_resolver 90 self._ref_resolver = ref_resolver
85 self._disable_refs = disable_refs 91 self._disable_refs = disable_refs
92 self._availability_finder = availability_finder
93 self._intro_tables = intro_cache.GetFromFile(
94 '%s/intro_tables.json' % svn_constants.JSON_PATH)
86 clean_json = copy.deepcopy(json) 95 clean_json = copy.deepcopy(json)
87 if _RemoveNoDocs(clean_json): 96 if _RemoveNoDocs(clean_json):
88 self._namespace = None 97 self._namespace = None
89 else: 98 else:
90 _InlineDocs(clean_json) 99 _InlineDocs(clean_json)
91 self._namespace = model.Namespace(clean_json, clean_json['namespace']) 100 self._namespace = model.Namespace(clean_json, clean_json['namespace'])
92 101
93 def _FormatDescription(self, description): 102 def _FormatDescription(self, description):
94 if self._disable_refs: 103 if self._disable_refs:
95 return description 104 return description
96 return self._ref_resolver.ResolveAllLinks(description, 105 return self._ref_resolver.ResolveAllLinks(description,
97 namespace=self._namespace.name) 106 namespace=self._namespace.name)
98 107
99 def _GetLink(self, link): 108 def _GetLink(self, link):
100 if self._disable_refs: 109 if self._disable_refs:
101 type_name = link.split('.', 1)[-1] 110 type_name = link.split('.', 1)[-1]
102 return { 'href': '#type-%s' % type_name, 'text': link, 'name': link } 111 return { 'href': '#type-%s' % type_name, 'text': link, 'name': link }
103 return self._ref_resolver.SafeGetLink(link, namespace=self._namespace.name) 112 return self._ref_resolver.SafeGetLink(link, namespace=self._namespace.name)
104 113
105 def ToDict(self): 114 def ToDict(self):
106 if self._namespace is None: 115 if self._namespace is None:
107 return {} 116 return {}
108 return { 117 return {
109 'name': self._namespace.name, 118 'name': self._namespace.name,
119 'description': self._namespace.description,
120 'availability': self._GetApiAvailability(),
121 'experimental': self._CheckExperimental(),
122 'api_permissions': self._GetPermissions(),
123 'learn_more': self._GetMoreLearning(),
110 'types': self._GenerateTypes(self._namespace.types.values()), 124 'types': self._GenerateTypes(self._namespace.types.values()),
111 'functions': self._GenerateFunctions(self._namespace.functions), 125 'functions': self._GenerateFunctions(self._namespace.functions),
112 'events': self._GenerateEvents(self._namespace.events), 126 'events': self._GenerateEvents(self._namespace.events),
113 'properties': self._GenerateProperties(self._namespace.properties) 127 'properties': self._GenerateProperties(self._namespace.properties)
114 } 128 }
115 129
130 def _GetApiAvailability(self):
131 """Finds the earliest stable version of chrome where an API was available
132 using AvailabilityFinder. If an API hasn't been released to stable yet,
133 then the development channel that the API is currently available on will
134 be returned instead.
135 """
136 if not self._CheckExperimental():
137 availability = self._availability_finder.GetApiAvailability(
138 self._namespace.name)
139 if availability:
140 return availability.version
141 # A special message for experimental APIs' availability will be displayed.
142 return None
143
144 def _CheckExperimental(self):
145 return self._namespace.name.startswith('experimental')
146
147 def _GetPermissions(self):
148 """This is a temporary fix for generating API intro tables automatically.
149 Information on an API's permissions is pulled from a json file so that it
150 can be sent to the templates along with other relevant intro data.
151 """
152 table_info = self._intro_tables.get(self._namespace.name)
153 return table_info.get('Permissions') if table_info else None
154
155 def _GetMoreLearning(self):
156 """We can't automatically generate the "Learn more" field in an API's.
157 intro table; instead, we pull it from a json file so that it can be sent to
158 the templates along with other relevant intro data.
159 """
160 table_info = self._intro_tables.get(self._namespace.name)
161 return table_info.get('LearnMore') if table_info else None
162
116 def _GenerateTypes(self, types): 163 def _GenerateTypes(self, types):
117 return [self._GenerateType(t) for t in types] 164 return [self._GenerateType(t) for t in types]
118 165
119 def _GenerateType(self, type_): 166 def _GenerateType(self, type_):
120 type_dict = { 167 type_dict = {
121 'name': type_.simple_name, 168 'name': type_.simple_name,
122 'description': self._FormatDescription(type_.description), 169 'description': self._FormatDescription(type_.description),
123 'properties': self._GenerateProperties(type_.properties), 170 'properties': self._GenerateProperties(type_.properties),
124 'functions': self._GenerateFunctions(type_.functions), 171 'functions': self._GenerateFunctions(type_.functions),
125 'events': self._GenerateEvents(type_.events), 172 'events': self._GenerateEvents(type_.events),
(...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after
292 self._samples = samples 339 self._samples = samples
293 340
294 def get(self, key): 341 def get(self, key):
295 return self._samples.FilterSamples(key, self._api_name) 342 return self._samples.FilterSamples(key, self._api_name)
296 343
297 class APIDataSource(object): 344 class APIDataSource(object):
298 """This class fetches and loads JSON APIs from the FileSystem passed in with 345 """This class fetches and loads JSON APIs from the FileSystem passed in with
299 |compiled_fs_factory|, so the APIs can be plugged into templates. 346 |compiled_fs_factory|, so the APIs can be plugged into templates.
300 """ 347 """
301 class Factory(object): 348 class Factory(object):
302 def __init__(self, compiled_fs_factory, base_path): 349 def __init__(self,
350 compiled_fs_factory,
351 base_path,
352 availability_finder_factory):
303 def create_compiled_fs(fn, category): 353 def create_compiled_fs(fn, category):
304 return compiled_fs_factory.Create(fn, APIDataSource, category=category) 354 return compiled_fs_factory.Create(fn, APIDataSource, category=category)
305 355
306 self._permissions_cache = create_compiled_fs(self._LoadPermissions, 356 self._permissions_cache = create_compiled_fs(self._LoadPermissions,
307 'permissions') 357 'permissions')
308 358
309 self._json_cache = create_compiled_fs( 359 self._json_cache = create_compiled_fs(
310 lambda api_name, api: self._LoadJsonAPI(api, False), 360 lambda api_name, api: self._LoadJsonAPI(api, False),
311 'json') 361 'json')
312 self._idl_cache = create_compiled_fs( 362 self._idl_cache = create_compiled_fs(
313 lambda api_name, api: self._LoadIdlAPI(api, False), 363 lambda api_name, api: self._LoadIdlAPI(api, False),
314 'idl') 364 'idl')
315 365
316 # These caches are used if an APIDataSource does not want to resolve the 366 # These caches are used if an APIDataSource does not want to resolve the
317 # $refs in an API. This is needed to prevent infinite recursion in 367 # $refs in an API. This is needed to prevent infinite recursion in
318 # ReferenceResolver. 368 # ReferenceResolver.
319 self._json_cache_no_refs = create_compiled_fs( 369 self._json_cache_no_refs = create_compiled_fs(
320 lambda api_name, api: self._LoadJsonAPI(api, True), 370 lambda api_name, api: self._LoadJsonAPI(api, True),
321 'json-no-refs') 371 'json-no-refs')
322 self._idl_cache_no_refs = create_compiled_fs( 372 self._idl_cache_no_refs = create_compiled_fs(
323 lambda api_name, api: self._LoadIdlAPI(api, True), 373 lambda api_name, api: self._LoadIdlAPI(api, True),
324 'idl-no-refs') 374 'idl-no-refs')
325 375
326 self._idl_names_cache = create_compiled_fs(self._GetIDLNames, 'idl-names') 376 self._idl_names_cache = create_compiled_fs(self._GetIDLNames, 'idl-names')
327 self._names_cache = create_compiled_fs(self._GetAllNames, 'names') 377 self._names_cache = create_compiled_fs(self._GetAllNames, 'names')
328 378
329 self._base_path = base_path 379 self._base_path = base_path
330 380 self._availability_finder = availability_finder_factory.Create()
381 def compiled_fs_parse(path, json):
382 return json_parse.Parse(json)
383 self._intro_cache = create_compiled_fs(compiled_fs_parse, 'intro-cache')
331 # These must be set later via the SetFooDataSourceFactory methods. 384 # These must be set later via the SetFooDataSourceFactory methods.
332 self._ref_resolver_factory = None 385 self._ref_resolver_factory = None
333 self._samples_data_source_factory = None 386 self._samples_data_source_factory = None
334 387
335 def SetSamplesDataSourceFactory(self, samples_data_source_factory): 388 def SetSamplesDataSourceFactory(self, samples_data_source_factory):
336 self._samples_data_source_factory = samples_data_source_factory 389 self._samples_data_source_factory = samples_data_source_factory
337 390
338 def SetReferenceResolverFactory(self, ref_resolver_factory): 391 def SetReferenceResolverFactory(self, ref_resolver_factory):
339 self._ref_resolver_factory = ref_resolver_factory 392 self._ref_resolver_factory = ref_resolver_factory
340 393
(...skipping 25 matching lines...) Expand all
366 samples, 419 samples,
367 disable_refs) 420 disable_refs)
368 421
369 def _LoadPermissions(self, file_name, json_str): 422 def _LoadPermissions(self, file_name, json_str):
370 return json_parse.Parse(json_str) 423 return json_parse.Parse(json_str)
371 424
372 def _LoadJsonAPI(self, api, disable_refs): 425 def _LoadJsonAPI(self, api, disable_refs):
373 return _JSCModel( 426 return _JSCModel(
374 json_parse.Parse(api)[0], 427 json_parse.Parse(api)[0],
375 self._ref_resolver_factory.Create() if not disable_refs else None, 428 self._ref_resolver_factory.Create() if not disable_refs else None,
376 disable_refs).ToDict() 429 disable_refs,
430 self._availability_finder,
431 self._intro_cache).ToDict()
377 432
378 def _LoadIdlAPI(self, api, disable_refs): 433 def _LoadIdlAPI(self, api, disable_refs):
379 idl = idl_parser.IDLParser().ParseData(api) 434 idl = idl_parser.IDLParser().ParseData(api)
380 return _JSCModel( 435 return _JSCModel(
381 idl_schema.IDLSchema(idl).process()[0], 436 idl_schema.IDLSchema(idl).process()[0],
382 self._ref_resolver_factory.Create() if not disable_refs else None, 437 self._ref_resolver_factory.Create() if not disable_refs else None,
383 disable_refs).ToDict() 438 disable_refs,
439 self._availability_finder,
440 self._intro_cache).ToDict()
384 441
385 def _GetIDLNames(self, base_dir, apis): 442 def _GetIDLNames(self, base_dir, apis):
386 return self._GetExtNames(apis, ['idl']) 443 return self._GetExtNames(apis, ['idl'])
387 444
388 def _GetAllNames(self, base_dir, apis): 445 def _GetAllNames(self, base_dir, apis):
389 return self._GetExtNames(apis, ['json', 'idl']) 446 return self._GetExtNames(apis, ['json', 'idl'])
390 447
391 def _GetExtNames(self, apis, exts): 448 def _GetExtNames(self, apis, exts):
392 return [model.UnixName(os.path.splitext(api)[0]) for api in apis 449 return [model.UnixName(os.path.splitext(api)[0]) for api in apis
393 if os.path.splitext(api)[1][1:] in exts] 450 if os.path.splitext(api)[1][1:] in exts]
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
450 return feature_data 507 return feature_data
451 508
452 def _GenerateHandlebarContext(self, handlebar_dict, path): 509 def _GenerateHandlebarContext(self, handlebar_dict, path):
453 handlebar_dict['permissions'] = self._GetFeatureData(path) 510 handlebar_dict['permissions'] = self._GetFeatureData(path)
454 handlebar_dict['samples'] = _LazySamplesGetter(path, self._samples) 511 handlebar_dict['samples'] = _LazySamplesGetter(path, self._samples)
455 return handlebar_dict 512 return handlebar_dict
456 513
457 def _GetAsSubdirectory(self, name): 514 def _GetAsSubdirectory(self, name):
458 if name.startswith('experimental_'): 515 if name.startswith('experimental_'):
459 parts = name[len('experimental_'):].split('_', 1) 516 parts = name[len('experimental_'):].split('_', 1)
460 parts[1] = 'experimental_%s' % parts[1] 517 if len(parts) > 1:
461 return '/'.join(parts) 518 parts[1] = 'experimental_%s' % parts[1]
519 return '/'.join(parts)
520 else:
521 return '%s/%s' % (parts[0], name)
462 return name.replace('_', '/', 1) 522 return name.replace('_', '/', 1)
463 523
464 def get(self, key): 524 def _DetermineNamesForGet(self, key):
465 if key.endswith('.html') or key.endswith('.json') or key.endswith('.idl'): 525 if key.endswith('.html') or key.endswith('.json') or key.endswith('.idl'):
466 path, ext = os.path.splitext(key) 526 path, ext = os.path.splitext(key)
467 else: 527 else:
468 path = key 528 path = key
469 unix_name = model.UnixName(path) 529 unix_name = model.UnixName(path)
470 idl_names = self._idl_names_cache.GetFromFileListing(self._base_path)
471 names = self._names_cache.GetFromFileListing(self._base_path) 530 names = self._names_cache.GetFromFileListing(self._base_path)
472 if unix_name not in names and self._GetAsSubdirectory(unix_name) in names: 531 if unix_name not in names and self._GetAsSubdirectory(unix_name) in names:
473 unix_name = self._GetAsSubdirectory(unix_name) 532 unix_name = self._GetAsSubdirectory(unix_name)
533 return (path, unix_name)
474 534
535 def _DetermineCacheForGet(self, unix_name):
536 idl_names = self._idl_names_cache.GetFromFileListing(self._base_path)
475 if self._disable_refs: 537 if self._disable_refs:
476 cache, ext = ( 538 cache, ext = (
477 (self._idl_cache_no_refs, '.idl') if (unix_name in idl_names) else 539 (self._idl_cache_no_refs, '.idl') if (unix_name in idl_names) else
478 (self._json_cache_no_refs, '.json')) 540 (self._json_cache_no_refs, '.json'))
479 else: 541 else:
480 cache, ext = ((self._idl_cache, '.idl') if (unix_name in idl_names) else 542 cache, ext = ((self._idl_cache, '.idl') if (unix_name in idl_names) else
481 (self._json_cache, '.json')) 543 (self._json_cache, '.json'))
544 return (cache, ext)
545
546 def get(self, key):
547 path, unix_name = self._DetermineNamesForGet(key)
548 cache, ext = self._DetermineCacheForGet(unix_name)
482 return self._GenerateHandlebarContext( 549 return self._GenerateHandlebarContext(
483 cache.GetFromFile('%s/%s%s' % (self._base_path, unix_name, ext)), 550 cache.GetFromFile('%s/%s%s' % (self._base_path, unix_name, ext)),
484 path) 551 path)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698