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 json | |
6 import logging | |
7 import os | |
8 | |
9 from appengine_url_fetcher import AppEngineUrlFetcher | |
10 from caching_file_system import CachingFileSystem | |
11 from compiled_file_system import CompiledFileSystem | |
12 from subversion_file_system import SubversionFileSystem | |
13 import svn_constants | |
14 from third_party.json_schema_compiler import json_comment_eater | |
15 from third_party.json_schema_compiler import model | |
16 import url_constants | |
17 | |
18 # These labels are used for interacting with AvailabilityDataSource's object | |
19 # store. The format is <label><string> (e.g. svn21, or permissiontabs26) | |
20 SVN = 'svn' | |
21 PERMISSION = 'permission' | |
22 MANIFEST = 'manifest' | |
23 FILESYS = 'filesys' | |
not at google - send to devlin
2013/04/30 18:34:19
use these in category= when creating the object st
epeterson
2013/05/13 02:38:10
Done.
| |
24 | |
25 class AvailabilityDataSource(object): | |
26 '''Uses API data sources generated by a ChromeVersionDataSource in order to | |
27 search the filesystem for the earliest existence of a specified API throughout | |
28 the different versions of Chrome; this constitutes an API's availability. | |
29 ''' | |
30 class Factory(object): | |
31 def __init__(self, | |
32 chrome_version_utility, | |
33 object_store_creator_factory, | |
34 svn_file_system): | |
35 self._chrome_version_utility = chrome_version_utility | |
36 self._object_store_creator_factory = object_store_creator_factory | |
37 json_string = svn_file_system.ReadSingle('%s/api_availabilities.json' | |
38 % svn_constants.JSON_PATH) | |
39 self._existing_availabilities = json.loads(json_string) | |
40 json_string = svn_file_system.ReadSingle('%s/_permission_features.json' | |
41 % svn_constants.API_PATH) | |
42 self._permission_features = json.loads(json_comment_eater.Nom( | |
43 json_string)) | |
44 json_string = svn_file_system.ReadSingle('%s/_manifest_features.json' | |
45 % svn_constants.API_PATH) | |
not at google - send to devlin
2013/04/30 18:34:19
use third_party.json_schema_compiler.json_parse as
epeterson
2013/05/13 02:38:10
Done.
| |
46 self._manifest_features = json.loads(json_comment_eater.Nom(json_string)) | |
47 | |
48 def Create(self): | |
49 return AvailabilityDataSource(self._chrome_version_utility, | |
50 self._object_store_creator_factory, | |
51 self._existing_availabilities, | |
52 self._permission_features, | |
53 self._manifest_features) | |
54 | |
55 def __init__(self, | |
56 chrome_version_utility, | |
57 object_store_creator_factory, | |
58 existing_availabilities, | |
59 permission_features, | |
60 manifest_features): | |
61 self._chrome_version_utility = chrome_version_utility | |
62 self._object_store_creator_factory = object_store_creator_factory | |
63 self._object_store = self._object_store_creator_factory.Create( | |
64 AvailabilityDataSource).Create() | |
65 self._existing_availabilities = existing_availabilities | |
66 self._permission_features = permission_features | |
67 self._manifest_features = manifest_features | |
68 self._permission_apis = [] | |
69 self._manifest_apis = [] | |
70 self._orphan_apis = [] | |
71 | |
72 def _CreateSvnFileSystemForBranch(self, branch): | |
not at google - send to devlin
2013/04/30 18:34:19
maybe this should go as a little helper on Subvers
epeterson
2013/05/13 02:38:10
Well, I had done this, and modified the old Create
| |
73 '''Creates a file system tied to a specific |branch| of the svn repository. | |
74 ''' | |
75 if branch == 'trunk': | |
76 svn_url = '/'.join((url_constants.SVN_TRUNK_URL, | |
77 'src', | |
78 svn_constants.EXTENSIONS_PATH)) | |
79 else: | |
80 svn_url = '/'.join((url_constants.SVN_BRANCH_URL, | |
81 branch, | |
82 'src', | |
83 svn_constants.EXTENSIONS_PATH)) | |
84 viewvc_url = svn_url.replace(url_constants.SVN_URL, | |
85 url_constants.VIEWVC_URL) | |
86 | |
87 svn_file_system = CachingFileSystem( | |
88 SubversionFileSystem(AppEngineUrlFetcher(svn_url), | |
89 AppEngineUrlFetcher(viewvc_url)), | |
90 self._object_store_creator_factory) | |
91 return svn_file_system | |
92 | |
93 def _GetChannelFromPermissionFeatures(self, api_name, permission_features): | |
94 '''Sometimes the API information will be in a dict, other times it will be | |
95 in a dict contained in a list. | |
96 ''' | |
97 api_info = permission_features.get(api_name, None) | |
98 if api_info is not None: | |
99 try: | |
100 channel = api_info.get('channel') | |
101 except AttributeError: | |
102 channel = api_info[0].get('channel') | |
not at google - send to devlin
2013/04/30 18:34:19
don't use exceptions for control flow. Also, if it
epeterson
2013/05/13 02:38:10
Done.
| |
103 finally: | |
104 return channel | |
105 return None | |
106 | |
107 def _CheckStablePermissionExistence(self, api_name, version): | |
108 '''Checks _permission_features.json for the given |version| of Chrome, | |
109 and returns a boolean representing whether or not the API was available on | |
110 the stable channel in |version|. | |
111 ''' | |
112 existence = self._object_store.Get('%s%s%s' | |
113 % (PERMISSION, api_name, version)).Get() | |
114 if existence is not None: | |
115 return existence | |
116 | |
117 svn_file_system = self._object_store.Get('%s%s' % (SVN, version)).Get() | |
118 if svn_file_system is None: | |
119 svn_file_system = self._CreateSvnFileSystemForBranch( | |
120 self._chrome_version_utility.GetBranchNumberForVersion(version)) | |
not at google - send to devlin
2013/04/30 18:34:19
I don't think you can put svn file systems in obje
epeterson
2013/05/13 02:38:10
Done.
| |
121 self._object_store.Set('%s%s' % (SVN, version), svn_file_system) | |
122 | |
123 json_string = svn_file_system.ReadSingle('%s/_permission_features.json' | |
124 % svn_constants.API_PATH) | |
125 permission_features = json.loads(json_comment_eater.Nom(json_string)) | |
not at google - send to devlin
2013/04/30 18:34:19
same comment re json parsing i.e. use json_parse.P
epeterson
2013/05/13 02:38:10
Done.
| |
126 | |
127 existence = 'stable' == self._GetChannelFromPermissionFeatures( | |
128 api_name, permission_features) | |
129 self._object_store.Set('%s%s%s' % (PERMISSION, api_name, version), | |
130 existence) | |
131 return existence | |
132 | |
133 def _CheckManifestExistence(self, api_name, version): | |
134 '''As a fallback measure for an API not being found in _permission_features, | |
135 check _manifest_features.json for the given |version| of Chrome, | |
136 and return a boolean representing whether or not the API was represented | |
137 within the file. | |
138 ''' | |
139 #_manifest_features.json uses unix_hacker_style API names. | |
140 api_name = model.UnixName(api_name) | |
141 existence = self._object_store.Get('%s%s%s' | |
142 % (MANIFEST, api_name, version)).Get() | |
143 if existence is not None: | |
144 return existence | |
145 | |
146 svn_file_system = self._object_store.Get('%s%s' % (SVN, version)).Get() | |
147 if svn_file_system is None: | |
148 svn_file_system = self._CreateSvnFileSystemForBranch( | |
149 self._chrome_version_utility.GetBranchNumberForVersion(version)) | |
150 self._object_store.Set('%s%s' % (SVN, version), svn_file_system) | |
151 | |
152 json_string = svn_file_system.ReadSingle('%s/_manifest_features.json' | |
153 % svn_constants.API_PATH) | |
154 manifest_features = json.loads(json_comment_eater.Nom(json_string)) | |
155 if manifest_features.get(api_name, None) is not None: | |
156 existence = True | |
157 else: | |
158 existence = False | |
159 self._object_store.Set('%s%s%s' % (MANIFEST, api_name, version), existence) | |
160 return existence | |
not at google - send to devlin
2013/04/30 18:34:19
similar comments
epeterson
2013/05/13 02:38:10
Done.
| |
161 | |
162 def _GetAllNames(self, files_dict): | |
163 '''Returns extension-less names for a list of files. | |
164 ''' | |
165 files = files_dict[svn_constants.API_PATH + '/'] | |
166 return [model.UnixName(os.path.splitext(f)[0]) for f in files] | |
167 | |
168 def _CheckFileSystemExistence(self, api_name, version): | |
169 '''Uses a list of API names for a given branch to check simply for the | |
170 existence of a given API in that branch. | |
171 ''' | |
172 names = self._object_store.Get('%s%s' % (FILESYS, version)).Get() | |
173 if names is not None: | |
174 return api_name in names | |
175 | |
176 svn_file_system = self._object_store.Get('%s%s' % (SVN, version)).Get() | |
177 if svn_file_system is None: | |
178 svn_file_system = self._CreateSvnFileSystemForBranch( | |
179 self._chrome_version_utility.GetBranchNumberForVersion(version)) | |
180 self._object_store.Set('%s%s' % (SVN, version), svn_file_system) | |
181 names = svn_file_system.Read([svn_constants.API_PATH + '/']).Get() | |
182 names = self._GetAllNames(names) | |
183 self._object_store.Set('%s%s' % (FILESYS, version), names) | |
184 return api_name in names | |
not at google - send to devlin
2013/04/30 18:34:19
similar comments
epeterson
2013/05/13 02:38:10
Done.
| |
185 | |
186 def _FindEarliestStableAvailability(self, api_name, version): | |
187 '''Searches in descending order through filesystem caches tied to specific | |
188 chrome version numbers and looks for the availability of an API, |api_name|, | |
189 on the stable channel. When a version is found where the API is no longer | |
190 available on stable, returns the previous version number (the last known | |
191 version where the API was stable). | |
192 ''' | |
193 # The _permission_features.json file (with 'channel' keys) is present only | |
194 # in Chrome 20 and onwards. | |
195 while version >= 20: | |
196 if ((api_name in self._permission_apis | |
197 and self._CheckStablePermissionExistence(api_name, str(version))) | |
198 or (api_name in self._manifest_apis | |
199 and self._CheckManifestExistence(api_name, str(version))) | |
200 or (api_name in self._orphan_apis | |
201 and self._CheckFileSystemExistence(api_name, str(version)))): | |
202 version -= 1 | |
203 else: | |
204 break | |
205 version += 1 | |
206 return str(version) | |
not at google - send to devlin
2013/04/30 18:34:19
return version + 1 rather than incrementing
epeterson
2013/05/13 02:38:10
Done.
| |
207 | |
208 def GetAvailability(self, api_name): | |
209 '''Determines the availability for an API by testing several scenarios. | |
210 (i.e. Is the API experimental? Only available on certain development | |
211 channels? If it's stable, when did it first become stable? etc.) | |
212 ''' | |
213 availability = self._object_store.Get(api_name).Get() | |
214 if availability is not None: | |
215 return availability | |
216 | |
217 # Check for a predetermined availability for this API. | |
218 availability = self._existing_availabilities.get(api_name, None) | |
not at google - send to devlin
2013/04/30 18:34:19
the None is implicit in get()
epeterson
2013/05/13 02:38:10
Done.
| |
219 if availability is not None: | |
220 self._object_store.Set(api_name, availability) | |
221 return availability | |
222 | |
223 latest_version = str(self._chrome_version_utility.GetLatestVersionNumber()) | |
224 channel = self._GetChannelFromPermissionFeatures(api_name, | |
225 self._permission_features) | |
226 if channel is not None: | |
227 # This is a special case: availability can be any of the dev channels, | |
228 # to check for this information instead of checking just for existence. | |
229 availability = channel | |
230 self._permission_apis.append(api_name) | |
231 elif self._CheckManifestExistence(api_name, latest_version): | |
232 availability = 'stable' | |
233 self._manifest_apis.append(api_name) | |
234 elif self._CheckFileSystemExistence(api_name, latest_version): | |
235 availability = 'stable' | |
236 self._orphan_apis.append(api_name) | |
237 else: | |
238 logging.warning('%s could not be located. %s\'s availability is None.' | |
239 % (api_name, api_name)) | |
240 availability = None | |
not at google - send to devlin
2013/04/30 18:34:19
throw a FileNotFoundError here or something, so we
epeterson
2013/05/13 02:38:10
Done.
| |
241 | |
242 if availability != 'stable': | |
243 self._object_store.Set(api_name, availability) | |
244 return availability | |
245 | |
246 availability = self._FindEarliestStableAvailability(api_name, | |
247 int(latest_version) - 1) | |
248 self._object_store.Set(api_name, availability) | |
249 return availability | |
not at google - send to devlin
2013/04/30 18:34:19
slightly nicer factoring:
if availability == 'sta
epeterson
2013/05/13 02:38:10
Done.
| |
OLD | NEW |