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 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 |