OLD | NEW |
---|---|
(Empty) | |
1 # Copyright (c) 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 import collections | |
6 import logging | |
7 import os | |
8 | |
9 from branch_utility import BranchUtility | |
10 from compiled_file_system import CompiledFileSystem | |
11 from file_system import FileNotFoundError | |
12 import svn_constants | |
13 from third_party.json_schema_compiler import json_parse, model | |
14 | |
15 _API_AVAILABILITIES = svn_constants.JSON_PATH + '/api_availabilities.json' | |
16 _EXTENSION_API = svn_constants.API_PATH + '/extension_api.json' | |
17 _API_FEATURES = svn_constants.API_PATH + '/_api_features.json' | |
18 _MANIFEST_FEATURES = svn_constants.API_PATH + '/_manifest_features.json' | |
19 _PERMISSION_FEATURES = svn_constants.API_PATH + '/_permission_features.json' | |
20 | |
21 def _GetChannelFromFeature(feature): | |
22 '''Handles finding API channel information from _features.json files. | |
not at google - send to devlin
2013/06/05 00:24:09
"Finds API channel information..."
epeterson
2013/06/17 20:05:49
Done.
| |
23 Sometimes this info will be in a dict, but other times it will be | |
24 in a dict contained in a list for whitelisting purposes. | |
25 ''' | |
26 channel = None | |
27 if feature is not None: | |
28 if isinstance(feature, collections.Mapping): | |
29 channel = feature.get('channel') | |
30 else: | |
31 channel = BranchUtility.NewestChannel(entry.get('channel') | |
32 for entry in feature) | |
33 return channel | |
not at google - send to devlin
2013/06/05 00:24:09
more concise:
if feature is None:
return None
i
epeterson
2013/06/17 20:05:49
Done.
| |
34 | |
epeterson
2013/06/02 00:25:50
A lot of the logic for checking individual files/f
| |
35 def _GetFeature(api_name, file_system, path, unix_name=False): | |
not at google - send to devlin
2013/06/05 00:24:09
this is only used in ExistsInFeatures, move it in
epeterson
2013/06/17 20:05:49
Done. It might be useful to access the rest of the
| |
36 '''Gets the data for a given API schema from api/_api_features.json | |
37 within the given file system. | |
38 ''' | |
39 if unix_name: | |
40 api_name = model.UnixName(api_name) | |
not at google - send to devlin
2013/06/05 00:24:09
what is this unix_name thing?
epeterson
2013/06/17 20:05:49
_manifest_features.json uses unix_style API names,
| |
41 return file_system.GetFromFile(path).get(api_name) | |
42 | |
43 def _ExistsInFeatures(api_name, file_system, path, unix_name=False): | |
not at google - send to devlin
2013/06/05 00:24:09
as per comment way below, call all of these method
epeterson
2013/06/17 20:05:49
Done.
| |
44 '''Returns the name of the development channel that the API is available on | |
45 if this information can be located in the _features.json file specified by | |
46 |path| within the given |file_system|. Returns None, otherwise. | |
47 ''' | |
48 return _GetChannelFromFeature( | |
49 _GetFeature(api_name, file_system, path, unix_name)) | |
50 | |
51 def _ExistsInApiFeatures(api_name, file_system): | |
52 try: | |
53 return _ExistsInFeatures(api_name, file_system, _API_FEATURES) | |
54 except FileNotFoundError as e: | |
55 # TODO(epeterson) Remove except block once _api_features is in all channels. | |
56 logging.warning('_api_features.json not found') | |
not at google - send to devlin
2013/06/05 00:24:09
don't warn it'll pollute the logs - but good point
epeterson
2013/06/17 20:05:49
Done.
| |
57 return False | |
58 | |
59 def _ExistsInPermissionFeatures(api_name, file_system): | |
60 return _ExistsInFeatures(api_name, file_system, _PERMISSION_FEATURES) | |
61 | |
62 def _ExistsInManifestFeatures(api_name, file_system): | |
not at google - send to devlin
2013/06/05 00:24:09
_GetChannelFromManifestFeatures
epeterson
2013/06/17 20:05:49
Done.
| |
63 return _ExistsInFeatures(api_name, file_system, _MANIFEST_FEATURES) | |
64 | |
65 def _ExistsInFileSystem(api_name, file_system): | |
66 '''Checks for existence of |api_name| within the list of files in the api/ | |
67 directory found using the given file system. | |
68 ''' | |
69 api_name = model.UnixName(api_name) | |
70 return api_name in file_system.GetFromFileListing(svn_constants.API_PATH) | |
71 | |
72 def _ExistsInExtensionApi(api_name, file_system): | |
73 '''Parses the api/extension_api.json file (available in Chrome versions | |
74 before 18) for an API namespace. If this is successfully found, then the API | |
75 is considered to have been 'stable' for the given version. | |
76 ''' | |
77 try: | |
78 extension_api_json = file_system.GetFromFile(_EXTENSION_API) | |
79 api_rows = [row.get('namespace') for row in extension_api_json | |
80 if 'namespace' in row] | |
81 return True if api_name in api_rows else False | |
82 except FileNotFoundError as e: | |
83 # This should only happen on preview.py since extension_api.json is no | |
84 # longer present in trunk. | |
85 return False | |
86 | |
87 class AvailabilityFinder(object): | |
88 '''Uses API data sources generated by a ChromeVersionDataSource in order to | |
89 search the filesystem for the earliest existence of a specified API throughout | |
90 the different versions of Chrome; this constitutes an API's availability. | |
91 ''' | |
92 class Factory(object): | |
93 def __init__(self, | |
94 object_store_creator, | |
95 compiled_host_fs_factory, | |
96 branch_utility, | |
97 create_file_system): | |
not at google - send to devlin
2013/06/05 00:24:09
please document create_file_system. create_file_sy
epeterson
2013/06/17 20:05:49
Done.
| |
98 self._object_store_creator = object_store_creator | |
99 self._compiled_host_fs_factory = compiled_host_fs_factory | |
100 self._branch_utility = branch_utility | |
101 self._create_file_system = create_file_system | |
102 | |
103 def Create(self): | |
104 return AvailabilityFinder(self._object_store_creator, | |
105 self._compiled_host_fs_factory, | |
106 self._branch_utility, | |
107 self._create_file_system) | |
108 | |
109 def __init__(self, | |
110 object_store_creator, | |
111 compiled_host_fs_factory, | |
112 branch_utility, | |
113 create_file_system): | |
114 self._object_store_creator = object_store_creator | |
115 self._json_cache = compiled_host_fs_factory.Create( | |
116 lambda _, json: json_parse.Parse(json), | |
117 AvailabilityFinder, | |
118 'json-cache') | |
119 self._branch_utility = branch_utility | |
120 self._create_file_system = create_file_system | |
121 self._object_store = object_store_creator.Create(AvailabilityFinder) | |
122 | |
123 def _CreateFeaturesAndNamesFileSystems(self, version): | |
epeterson
2013/06/02 00:25:50
Checking _features.json files requires a different
| |
124 '''The 'features' compiled file system's populate function parses and | |
125 returns the contents of a _features.json file. The 'names' compiled file | |
126 system's populate function creates a list of file names with .json or .idl | |
127 extensions. | |
128 ''' | |
129 fs_factory = CompiledFileSystem.Factory( | |
not at google - send to devlin
2013/06/05 00:24:09
recreating them each time like this is inefficent
epeterson
2013/06/17 20:05:49
Done.
| |
130 self._create_file_system(version), | |
131 self._object_store_creator) | |
132 features_fs = fs_factory.Create(lambda _, json: json_parse.Parse(json), | |
133 AvailabilityFinder, | |
134 '%sFeatures' % version) | |
not at google - send to devlin
2013/06/05 00:24:09
category=
also you don't need the version since t
epeterson
2013/06/17 20:05:49
Ok, this clears things up. thank you.
| |
135 names_fs = fs_factory.Create(self._GetExtNames, | |
136 AvailabilityFinder, | |
137 '%sNames' % version) | |
not at google - send to devlin
2013/06/05 00:24:09
likewise category='names'
epeterson
2013/06/17 20:05:49
Done.
| |
138 return (features_fs, names_fs) | |
139 | |
140 def _GetExtNames(self, base_path, apis): | |
141 return [os.path.splitext(api)[0] for api in apis | |
142 if os.path.splitext(api)[1][1:] in ['json', 'idl']] | |
143 | |
144 def _FindEarliestAvailability(self, api_name, version): | |
145 '''Searches in descending order through filesystem caches tied to specific | |
146 chrome version numbers and looks for the availability of an API, |api_name|, | |
147 on the stable channel. When a version is found where the API is no longer | |
148 available on stable, returns the previous version number (the last known | |
149 version where the API was stable). | |
150 ''' | |
epeterson
2013/06/02 00:25:50
I think this code looks a lot more readable, and s
not at google - send to devlin
2013/06/05 00:24:09
Yes much better, though recursiveness should be av
epeterson
2013/06/17 20:05:49
Here's an iterative version.
| |
151 if version >= 5: | |
152 features_fs, names_fs = self._CreateFeaturesAndNamesFileSystems(version) | |
not at google - send to devlin
2013/06/05 00:24:09
just have an "if version < 5: return version" at t
epeterson
2013/06/17 20:05:49
Done.
| |
153 | |
154 available = False | |
155 # The _api_features.json file first appears in version 28 and should be the | |
156 # most reliable for finding API availabilities, so it gets checked first. | |
157 # The _permission_features.json and _manifest_features.json files are | |
158 # present in Chrome 20 and onwards. Fall back to a check for file system | |
159 # existence if the API is not stable in any of the _features.json files. | |
not at google - send to devlin
2013/06/05 00:24:09
could you put all of these comments inside the blo
epeterson
2013/06/17 20:05:49
Done.
| |
160 if version >= 28: | |
161 available = _ExistsInApiFeatures(api_name, features_fs) == 'stable' | |
162 # Check other _features.json files/file existence if the API wasn't found in | |
163 # _api_features.json, or if _api_features.json wasn't present. | |
164 if version >= 20: | |
165 available = available or ( | |
166 _ExistsInPermissionFeatures(api_name, features_fs) == 'stable' | |
167 or _ExistsInManifestFeatures(api_name, features_fs) == 'stable' | |
168 or _ExistsInFileSystem(api_name, names_fs)) | |
169 # These versions are a little troublesome. Version 19 has | |
170 # _permission_features.json, but it lacks 'channel' information. Version 18 | |
171 # lacks all of the _features.json files. For now, we're using a simple check | |
172 # for filesystem existence here. | |
173 elif version >= 18: | |
174 available = _ExistsInFileSystem(api_name, names_fs) | |
175 # Versions 17 and down to 5 have an extension_api.json file which contains | |
176 # namespaces for each API that was available at the time. We can use this | |
177 # file to check for API existence. | |
178 elif version >= 5: | |
179 available = _ExistsInExtensionApi(api_name, features_fs) | |
180 | |
181 if available: | |
182 return self._FindEarliestAvailability(api_name, version - 1) | |
183 return version + 1 | |
184 | |
185 def _IsAvailableInFileSystem(self, api_name, version): | |
not at google - send to devlin
2013/06/05 00:24:09
as noted below, make this _GetAvailableChannelForV
epeterson
2013/06/17 20:05:49
Done.
| |
186 features_fs, names_fs = self._CreateFeaturesAndNamesFileSystems(version) | |
187 return (_ExistsInApiFeatures(api_name, features_fs) | |
188 or _ExistsInPermissionFeatures(api_name, features_fs) | |
189 or _ExistsInManifestFeatures(api_name, features_fs) | |
190 or _ExistsInFileSystem(api_name, names_fs)) | |
191 | |
192 def GetApiAvailability(self, api_name): | |
193 '''Determines the availability for an API by testing several scenarios. | |
194 (i.e. Is the API experimental? Only available on certain development | |
195 channels? If it's stable, when did it first become stable? etc.) | |
196 ''' | |
197 availability = self._object_store.Get(api_name).Get() | |
198 if availability is not None: | |
199 return availability | |
200 | |
201 # Check for a predetermined availability for this API. | |
202 api_info = self._json_cache.GetFromFile(_API_AVAILABILITIES).get(api_name) | |
203 if api_info is not None: | |
204 channel = api_info.get('channel') | |
205 version = api_info.get('version') if channel == 'stable' else None | |
206 availability = AvailabilityInfo(channel, version) | |
207 self._object_store.Set(api_name, availability) | |
208 return availability | |
not at google - send to devlin
2013/06/05 00:24:09
more concise:
availability = AvailabilityInfo(api
epeterson
2013/06/17 20:05:49
Done.
| |
209 | |
210 # Check for the API in the development channels. | |
211 availability = None | |
212 for channel_info in self._branch_utility.GetAllChannelInfo(): | |
213 if self._IsAvailableInFileSystem(api_name, | |
214 channel_info.version): | |
215 availability = AvailabilityInfo(channel_info.channel, | |
216 channel_info.version) | |
217 break | |
not at google - send to devlin
2013/06/05 00:24:09
i think this has a bug in scenarios such as: an AP
epeterson
2013/06/17 20:05:49
Going to continue testing as I prepare to submit a
| |
218 | |
219 # The API should at least be available on trunk. It's a bug otherwise. | |
220 assert availability, 'No availability found for %s' % api_name | |
221 | |
222 # If the API is in stable, find the chrome version in which it became | |
223 # stable. | |
224 if availability.channel == 'stable': | |
225 availability.version = self._FindEarliestAvailability( | |
226 api_name, | |
227 availability.version) | |
228 | |
229 self._object_store.Set(api_name, availability) | |
230 return availability | |
231 | |
232 class AvailabilityInfo(object): | |
not at google - send to devlin
2013/06/05 00:24:09
declare at top
epeterson
2013/06/17 20:05:49
Done.
| |
233 def __init__(self, channel, version): | |
234 self.channel = channel | |
235 self.version = version | |
OLD | NEW |