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