OLD | NEW |
| (Empty) |
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. | |
4 | |
5 from collections import defaultdict, Mapping | |
6 import traceback | |
7 | |
8 from third_party.json_schema_compiler import json_parse, idl_schema, idl_parser | |
9 | |
10 | |
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 | |
28 | |
29 | |
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 | |
36 | |
37 ignore = frozenset(('value', 'choices')) | |
38 refcounts = defaultdict(int) | |
39 # Use an explicit stack instead of recursion. | |
40 stack = [schema] | |
41 | |
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) | |
50 | |
51 for type_ in schema['types']: | |
52 if not 'noinline_doc' in type_: | |
53 if refcounts[type_['id']] == 1: | |
54 type_['inline_doc'] = True | |
55 | |
56 | |
57 def InlineDocs(schema, retain_inlined_types): | |
58 '''Replace '$ref's that refer to inline_docs with the json for those docs. | |
59 If |retain_inlined_types| is False, then the inlined nodes are removed | |
60 from the schema. | |
61 ''' | |
62 types = schema.get('types') | |
63 if types is None: | |
64 return | |
65 | |
66 inline_docs = {} | |
67 types_without_inline_doc = [] | |
68 | |
69 # Gather the types with inline_doc. | |
70 for type_ in types: | |
71 if type_.get('inline_doc'): | |
72 inline_docs[type_['id']] = type_ | |
73 if not retain_inlined_types: | |
74 for k in ('description', 'id', 'inline_doc'): | |
75 type_.pop(k, None) | |
76 else: | |
77 types_without_inline_doc.append(type_) | |
78 if not retain_inlined_types: | |
79 schema['types'] = types_without_inline_doc | |
80 | |
81 def apply_inline(node): | |
82 if isinstance(node, list): | |
83 for i in node: | |
84 apply_inline(i) | |
85 elif isinstance(node, Mapping): | |
86 ref = node.get('$ref') | |
87 if ref and ref in inline_docs: | |
88 node.update(inline_docs[ref]) | |
89 del node['$ref'] | |
90 for k, v in node.iteritems(): | |
91 apply_inline(v) | |
92 | |
93 apply_inline(schema) | |
94 | |
95 | |
96 def ProcessSchema(path, file_data, retain_inlined_types=False): | |
97 '''Parses |file_data| using a method determined by checking the | |
98 extension of the file at the given |path|. Then, trims 'nodoc' and if | |
99 |retain_inlined_types| is given and False, removes inlineable types from | |
100 the parsed schema data. | |
101 ''' | |
102 def trim_and_inline(schema, is_idl=False): | |
103 '''Modifies an API schema in place by removing nodes that shouldn't be | |
104 documented and inlining schema types that are only referenced once. | |
105 ''' | |
106 if RemoveNoDocs(schema): | |
107 # A return of True signifies that the entire schema should not be | |
108 # documented. Otherwise, only nodes that request 'nodoc' are removed. | |
109 return None | |
110 if is_idl: | |
111 DetectInlineableTypes(schema) | |
112 InlineDocs(schema, retain_inlined_types) | |
113 return schema | |
114 | |
115 if path.endswith('.idl'): | |
116 idl = idl_schema.IDLSchema(idl_parser.IDLParser().ParseData(file_data)) | |
117 # Wrap the result in a list so that it behaves like JSON API data. | |
118 return [trim_and_inline(idl.process()[0], is_idl=True)] | |
119 | |
120 try: | |
121 schemas = json_parse.Parse(file_data) | |
122 except: | |
123 raise ValueError('Cannot parse "%s" as JSON:\n%s' % | |
124 (path, traceback.format_exc())) | |
125 for schema in schemas: | |
126 # Schemas could consist of one API schema (data for a specific API file) | |
127 # or multiple (data from extension_api.json). | |
128 trim_and_inline(schema) | |
129 return schemas | |
OLD | NEW |