availability_finder.py revision 424c4d7b64af9d0d8fd9624f381f469654d5e3d2
1# Copyright 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 5import collections 6import os 7 8import svn_constants 9from branch_utility import BranchUtility 10from compiled_file_system import CompiledFileSystem 11from file_system import FileNotFoundError 12from third_party.json_schema_compiler import json_parse 13from third_party.json_schema_compiler.memoize import memoize 14from third_party.json_schema_compiler.model import UnixName 15 16 17def _GetChannelFromFeatures(api_name, file_system, path): 18 '''Finds API channel information within _features.json files at the given 19 |path| for the given |file_system|. Returns None if channel information for 20 the API cannot be located. 21 ''' 22 feature = file_system.GetFromFile(path).get(api_name) 23 24 if feature is None: 25 return None 26 if isinstance(feature, collections.Mapping): 27 # The channel information exists as a solitary dict. 28 return feature.get('channel') 29 # The channel information dict is nested within a list for whitelisting 30 # purposes. Take the newest channel out of all of the entries. 31 return BranchUtility.NewestChannel(entry.get('channel') for entry in feature) 32 33 34def _GetChannelFromApiFeatures(api_name, file_system): 35 return _GetChannelFromFeatures( 36 api_name, 37 file_system, 38 '%s/_api_features.json' % svn_constants.API_PATH) 39 40 41def _GetChannelFromManifestFeatures(api_name, file_system): 42 return _GetChannelFromFeatures( 43 UnixName(api_name), #_manifest_features uses unix_style API names 44 file_system, 45 '%s/_manifest_features.json' % svn_constants.API_PATH) 46 47 48def _GetChannelFromPermissionFeatures(api_name, file_system): 49 return _GetChannelFromFeatures( 50 api_name, 51 file_system, 52 '%s/_permission_features.json' % svn_constants.API_PATH) 53 54 55def _ExistsInExtensionApi(api_name, file_system): 56 '''Parses the api/extension_api.json file (available in Chrome versions 57 before 18) for an API namespace. If this is successfully found, then the API 58 is considered to have been 'stable' for the given version. 59 ''' 60 try: 61 extension_api_json = file_system.GetFromFile( 62 '%s/extension_api.json' % svn_constants.API_PATH) 63 api_rows = [row.get('namespace') for row in extension_api_json 64 if 'namespace' in row] 65 return api_name in api_rows 66 except FileNotFoundError: 67 # This should only happen on preview.py since extension_api.json is no 68 # longer present in trunk. 69 return False 70 71 72class AvailabilityFinder(object): 73 '''Generates availability information for APIs by looking at API schemas and 74 _features files over multiple release versions of Chrome. 75 ''' 76 77 def __init__(self, 78 file_system_iterator, 79 object_store_creator, 80 branch_utility): 81 self._file_system_iterator = file_system_iterator 82 self._object_store_creator = object_store_creator 83 self._object_store = self._object_store_creator.Create(AvailabilityFinder) 84 self._branch_utility = branch_utility 85 86 def _ExistsInFileSystem(self, api_name, file_system): 87 '''Checks for existence of |api_name| within the list of api files in the 88 api/ directory found using the given |file_system|. 89 ''' 90 file_names = file_system.ReadSingle('%s/' % svn_constants.API_PATH) 91 api_names = tuple(os.path.splitext(name)[0] for name in file_names 92 if os.path.splitext(name)[1][1:] in ['json', 'idl']) 93 94 # API file names in api/ are unix_name at every version except for versions 95 # 18, 19, and 20. Since unix_name is the more common format, check it first. 96 return (UnixName(api_name) in api_names) or (api_name in api_names) 97 98 def _CheckStableAvailability(self, api_name, file_system, version): 99 '''Checks for availability of an API, |api_name|, on the stable channel. 100 Considers several _features.json files, file system existence, and 101 extension_api.json depending on the given |version|. 102 ''' 103 if version < 5: 104 # SVN data isn't available below version 5. 105 return False 106 available_channel = None 107 fs_factory = CompiledFileSystem.Factory(file_system, 108 self._object_store_creator) 109 features_fs = fs_factory.Create(lambda _, json: json_parse.Parse(json), 110 AvailabilityFinder, 111 category='features') 112 if version >= 28: 113 # The _api_features.json file first appears in version 28 and should be 114 # the most reliable for finding API availability. 115 available_channel = _GetChannelFromApiFeatures(api_name, features_fs) 116 if version >= 20: 117 # The _permission_features.json and _manifest_features.json files are 118 # present in Chrome 20 and onwards. Use these if no information could be 119 # found using _api_features.json. 120 available_channel = available_channel or ( 121 _GetChannelFromPermissionFeatures(api_name, features_fs) 122 or _GetChannelFromManifestFeatures(api_name, features_fs)) 123 if available_channel is not None: 124 return available_channel == 'stable' 125 if version >= 18: 126 # Fall back to a check for file system existence if the API is not 127 # stable in any of the _features.json files, OR if we're dealing with 128 # version 18 or 19, which don't contain relevant _features information. 129 return self._ExistsInFileSystem(api_name, file_system) 130 if version >= 5: 131 # Versions 17 down to 5 have an extension_api.json file which 132 # contains namespaces for each API that was available at the time. 133 return _ExistsInExtensionApi(api_name, features_fs) 134 135 def _CheckChannelAvailability(self, api_name, file_system, channel_name): 136 '''Searches through the _features files in a given |file_system| and 137 determines whether or not an API is available on the given channel, 138 |channel_name|. 139 ''' 140 fs_factory = CompiledFileSystem.Factory(file_system, 141 self._object_store_creator) 142 features_fs = fs_factory.Create(lambda _, json: json_parse.Parse(json), 143 AvailabilityFinder, 144 category='features') 145 available_channel = (_GetChannelFromApiFeatures(api_name, features_fs) 146 or _GetChannelFromPermissionFeatures(api_name, features_fs) 147 or _GetChannelFromManifestFeatures(api_name, features_fs)) 148 if (available_channel is None and 149 self._ExistsInFileSystem(api_name, file_system)): 150 # If an API is not represented in any of the _features files, but exists 151 # in the filesystem, then assume it is available in this version. 152 # The windows API is an example of this. 153 available_channel = channel_name 154 # If the channel we're checking is the same as or newer than the 155 # |available_channel| then the API is available at this channel. 156 return (available_channel is not None and 157 BranchUtility.NewestChannel((available_channel, channel_name)) 158 == channel_name) 159 160 def _CheckApiAvailability(self, api_name, file_system, channel_info): 161 '''Determines the availability for an API at a certain version of Chrome. 162 Two branches of logic are used depending on whether or not the API is 163 determined to be 'stable' at the given version. 164 ''' 165 if channel_info.channel == 'stable': 166 return self._CheckStableAvailability(api_name, 167 file_system, 168 channel_info.version) 169 return self._CheckChannelAvailability(api_name, 170 file_system, 171 channel_info.channel) 172 173 def GetApiAvailability(self, api_name): 174 '''Performs a search for an API's top-level availability by using a 175 HostFileSystemIterator instance to traverse multiple version of the 176 SVN filesystem. 177 ''' 178 availability = self._object_store.Get(api_name).Get() 179 if availability is not None: 180 return availability 181 182 def check_api_availability(file_system, channel_info): 183 return self._CheckApiAvailability(api_name, file_system, channel_info) 184 185 availability = self._file_system_iterator.Descending( 186 self._branch_utility.GetChannelInfo('dev'), 187 check_api_availability) 188 if availability is None: 189 # The API wasn't available on 'dev', so it must be a 'trunk'-only API. 190 availability = self._branch_utility.GetChannelInfo('trunk') 191 self._object_store.Set(api_name, availability) 192 return availability 193