Suppress tabs permission warning if there is already a browsingHistory warning.
[chromium-blink-merge.git] / chrome / common / extensions / docs / server2 / schema_util.py
blobdba7c7c7e8f01cf7abcc1c7a7953c935bedcdc61
1 # Copyright 2013 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
5 from collections import defaultdict, Mapping
6 import traceback
8 from third_party.json_schema_compiler import json_parse, idl_schema, idl_parser
11 def RemoveNoDocs(item):
12 '''Removes nodes that should not be rendered from an API schema.
13 '''
14 if json_parse.IsDict(item):
15 if item.get('nodoc', False):
16 return True
17 for key, value in item.items():
18 if RemoveNoDocs(value):
19 del item[key]
20 elif type(item) == list:
21 to_remove = []
22 for i in item:
23 if RemoveNoDocs(i):
24 to_remove.append(i)
25 for i in to_remove:
26 item.remove(i)
27 return False
30 def DetectInlineableTypes(schema):
31 '''Look for documents that are only referenced once and mark them as inline.
32 Actual inlining is done by _InlineDocs.
33 '''
34 if not schema.get('types'):
35 return
37 ignore = frozenset(('value', 'choices'))
38 refcounts = defaultdict(int)
39 # Use an explicit stack instead of recursion.
40 stack = [schema]
42 while stack:
43 node = stack.pop()
44 if isinstance(node, list):
45 stack.extend(node)
46 elif isinstance(node, Mapping):
47 if '$ref' in node:
48 refcounts[node['$ref']] += 1
49 stack.extend(v for k, v in node.iteritems() if k not in ignore)
51 for type_ in schema['types']:
52 if not 'noinline_doc' in type_:
53 if refcounts[type_['id']] == 1:
54 type_['inline_doc'] = True
57 def InlineDocs(schema):
58 '''Replace '$ref's that refer to inline_docs with the json for those docs.
59 '''
60 types = schema.get('types')
61 if types is None:
62 return
64 inline_docs = {}
65 types_without_inline_doc = []
67 # Gather the types with inline_doc.
68 for type_ in types:
69 if type_.get('inline_doc'):
70 inline_docs[type_['id']] = type_
71 for k in ('description', 'id', 'inline_doc'):
72 type_.pop(k, None)
73 else:
74 types_without_inline_doc.append(type_)
75 schema['types'] = types_without_inline_doc
77 def apply_inline(node):
78 if isinstance(node, list):
79 for i in node:
80 apply_inline(i)
81 elif isinstance(node, Mapping):
82 ref = node.get('$ref')
83 if ref and ref in inline_docs:
84 node.update(inline_docs[ref])
85 del node['$ref']
86 for k, v in node.iteritems():
87 apply_inline(v)
89 apply_inline(schema)
92 def ProcessSchema(path, file_data):
93 '''Parses |file_data| using a method determined by checking the
94 extension of the file at the given |path|. Then, trims 'nodoc' and handles
95 inlineable types from the parsed schema data.
96 '''
97 def trim_and_inline(schema, is_idl=False):
98 '''Modifies an API schema in place by removing nodes that shouldn't be
99 documented and inlining schema types that are only referenced once.
101 if RemoveNoDocs(schema):
102 # A return of True signifies that the entire schema should not be
103 # documented. Otherwise, only nodes that request 'nodoc' are removed.
104 return None
105 if is_idl:
106 DetectInlineableTypes(schema)
107 InlineDocs(schema)
108 return schema
110 if path.endswith('.idl'):
111 idl = idl_schema.IDLSchema(idl_parser.IDLParser().ParseData(file_data))
112 # Wrap the result in a list so that it behaves like JSON API data.
113 return [trim_and_inline(idl.process()[0], is_idl=True)]
115 try:
116 schemas = json_parse.Parse(file_data)
117 except:
118 raise ValueError('Cannot parse "%s" as JSON:\n%s' %
119 (path, traceback.format_exc()))
120 for schema in schemas:
121 # Schemas could consist of one API schema (data for a specific API file)
122 # or multiple (data from extension_api.json).
123 trim_and_inline(schema)
124 return schemas