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 file_system import FileNotFoundError | |
| 11 import svn_constants | |
| 12 from third_party.json_schema_compiler import json_parse, model | |
| 13 | |
| 14 EXTENSION_API = 'extension_api.json' | |
| 15 MANIFEST_FEATURES = '_manifest_features.json' | |
| 16 PERMISSION_FEATURES = '_permission_features.json' | |
| 17 | |
| 18 class AvailabilityDataSource(object): | |
|
not at google - send to devlin
2013/05/13 21:26:41
Late stage for this, but this isn't actually a *Da
epeterson
2013/05/15 07:38:34
Done.
| |
| 19 '''Uses API data sources generated by a ChromeVersionDataSource in order to | |
| 20 search the filesystem for the earliest existence of a specified API throughout | |
| 21 the different versions of Chrome; this constitutes an API's availability. | |
| 22 ''' | |
| 23 class Factory(object): | |
| 24 def __init__(self, | |
| 25 chrome_version_utility, | |
| 26 object_store_creator, | |
| 27 create_file_system): | |
|
not at google - send to devlin
2013/05/13 21:26:41
Can we try making this a method of ChromeVersionUt
| |
| 28 self._chrome_version_utility = chrome_version_utility | |
| 29 self._object_store_creator = object_store_creator | |
| 30 self._create_file_system = create_file_system | |
| 31 | |
| 32 def Create(self): | |
| 33 return AvailabilityDataSource(self._chrome_version_utility, | |
| 34 self._object_store_creator, | |
| 35 self._create_file_system) | |
| 36 | |
| 37 def __init__(self, | |
| 38 chrome_version_utility, | |
| 39 object_store_creator, | |
| 40 create_file_system): | |
| 41 self._chrome_version_utility = chrome_version_utility | |
| 42 self._object_store_creator = object_store_creator | |
| 43 self._create_file_system = create_file_system | |
| 44 | |
| 45 svn_file_system = create_file_system('trunk') | |
| 46 def read_json(path): | |
| 47 return json_parse.Parse(svn_file_system.ReadSingle(path)) | |
| 48 self._existing_availabilities = read_json( | |
| 49 '%s/api_availabilities.json' % svn_constants.JSON_PATH) | |
| 50 self._permission_features = read_json( | |
| 51 '%s/%s' % (svn_constants.API_PATH, PERMISSION_FEATURES)) | |
| 52 self._manifest_features = read_json( | |
| 53 '%s/%s' % (svn_constants.API_PATH, MANIFEST_FEATURES)) | |
|
not at google - send to devlin
2013/05/13 15:43:32
here is one problem. The file systems are being re
not at google - send to devlin
2013/05/13 21:26:41
Continuing from this comment, and reading below: I
| |
| 54 | |
| 55 def create_object_store(category): | |
| 56 return (object_store_creator.Create(AvailabilityDataSource, | |
| 57 category=category)) | |
| 58 self._permission_object_store = create_object_store('permission') | |
| 59 self._manifest_object_store = create_object_store('manifest') | |
| 60 self._names_object_store = create_object_store('names') | |
| 61 self._object_store = create_object_store(None) | |
| 62 | |
| 63 self._permission_apis = [] | |
| 64 self._manifest_apis = [] | |
| 65 self._orphan_apis = [] | |
|
not at google - send to devlin
2013/05/13 21:26:41
Looking at the way this is used, it's a bit strang
| |
| 66 | |
| 67 def _ReadJson(self, svn_file_system, path): | |
| 68 return json_parse.Parse(svn_file_system.ReadSingle(path)) | |
| 69 | |
| 70 def _GetChannelFromPermissionFeatures(self, api_name, permission_features): | |
|
not at google - send to devlin
2013/05/13 21:26:41
this function should be able to use self._permissi
epeterson
2013/05/15 07:38:34
Done.
| |
| 71 '''Handles finding API channel information from _permission_features.json. | |
| 72 Sometimes this info will be in a dict, but other times it will be | |
| 73 in a dict contained in a list. | |
| 74 ''' | |
| 75 channel = None | |
| 76 api_info = permission_features.get(api_name) | |
| 77 if api_info is not None: | |
| 78 if isinstance(api_info, collections.Mapping): | |
| 79 channel = api_info.get('channel') | |
| 80 else: | |
| 81 channel = BranchUtility.NewestChannel([entry.get('channel') | |
| 82 for entry in api_info]) | |
|
not at google - send to devlin
2013/05/13 21:26:41
I don't think that the list comprehension is neces
epeterson
2013/05/15 07:38:34
Done.
| |
| 83 return channel | |
| 84 | |
| 85 def _CheckStablePermissionExistence(self, api_name, version): | |
| 86 '''Checks _permission_features.json for the given |version| of Chrome, | |
| 87 and returns a boolean representing whether or not the API was available on | |
| 88 the stable channel in |version|. | |
| 89 ''' | |
| 90 existence = self._permission_object_store.Get( | |
| 91 '%s%s' % (api_name, version)).Get() | |
| 92 if existence is not None: | |
| 93 return existence | |
| 94 svn_file_system = self._create_file_system( | |
| 95 self._chrome_version_utility.GetBranchNumberForVersion(version)) | |
| 96 permission_features = self._ReadJson( | |
| 97 svn_file_system, | |
| 98 '%s/%s' % (svn_constants.API_PATH, PERMISSION_FEATURES)) | |
| 99 existence = 'stable' == self._GetChannelFromPermissionFeatures( | |
| 100 api_name, | |
| 101 permission_features) | |
| 102 self._object_store.Set('%s%s' % (api_name, version), existence) | |
| 103 return existence | |
| 104 | |
| 105 def _CheckManifestExistence(self, api_name, version): | |
| 106 '''As a fallback measure for an API not being found in _permission_features, | |
| 107 check _manifest_features.json for the given |version| of Chrome, | |
| 108 and return a boolean representing whether or not the API was represented | |
| 109 within the file. | |
| 110 ''' | |
| 111 #_manifest_features.json uses unix_hacker_style API names. | |
| 112 api_name = model.UnixName(api_name) | |
| 113 existence = self._manifest_object_store.Get( | |
| 114 '%s%s' % (api_name, version)).Get() | |
| 115 if existence is not None: | |
| 116 return existence | |
| 117 svn_file_system = self._create_file_system( | |
| 118 self._chrome_version_utility.GetBranchNumberForVersion(version)) | |
| 119 manifest_features = self._ReadJson( | |
| 120 svn_file_system, | |
| 121 '%s/%s' % (svn_constants.API_PATH, MANIFEST_FEATURES)) | |
| 122 if manifest_features.get(api_name) is not None: | |
| 123 existence = True | |
| 124 else: | |
| 125 existence = False | |
|
not at google - send to devlin
2013/05/13 21:26:41
existence = api_name in manifest_features
epeterson
2013/05/15 07:38:34
Done.
| |
| 126 self._object_store.Set('%s%s' % (api_name, version), existence) | |
| 127 return existence | |
| 128 | |
| 129 def _GetAllNames(self, files_dict): | |
| 130 '''Returns extension-less names for a list of files. | |
| 131 ''' | |
| 132 files = files_dict[svn_constants.API_PATH + '/'] | |
| 133 return [os.path.splitext(f)[0] for f in files] | |
| 134 | |
| 135 def _CheckFileSystemExistence(self, api_name, version): | |
| 136 '''Uses a list of API names for a given branch to check simply for the | |
| 137 existence of a given API in that branch. | |
| 138 ''' | |
| 139 names = self._names_object_store.Get(version).Get() | |
| 140 if names is not None: | |
| 141 return api_name in names | |
| 142 svn_file_system = self._create_file_system( | |
| 143 self._chrome_version_utility.GetBranchNumberForVersion(version)) | |
| 144 names = svn_file_system.Read([svn_constants.API_PATH + '/']).Get() | |
|
not at google - send to devlin
2013/05/13 21:26:41
ReadSingle
search in this file for ".Read(" and s
epeterson
2013/05/15 07:38:34
Done.
| |
| 145 names = self._GetAllNames(names) | |
| 146 self._object_store.Set(version, names) | |
| 147 return api_name in names | |
| 148 | |
| 149 def _CheckExtensionAPIExistence(self, api_name, version): | |
| 150 '''Parses the extension_api.json file (available in earlier versions of | |
| 151 chrome) for an API namespace. If this is successfully found, then the API | |
| 152 is considered to have been 'stable' for the given version. | |
| 153 ''' | |
| 154 # The extension_api.json files only appear from version 5 to version 17. | |
| 155 if int(version) > 17 or int(version) < 5: | |
|
not at google - send to devlin
2013/05/13 21:26:41
make these assert, this method should only be call
epeterson
2013/05/15 07:38:34
Done.
| |
| 156 return False | |
| 157 | |
| 158 # Re-use this object-store - the versions won't conflict with each other. | |
| 159 existence = self._names_object_store.Get('%s%s' % (api_name, version)).Get() | |
|
not at google - send to devlin
2013/05/13 21:26:41
I don't think caching needs to be done here, you'r
epeterson
2013/05/15 07:38:34
Done.
| |
| 160 if existence is not None: | |
| 161 return existence | |
| 162 svn_file_system = self._create_file_system( | |
| 163 self._chrome_version_utility.GetBranchNumberForVersion(version)) | |
| 164 try: | |
| 165 extension_api = self._ReadJson( | |
| 166 svn_file_system, | |
| 167 '%s/%s' % (svn_constants.API_PATH, EXTENSION_API)) | |
| 168 api_rows = [row.get('namespace') for row in extension_api | |
| 169 if 'namespace' in row] | |
| 170 return True if api_name in api_rows else False | |
| 171 except FileNotFoundError as e: | |
| 172 # This should only happen on preview.py, since extension_api.json is no | |
| 173 # longer present in current versions. | |
| 174 return False | |
| 175 | |
| 176 def _FindEarliestStableAvailability(self, api_name, version): | |
| 177 '''Searches in descending order through filesystem caches tied to specific | |
| 178 chrome version numbers and looks for the availability of an API, |api_name|, | |
| 179 on the stable channel. When a version is found where the API is no longer | |
| 180 available on stable, returns the previous version number (the last known | |
| 181 version where the API was stable). | |
| 182 ''' | |
| 183 # The _permission_features.json file (with 'channel' keys) is present only | |
| 184 # in Chrome 20 and onwards. We'll also check the _manifest_features.json if | |
| 185 # an API isn't found in _permission_features, and, failing that, we'll check | |
| 186 # for the API's existence in the filesystem. | |
| 187 while version >= 20: | |
| 188 if ((api_name in self._permission_apis | |
| 189 and self._CheckStablePermissionExistence(api_name, str(version))) | |
| 190 or (api_name in self._manifest_apis | |
| 191 and self._CheckManifestExistence(api_name, str(version))) | |
| 192 or (api_name in self._orphan_apis | |
| 193 and self._CheckFileSystemExistence(api_name, str(version)))): | |
| 194 version -= 1 | |
| 195 else: | |
| 196 break | |
|
not at google - send to devlin
2013/05/13 21:26:41
THis method is super cool, but a bit odd to read a
epeterson
2013/05/15 07:38:34
Done.
| |
| 197 # These versions are a little troublesome. Version 19 has | |
| 198 # _permission_features.json, but it lacks 'channel' information. Version 18 | |
| 199 # doesn't have either of the json files. For now, we're using a simple check | |
| 200 # for filesystem existence here. | |
| 201 while version < 20 and version >= 18: | |
| 202 if self._CheckFileSystemExistence(api_name, str(version)): | |
| 203 version -= 1 | |
| 204 else: | |
| 205 break | |
| 206 # Versions 17 and earlier have an extension_api.json file which contains | |
| 207 # namespaces for each API that was available at the time. We can use this | |
| 208 # file to check for existence, however, we no longer have | |
| 209 # file system access after version 5, so stop there. | |
| 210 while version < 18 and version >= 5: | |
| 211 if self._CheckExtensionAPIExistence(api_name, str(version)): | |
|
not at google - send to devlin
2013/05/13 21:26:41
why str(version)? Can we use ints everywhere?
epeterson
2013/05/15 07:38:34
Done.
| |
| 212 version -= 1 | |
| 213 else: | |
| 214 break | |
| 215 return str(version + 1) | |
| 216 | |
| 217 def GetAvailability(self, api_name): | |
|
not at google - send to devlin
2013/05/13 21:26:41
I will want to add event/function/property availab
epeterson
2013/05/15 07:38:34
Done.
| |
| 218 '''Determines the availability for an API by testing several scenarios. | |
| 219 (i.e. Is the API experimental? Only available on certain development | |
| 220 channels? If it's stable, when did it first become stable? etc.) | |
| 221 ''' | |
| 222 availability = self._object_store.Get(api_name).Get() | |
| 223 if availability is not None: | |
| 224 return availability | |
| 225 | |
| 226 # Check for a predetermined availability for this API. | |
| 227 availability = self._existing_availabilities.get(api_name) | |
|
not at google - send to devlin
2013/05/13 21:26:41
e.g. in lazy creation world this would be self._Ge
epeterson
2013/05/15 07:38:34
Done.
| |
| 228 if availability is not None: | |
| 229 self._object_store.Set(api_name, availability) | |
| 230 return availability | |
| 231 | |
| 232 latest_version = str(self._chrome_version_utility.GetLatestVersionNumber()) | |
| 233 channel = self._GetChannelFromPermissionFeatures(api_name, | |
| 234 self._permission_features) | |
|
not at google - send to devlin
2013/05/13 21:26:41
This won't actually work properly, though: say if
epeterson
2013/05/15 07:38:34
So this is a bit of a mess now. Couldn't quite get
| |
| 235 | |
| 236 # There are three different scenarios for APIs in Chrome 20 and up. They | |
| 237 # can be in _permission_features, _manifest_features, or neither of the two. | |
| 238 # APIs are added to a respective list depending on which of these scenarios | |
| 239 # they fall into within the trunk filesystem. | |
| 240 if channel is not None: | |
| 241 # This is a special case: availability can be any of the dev channels. | |
| 242 # First, check for this case before checking for stable existence. | |
| 243 availability = channel | |
| 244 self._permission_apis.append(api_name) | |
| 245 elif self._CheckManifestExistence(api_name, latest_version): | |
| 246 availability = 'stable' | |
| 247 self._manifest_apis.append(api_name) | |
| 248 elif self._CheckFileSystemExistence(api_name, latest_version): | |
| 249 availability = 'stable' | |
| 250 self._orphan_apis.append(api_name) | |
| 251 else: | |
| 252 raise FileNotFoundError( | |
| 253 'The API schema for %s could not be located.' % api_name) | |
| 254 availability = None | |
| 255 | |
| 256 if availability == 'stable': | |
| 257 availability = self._FindEarliestStableAvailability( | |
| 258 api_name, | |
| 259 int(latest_version) - 1) | |
| 260 self._object_store.Set(api_name, availability) | |
| 261 return availability | |
| OLD | NEW |