Chromium Code Reviews| OLD | NEW |
|---|---|
| 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] |
| OLD | NEW |