Chromium Code Reviews| 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 os | |
| 7 | |
| 8 from branch_utility import BranchUtility | |
| 9 from chrome_version_utility import ChromeVersionUtility | |
| 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 = 'api_availabilities.json' | |
| 16 EXTENSION_API = 'extension_api.json' | |
| 17 MANIFEST_FEATURES = '_manifest_features.json' | |
| 18 PERMISSION_FEATURES = '_permission_features.json' | |
|
not at google - send to devlin
2013/05/15 08:15:00
these are all private so should start with a _, an
epeterson
2013/06/02 00:25:50
Done.
| |
| 19 | |
| 20 def _GetChannelFromPermissionFeature(permission_feature): | |
| 21 '''Handles finding API channel information from _permission_features.json. | |
| 22 Sometimes this info will be in a dict, but other times it will be | |
| 23 in a dict contained in a list. | |
| 24 ''' | |
| 25 channel = None | |
| 26 if permission_feature is not None: | |
| 27 if isinstance(permission_feature, collections.Mapping): | |
| 28 channel = permission_feature.get('channel') | |
| 29 else: | |
| 30 channel = BranchUtility.NewestChannel(entry.get('channel') | |
| 31 for entry in permission_feature) | |
| 32 return channel | |
| 33 | |
| 34 class AvailabilityFinder(object): | |
| 35 '''Uses API data sources generated by a ChromeVersionDataSource in order to | |
| 36 search the filesystem for the earliest existence of a specified API throughout | |
| 37 the different versions of Chrome; this constitutes an API's availability. | |
| 38 ''' | |
| 39 class AvailabilityInfo(object): | |
|
not at google - send to devlin
2013/05/15 08:15:00
this object is picklable, so it can be directly pu
epeterson
2013/06/02 00:25:50
Done. (I had tried this initially, but I think it
| |
| 40 def __init__(self, channel, version): | |
| 41 self.channel = channel | |
| 42 self.version = version | |
| 43 | |
| 44 class Factory(object): | |
| 45 def __init__(self, | |
| 46 chrome_version_utility, | |
| 47 object_store_creator, | |
| 48 compiled_host_fs_factory, | |
| 49 create_file_system): | |
| 50 self._chrome_version_utility = chrome_version_utility | |
| 51 self._object_store_creator = object_store_creator | |
| 52 self._compiled_host_fs_factory = compiled_host_fs_factory | |
| 53 self._create_file_system = create_file_system | |
| 54 | |
| 55 def Create(self): | |
| 56 return AvailabilityFinder(self._chrome_version_utility, | |
| 57 self._object_store_creator, | |
| 58 self._compiled_host_fs_factory, | |
| 59 self._create_file_system) | |
| 60 | |
| 61 def __init__(self, | |
| 62 chrome_version_utility, | |
| 63 object_store_creator, | |
| 64 compiled_host_fs_factory, | |
| 65 create_file_system): | |
| 66 self._chrome_version_utility = chrome_version_utility | |
| 67 self._object_store_creator = object_store_creator | |
| 68 self._json_cache = compiled_host_fs_factory.Create(self._ParseForCompiledFS, | |
|
not at google - send to devlin
2013/05/15 08:15:00
just pass in json_parse.Parse, no need to wrap it
epeterson
2013/06/02 00:25:50
compiled filesystems send a path and file data to
| |
| 69 AvailabilityFinder, | |
| 70 'json-cache') | |
| 71 self._branch_utility = BranchUtility.Create(object_store_creator) | |
| 72 self._create_file_system = create_file_system | |
| 73 self._object_store = object_store_creator.Create(AvailabilityFinder) | |
| 74 self._permission_apis = [] | |
|
not at google - send to devlin
2013/05/15 08:15:00
as I mentioned in IRC - holding onto these (permis
epeterson
2013/06/02 00:25:50
The intent was to use these to force APIs that wer
| |
| 75 self._manifest_apis = [] | |
| 76 self._orphan_apis = [] | |
| 77 | |
| 78 def _ParseForCompiledFS(self, path, json): | |
| 79 '''Wrapper method to be passed to compiled_file_systems upon creation. | |
| 80 ''' | |
| 81 return json_parse.Parse(json) | |
| 82 | |
| 83 def _CreateCompiledFS(self, branch): | |
| 84 # TODO(epeterson) Move create_file_system logic into ChromeVersionUtility. | |
|
not at google - send to devlin
2013/05/15 08:15:00
don't worry about it
| |
| 85 compiled_fs_factory = CompiledFileSystem.Factory( | |
| 86 self._create_file_system(branch), | |
| 87 self._object_store_creator) | |
| 88 return compiled_fs_factory.Create(self._ParseForCompiledFS, | |
|
not at google - send to devlin
2013/05/15 08:15:00
ditto
epeterson
2013/06/02 00:25:50
Done.
| |
| 89 AvailabilityFinder, | |
| 90 'branch') | |
| 91 | |
| 92 def _GetExistingAvailability(self, api_name, compiled_fs): | |
| 93 api_info = compiled_fs.GetFromFile( | |
| 94 '%s/%s' % (svn_constants.JSON_PATH, API_AVAILABILITIES)).get(api_name) | |
| 95 if api_info is not None: | |
| 96 return [api_info.get('channel'), api_info.get('version')] | |
| 97 return None | |
|
not at google - send to devlin
2013/05/15 08:15:00
as I mentioned, AvailabilityFinder is picklable, s
epeterson
2013/06/02 00:25:50
Done.
| |
| 98 | |
| 99 def _GetPermissionFeature(self, api_name, compiled_fs): | |
| 100 return compiled_fs.GetFromFile( | |
| 101 '%s/%s' % (svn_constants.API_PATH, PERMISSION_FEATURES)).get(api_name) | |
| 102 | |
| 103 def _GetManifestFeature(self, api_name, compiled_fs): | |
| 104 return compiled_fs.GetFromFile( | |
| 105 '%s/%s' % (svn_constants.API_PATH, MANIFEST_FEATURES)).get(api_name) | |
| 106 | |
| 107 def _GetJSONForExtensionAPI(self, api_name, compiled_fs): | |
| 108 return compiled_fs.GetFromFile( | |
| 109 '%s/%s' % (svn_constants.API_PATH, EXTENSION_API)) | |
| 110 | |
| 111 def _ReadJson(self, svn_file_system, path): | |
|
not at google - send to devlin
2013/05/15 08:15:00
not used
epeterson
2013/06/02 00:25:50
Done.
| |
| 112 return json_parse.Parse(svn_file_system.ReadSingle(path)) | |
| 113 | |
|
not at google - send to devlin
2013/05/15 08:15:00
extra \n
epeterson
2013/06/02 00:25:50
Done.
| |
| 114 | |
| 115 def _CheckStablePermissionExistence(self, api_name, version): | |
| 116 '''Checks _permission_features.json for the given |version| of Chrome, | |
| 117 and returns a boolean representing whether or not the API was available on | |
| 118 the stable channel in |version|. | |
| 119 ''' | |
| 120 compiled_fs = self._CreateCompiledFS( | |
| 121 self._chrome_version_utility.GetBranchNumberForVersion(version)) | |
| 122 permission_feature = self._GetPermissionFeature(api_name, compiled_fs) | |
| 123 existence = 'stable' == _GetChannelFromPermissionFeature(permission_feature) | |
| 124 return existence | |
| 125 | |
| 126 def _CheckChannelPermissionExistence(self, api_name, version, channel): | |
| 127 compiled_fs = self._CreateCompiledFS( | |
| 128 self._chrome_version_utility.GetBranchNumberForVersion(version)) | |
| 129 permission_feature = self._GetPermissionFeature(api_name, compiled_fs) | |
| 130 existence = channel == _GetChannelFromPermissionFeature(permission_feature) | |
| 131 return existence | |
| 132 | |
|
not at google - send to devlin
2013/05/15 08:15:00
extra \n
epeterson
2013/06/02 00:25:50
Done.
| |
| 133 | |
| 134 def _CheckSimplePermissionExistence(self, api_name, version): | |
|
not at google - send to devlin
2013/05/15 08:15:00
looks like this method can be combined with checks
epeterson
2013/06/02 00:25:50
Logic has been moved up above.
| |
| 135 compiled_fs = self._CreateCompiledFS( | |
| 136 self._chrome_version_utility.GetBranchNumberForVersion(version)) | |
| 137 permission_feature = self._GetPermissionFeature(api_name, compiled_fs) | |
| 138 return permission_feature is not None | |
| 139 | |
| 140 def _CheckManifestExistence(self, api_name, version): | |
|
not at google - send to devlin
2013/05/15 08:15:00
_ExistsInManifestFeatures, take a file system
epeterson
2013/06/02 00:25:50
Done.
| |
| 141 '''As a fallback measure for an API not being found in _permission_features, | |
| 142 check _manifest_features.json for the given |version| of Chrome, | |
| 143 and return a boolean representing whether or not the API was represented | |
| 144 within the file. | |
| 145 ''' | |
| 146 #_manifest_features.json uses unix_hacker_style API names. | |
| 147 api_name = model.UnixName(api_name) | |
| 148 compiled_fs = self._CreateCompiledFS( | |
| 149 self._chrome_version_utility.GetBranchNumberForVersion(version)) | |
| 150 manifest_feature = self._GetManifestFeature(api_name, compiled_fs) | |
| 151 return manifest_feature is not None | |
| 152 | |
| 153 def _GetAllNames(self, files): | |
| 154 '''Returns extension-less names for a list of files. | |
| 155 ''' | |
| 156 return [os.path.splitext(f)[0] for f in files] | |
| 157 | |
| 158 def _CheckFileSystemExistence(self, api_name, version): | |
|
not at google - send to devlin
2013/05/15 08:15:00
_ExistsInFileSystem, take a file system
epeterson
2013/06/02 00:25:50
Done.
| |
| 159 '''Uses a list of API names for a given branch to check simply for the | |
| 160 existence of a given API in that branch. | |
| 161 ''' | |
| 162 names = self._object_store.Get(str(version)).Get() | |
|
not at google - send to devlin
2013/05/15 08:15:00
shouldn't need to stringify version here
epeterson
2013/06/02 00:25:50
This has been removed completely. AvailabilityFind
| |
| 163 if names is not None: | |
| 164 return api_name in names | |
| 165 svn_file_system = self._create_file_system( | |
|
not at google - send to devlin
2013/05/15 08:15:00
use the compiled file system for this instead?
epeterson
2013/06/02 00:25:50
Done, although the logic's been moved into a funct
| |
| 166 self._chrome_version_utility.GetBranchNumberForVersion(version)) | |
| 167 names = self._GetAllNames( | |
| 168 svn_file_system.ReadSingle('%s/' % svn_constants.API_PATH)) | |
| 169 self._object_store.Set(str(version), names) | |
| 170 return api_name in names | |
| 171 | |
| 172 def _CheckExtensionAPIExistence(self, api_name, version): | |
| 173 '''Parses the extension_api.json file (available in earlier versions of | |
| 174 chrome) for an API namespace. If this is successfully found, then the API | |
| 175 is considered to have been 'stable' for the given version. | |
| 176 ''' | |
| 177 # The extension_api.json files only appear from version 5 to version 17. | |
| 178 assert(version <= 17 and version >= 5) | |
| 179 | |
| 180 compiled_fs = self._CreateCompiledFS( | |
| 181 self._chrome_version_utility.GetBranchNumberForVersion(version)) | |
| 182 try: | |
| 183 extension_api = self._GetJSONForExtensionAPI(api_name, compiled_fs) | |
| 184 api_rows = [row.get('namespace') for row in extension_api | |
| 185 if 'namespace' in row] | |
| 186 return True if api_name in api_rows else False | |
| 187 except FileNotFoundError as e: | |
| 188 # This should only happen on preview.py, since extension_api.json is no | |
| 189 # longer present in current versions. | |
| 190 return False | |
| 191 | |
| 192 def _FindEarliestAvailability(self, api_name, version): | |
| 193 '''Searches in descending order through filesystem caches tied to specific | |
| 194 chrome version numbers and looks for the availability of an API, |api_name|, | |
| 195 on the stable channel. When a version is found where the API is no longer | |
| 196 available on stable, returns the previous version number (the last known | |
| 197 version where the API was stable). | |
| 198 ''' | |
| 199 # The _permission_features.json file (with 'channel' keys) is present only | |
| 200 # in Chrome 20 and onwards. We'll also check the _manifest_features.json if | |
| 201 # an API isn't found in _permission_features, and, failing that, we'll check | |
| 202 # for the API's existence in the filesystem. | |
| 203 while version >= 20: | |
| 204 if ((api_name in self._permission_apis | |
| 205 and self._CheckChannelPermissionExistence(api_name, | |
| 206 version, | |
| 207 'stable')) | |
| 208 or (api_name in self._manifest_apis | |
| 209 and self._CheckManifestExistence(api_name, version)) | |
| 210 or (api_name in self._orphan_apis | |
| 211 and self._CheckFileSystemExistence(api_name, version))): | |
| 212 version -= 1 | |
| 213 else: | |
| 214 return version + 1 | |
|
not at google - send to devlin
2013/05/15 08:15:00
pls see earlier comments about simplifying the ret
| |
| 215 # These versions are a little troublesome. Version 19 has | |
| 216 # _permission_features.json, but it lacks 'channel' information. Version 18 | |
| 217 # doesn't have either of the json files. For now, we're using a simple check | |
| 218 # for filesystem existence here. | |
| 219 while version >= 18: | |
| 220 if self._CheckFileSystemExistence(api_name, version): | |
| 221 version -= 1 | |
| 222 else: | |
| 223 return version + 1 | |
| 224 # Versions 17 and earlier have an extension_api.json file which contains | |
| 225 # namespaces for each API that was available at the time. We can use this | |
| 226 # file to check for existence; however, we no longer have | |
| 227 # file system access after version 5, so the search stops there. | |
| 228 while version >= 5 and self._CheckExtensionAPIExistence(api_name, version): | |
| 229 version -= 1 | |
| 230 return version + 1 | |
| 231 | |
| 232 def _IsAvailable(self, api_name, version): | |
|
not at google - send to devlin
2013/05/15 08:15:00
yes, except pass this an api and a file system, no
epeterson
2013/06/02 00:25:50
The functions for checking existence now take a fi
| |
| 233 return ((api_name in self._permission_apis | |
| 234 and self._CheckSimplePermissionExistence(api_name, version)) | |
| 235 or (api_name in self._manifest_apis | |
| 236 and self._CheckManifestExistence(api_name, version)) | |
| 237 or (api_name in self._orphan_apis | |
| 238 and self._CheckFileSystemExistence(api_name, version))) | |
| 239 | |
| 240 def GetApiAvailability(self, api_name): | |
| 241 '''Determines the availability for an API by testing several scenarios. | |
| 242 (i.e. Is the API experimental? Only available on certain development | |
| 243 channels? If it's stable, when did it first become stable? etc.) | |
| 244 ''' | |
| 245 availability = self._object_store.Get(api_name).Get() | |
| 246 if availability is not None: | |
| 247 return self.AvailabilityInfo(availability[0], availability[1]) | |
| 248 | |
| 249 # Check for a predetermined availability for this API. | |
| 250 availability = self._GetExistingAvailability(api_name, self._json_cache) | |
| 251 if availability is not None: | |
| 252 self._object_store.Set(api_name, availability) | |
| 253 return self.AvailabilityInfo(availability[0], availability[1]) | |
|
not at google - send to devlin
2013/05/15 08:15:00
this block of code seems to be doing the same as t
epeterson
2013/06/02 00:25:50
The previous block is checking the object_store, w
| |
| 254 | |
| 255 latest_version = self._chrome_version_utility.GetLatestVersionNumber() | |
| 256 | |
| 257 # TODO(epeterson) This shouldn't be done upfront | |
|
not at google - send to devlin
2013/05/15 08:15:00
yeah I think this block of code can basically be d
epeterson
2013/06/02 00:25:50
Done.
| |
| 258 # There are three different scenarios for APIs in Chrome 20 and up. They | |
| 259 # can be in _permission_features, _manifest_features, or neither of the two. | |
| 260 # APIs are added to a respective list depending on which of these scenarios | |
| 261 # they fall into within the trunk filesystem. | |
| 262 if self._CheckSimplePermissionExistence(api_name, 'trunk'): | |
| 263 # This is a special case: availability can be any of the dev channels. | |
| 264 # First, check for this case before checking for stable existence. | |
| 265 self._permission_apis.append(api_name) | |
| 266 elif self._CheckManifestExistence(api_name, 'trunk'): | |
| 267 self._manifest_apis.append(api_name) | |
| 268 elif self._CheckFileSystemExistence(api_name, 'trunk'): | |
| 269 self._orphan_apis.append(api_name) | |
| 270 else: | |
| 271 raise FileNotFoundError( | |
| 272 'The API schema for %s could not be located.' % api_name) | |
| 273 | |
| 274 # Channel: availability[0]; Version: availability[1] | |
| 275 availability = ['trunk', latest_version] | |
| 276 for channel in BranchUtility.GetAllChannelNames(): | |
| 277 channel_info = self._branch_utility.GetChannelInfo(channel) | |
| 278 # TODO(epeterson) unicode to ints in branch_utility | |
| 279 if self._IsAvailable(api_name, availability[1]): | |
| 280 availability[0] = channel | |
| 281 if channel_info.version == 'trunk': | |
| 282 availability[1] = 'trunk' | |
| 283 else: | |
| 284 availability[1] = int(channel_info.version) | |
| 285 break | |
|
not at google - send to devlin
2013/05/15 08:15:00
this all looks very complicated?
I think it can b
epeterson
2013/06/02 00:25:50
Done.
| |
| 286 if availability[0] == 'stable': | |
| 287 availability[1] = self._FindEarliestAvailability( | |
| 288 api_name, | |
| 289 int(availability[1])) | |
| 290 | |
| 291 self._object_store.Set(api_name, availability) | |
| 292 return self.AvailabilityInfo(availability[0], availability[1]) | |
| OLD | NEW |