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

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

Issue 16410002: Docserver manifest follow up (rewrite) (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@gen-manifest-try-2
Patch Set: manifest follow up (rewrite) Created 7 years, 5 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 2013 The Chromium Authors. All rights reserved. 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 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 collections import OrderedDict
6 from copy import deepcopy 5 from copy import deepcopy
7 from operator import itemgetter
8 6
7 from features_utility import MergeDictionaries, ProcessFeaturesFile
9 from third_party.json_schema_compiler.json_parse import Parse 8 from third_party.json_schema_compiler.json_parse import Parse
10 9
10 def _InsertSubDocs(properties):
11 '''Properties that contain a '.' (period) should be inserted into a parent
12 dictionary. Ex, background.persistent should be a subdocument of
13 background. Mutates |properties|.
14 '''
15 def add_subdoc(propreties, parent, subname, value):
16 value['name'] = subname
17 if not 'subdocs' in properties[parent]:
18 properties[parent]['subdocs'] = {}
19 properties[parent]['subdocs'][subname] = value
20
21 for name in properties.keys():
22 if '.' in name:
23 value = properties.pop(name)
24 parent, subname = name.split('.', 1)
25 add_subdoc(properties, parent, subname, value)
26
27 for value in properties.values():
28 if 'subdocs' in value:
29 _InsertSubDocs(value['subdocs'])
30
31 def _Filter(properties, platform):
32 '''Recursively remove entries that are not relevant to the target
33 |platform|.
34 '''
35 for key in properties.keys():
36 value = properties[key]
37 if not platform in value['platform']:
38 del properties[key]
39 else:
40 if 'subdocs' in value:
41 _Filter(value['subdocs'], platform)
42
43 def _ConvertAndSortDocs(properties):
44 '''Convert docs and subdocs into lists and sort them, first by level, then
45 by name.
46 '''
47 levels = {
48 'required': 0,
49 'recommended': 1,
50 'only_one': 2,
51 'optional': 3,
52 }
53
54 keyfunc = lambda item: (levels[item.get('level', 'optional')], item['name'])
not at google - send to devlin 2013/07/24 21:45:56 this code is a bit obfuscated for me to read, but
jshumway 2013/07/26 00:36:46 brilliant idea, using list.index().
55
56 def convert_and_sort_recur(properties):
not at google - send to devlin 2013/07/24 21:45:56 the "recur" is a bit of an implementation detail..
jshumway 2013/07/26 00:36:46 Done.
57 for key in properties.keys():
58 value = properties[key]
59 if 'subdocs' in value:
60 properties[key]['subdocs'] = convert_and_sort_recur(value['subdocs'])
61 return sorted(properties.values(), key=keyfunc)
62
63 return convert_and_sort_recur(properties)
64
65 def _Annotate(properties):
66 '''Add level annotations to level groups in a manifest property list and
67 subdoc lists. Requeries a list of properties sorted by level, with subdocs
68 in sorted lists as well.
69 '''
70 annotations = {
71 'required': 'Required',
72 'recommended': 'Recommended',
73 'only_one': 'Pick one (or none)',
74 'optional': 'Optional'
75 }
76
77 def add_annotation(item, annotation):
78 if not 'annotations' in item:
79 item['annotations'] = []
80 item['annotations'].insert(0, annotation)
81
82 def annotate_recur(parent_level, properties):
not at google - send to devlin 2013/07/24 21:45:56 likewise
jshumway 2013/07/26 00:36:46 Done.
83 current_level = parent_level
84 for item in properties:
85 level = item.get('level', 'optional')
86 if level != current_level:
87 add_annotation(item, annotations[level])
88 current_level = level
89 if 'subdocs' in item:
90 annotate_recur(level, item['subdocs'])
91
92 annotate_recur('required', properties)
93
11 class ManifestDataSource(object): 94 class ManifestDataSource(object):
12 '''Provides a template with access to manifest properties specific to apps or 95 '''Provides a template with access to manifest properties specific to apps or
13 extensions. 96 extensions.
14 ''' 97 '''
15 def __init__(self, 98 def __init__(self,
16 compiled_fs_factory, 99 compiled_fs_factory,
17 file_system, 100 file_system,
18 manifest_path, 101 manifest_path,
19 features_path): 102 features_path):
20 self._manifest_path = manifest_path 103 self._manifest_path = manifest_path
21 self._features_path = features_path 104 self._features_path = features_path
22 self._file_system = file_system 105 self._file_system = file_system
23 self._cache = compiled_fs_factory.Create( 106 self._cache = compiled_fs_factory.Create(
24 self._CreateManifestData, ManifestDataSource) 107 self._CreateManifestData, ManifestDataSource)
25 108
26 def _ApplyAppsTransformations(self, manifest): 109 def _TransformForTemplates(self, master_dict, platform):
27 manifest['required'][0]['example'] = 'Application' 110 properties = deepcopy(master_dict)
28 manifest['optional'][-1]['is_last'] = True 111 _Filter(properties, platform)
112 properties = _ConvertAndSortDocs(properties)
113 _Annotate(properties)
not at google - send to devlin 2013/07/24 21:45:56 I'd rather see this as properties = _Annotate(_Co
jshumway 2013/07/26 00:36:46 Done.
29 114
30 def _ApplyExtensionsTransformations(self, manifest): 115 if platform == 'app':
31 manifest['optional'][-1]['is_last'] = True 116 for item in properties:
117 if item['name'] == 'name':
118 item['example']['value'] = 'Application'
119 break
not at google - send to devlin 2013/07/24 21:45:56 maybe we could do some slightly less hacky hackery
jshumway 2013/07/26 00:36:46 Done.
120
121 properties[-1]['is_last'] = True
122
123 return properties
32 124
33 def _CreateManifestData(self, _, content): 125 def _CreateManifestData(self, _, content):
34 '''Take the contents of |_manifest_path| and create apps and extensions 126 '''Combine the contents of |_manifest_path| and |_features_path| into a
35 versions of a manifest example based on the contents of |_features_path|. 127 master dictionary then create filtered lists to be used by templates.
36 ''' 128 '''
37 def create_manifest_dict(): 129 manifest_json = Parse(content)
38 manifest_dict = OrderedDict() 130 master = ProcessFeaturesFile(
39 for category in ('required', 'only_one', 'recommended', 'optional'): 131 Parse(self._file_system.ReadSingle(self._features_path)))
not at google - send to devlin 2013/07/24 21:45:56 better name that "master"
jshumway 2013/07/26 00:36:46 Done.
40 manifest_dict[category] = []
41 return manifest_dict
42 132
43 apps = create_manifest_dict() 133 MergeDictionaries(master, manifest_json)
44 extensions = create_manifest_dict() 134 _InsertSubDocs(master)
45 135
46 manifest_json = Parse(content) 136 return {
47 features_json = Parse(self._file_system.ReadSingle( 137 'apps': self._TransformForTemplates(master, 'app'),
48 self._features_path)) 138 'extensions': self._TransformForTemplates(master, 'extension'),
49 139 'master': master
not at google - send to devlin 2013/07/24 21:45:56 why do you need to cache the master?
jshumway 2013/07/26 00:36:46 I was talking with Evan about adding manifest prop
50 def add_property(feature, manifest_key, category): 140 }
51 '''If |feature|, from features_json, has the correct extension_types, add
52 |manifest_key| to either apps or extensions.
53 '''
54 added = False
55 extension_types = feature['extension_types']
56 if extension_types == 'all' or 'platform_app' in extension_types:
57 apps[category].append(deepcopy(manifest_key))
58 added = True
59 if extension_types == 'all' or 'extension' in extension_types:
60 extensions[category].append(deepcopy(manifest_key))
61 added = True
62 return added
63
64 # Property types are: required, only_one, recommended, and optional.
65 for category in manifest_json:
66 for manifest_key in manifest_json[category]:
67 # If a property is in manifest.json but not _manifest_features, this
68 # will cause an error.
69 feature = features_json[manifest_key['name']]
70 if add_property(feature, manifest_key, category):
71 del features_json[manifest_key['name']]
72
73 # All of the properties left in features_json are assumed to be optional.
74 for feature in features_json.keys():
75 item = features_json[feature]
76 # Handles instances where a features entry is a union with a whitelist.
77 if isinstance(item, list):
78 item = item[0]
79 add_property(item, {'name': feature}, 'optional')
80
81 apps['optional'].sort(key=itemgetter('name'))
82 extensions['optional'].sort(key=itemgetter('name'))
83
84 self._ApplyAppsTransformations(apps)
85 self._ApplyExtensionsTransformations(extensions)
86
87 return {'apps': apps, 'extensions': extensions}
88 141
89 def get(self, key): 142 def get(self, key):
90 return self._cache.GetFromFile(self._manifest_path)[key] 143 return self._cache.GetFromFile(self._manifest_path)[key]
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698