Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(226)

Side by Side Diff: chrome/common/extensions/docs/server2/availability_finder.py

Issue 17397010: Adding AvailabilityFinder to Doc Server (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fixes, Prettifying canned_data Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 compiled_file_system import CompiledFileSystem
10 from file_system import FileNotFoundError
11 import svn_constants
12 from third_party.json_schema_compiler import json_parse, model
13 from third_party.json_schema_compiler.memoize import memoize
14
15 _API_AVAILABILITIES = svn_constants.JSON_PATH + '/api_availabilities.json'
16 _API_FEATURES = svn_constants.API_PATH + '/_api_features.json'
17 _EXTENSION_API = svn_constants.API_PATH + '/extension_api.json'
18 _MANIFEST_FEATURES = svn_constants.API_PATH + '/_manifest_features.json'
19 _PERMISSION_FEATURES = svn_constants.API_PATH + '/_permission_features.json'
20 _STABLE = 'stable'
21
22 class AvailabilityInfo(object):
23 def __init__(self, channel, version):
24 self.channel = channel
25 self.version = version
26
27 def _GetChannelFromFeatures(api_name, file_system, path):
28 '''Finds API channel information within _features.json files at the given
29 |path| for the given |file_system|. Returns None if channel information for
30 the API cannot be located.
31 '''
32 feature = file_system.GetFromFile(path).get(api_name)
33
34 if feature is None:
35 return None
36 if isinstance(feature, collections.Mapping):
37 # The channel information dict is nested within a list for whitelisting
38 # purposes.
39 return feature.get('channel')
40 # Features can contain a list of entries. Take the newest branch.
41 return BranchUtility.NewestChannel(entry.get('channel')
42 for entry in feature)
43
44 def _GetChannelFromApiFeatures(api_name, file_system):
45 try:
46 return _GetChannelFromFeatures(api_name, file_system, _API_FEATURES)
47 except FileNotFoundError as e:
48 # TODO(epeterson) Remove except block once _api_features is in all channels.
49 return None
50
51 def _GetChannelFromPermissionFeatures(api_name, file_system):
52 return _GetChannelFromFeatures(api_name, file_system, _PERMISSION_FEATURES)
53
54 def _GetChannelFromManifestFeatures(api_name, file_system):
55 return _GetChannelFromFeatures(#_manifest_features uses unix_style API names
56 model.UnixName(api_name),
57 file_system,
58 _MANIFEST_FEATURES)
59
60 def _ExistsInFileSystem(api_name, file_system):
61 '''Checks for existence of |api_name| within the list of files in the api/
62 directory found using the given file system.
63 '''
64 file_names = file_system.GetFromFileListing(svn_constants.API_PATH)
65 # File names switch from unix_hacker_style to camelCase at versions <= 20.
66 return model.UnixName(api_name) in file_names or api_name in file_names
67
68 def _ExistsInExtensionApi(api_name, file_system):
69 '''Parses the api/extension_api.json file (available in Chrome versions
70 before 18) for an API namespace. If this is successfully found, then the API
71 is considered to have been 'stable' for the given version.
72 '''
73 try:
74 extension_api_json = file_system.GetFromFile(_EXTENSION_API)
75 api_rows = [row.get('namespace') for row in extension_api_json
76 if 'namespace' in row]
77 return True if api_name in api_rows else False
78 except FileNotFoundError as e:
79 # This should only happen on preview.py since extension_api.json is no
80 # longer present in trunk.
81 return False
82
83 class AvailabilityFinder(object):
84 '''Uses API data sources generated by a ChromeVersionDataSource in order to
85 search the filesystem for the earliest existence of a specified API throughout
86 the different versions of Chrome; this constitutes an API's availability.
87 '''
88 class Factory(object):
89 def __init__(self,
90 object_store_creator,
91 compiled_host_fs_factory,
92 branch_utility,
93 # Takes a |version|, and returns a caching offline or online
94 # subversion file system for that version.
95 create_file_system_at_version):
96 self._object_store_creator = object_store_creator
97 self._compiled_host_fs_factory = compiled_host_fs_factory
98 self._branch_utility = branch_utility
99 self._create_file_system_at_version = create_file_system_at_version
100
101 def Create(self):
102 return AvailabilityFinder(self._object_store_creator,
103 self._compiled_host_fs_factory,
104 self._branch_utility,
105 self._create_file_system_at_version)
106
107 def __init__(self,
108 object_store_creator,
109 compiled_host_fs_factory,
110 branch_utility,
111 create_file_system_at_version):
112 self._object_store_creator = object_store_creator
113 self._json_cache = compiled_host_fs_factory.Create(
114 lambda _, json: json_parse.Parse(json),
115 AvailabilityFinder,
116 'json-cache')
117 self._branch_utility = branch_utility
118 self._create_file_system_at_version = create_file_system_at_version
119 self._object_store = object_store_creator.Create(AvailabilityFinder)
120
121 @memoize
122 def _CreateFeaturesAndNamesFileSystems(self, version):
123 '''The 'features' compiled file system's populate function parses and
124 returns the contents of a _features.json file. The 'names' compiled file
125 system's populate function creates a list of file names with .json or .idl
126 extensions.
127 '''
128 fs_factory = CompiledFileSystem.Factory(
129 self._create_file_system_at_version(version),
130 self._object_store_creator)
131 features_fs = fs_factory.Create(lambda _, json: json_parse.Parse(json),
132 AvailabilityFinder,
133 category='features')
134 names_fs = fs_factory.Create(self._GetExtNames,
135 AvailabilityFinder,
136 category='names')
137 return (features_fs, names_fs)
138
139 def _GetExtNames(self, base_path, apis):
140 return [os.path.splitext(api)[0] for api in apis
141 if os.path.splitext(api)[1][1:] in ['json', 'idl']]
142
143 def _FindEarliestStableAvailability(self, api_name, version):
144 '''Searches in descending order through filesystem caches tied to specific
145 chrome version numbers and looks for the availability of an API, |api_name|,
146 on the stable channel. When a version is found where the API is no longer
147 available on stable, returns the previous version number (the last known
148 version where the API was stable).
149 '''
150 available = True
151 while available:
152 if version < 5:
153 # SVN data isn't available below version 5.
154 return version + 1
155 available = False
156 features_fs, names_fs = self._CreateFeaturesAndNamesFileSystems(version)
157 if version >= 28:
158 # The _api_features.json file first appears in version 28 and should be
159 # the most reliable for finding API availabilities, so it gets checked
160 # first. The _permission_features.json and _manifest_features.json files
161 # are present in Chrome 20 and onwards. Fall back to a check for file
162 # system existence if the API is not stable in any of the _features.json
163 # files.
164 available = _GetChannelFromApiFeatures(api_name, features_fs) == _STABLE
165 if version >= 20:
166 # Check other _features.json files/file existence if the API wasn't
167 # found in _api_features.json, or if _api_features.json wasn't present.
168 available = available or (
169 _GetChannelFromPermissionFeatures(api_name, features_fs) == _STABLE
170 or _GetChannelFromManifestFeatures(api_name, features_fs) == _STABLE
171 or _ExistsInFileSystem(api_name, names_fs))
172 elif version >= 18:
173 # These versions are a little troublesome. Version 19 has
174 # _permission_features.json, but it lacks 'channel' information.
175 # Version 18 lacks all of the _features.json files. For now, we're using
176 # a simple check for filesystem existence here.
177 available = _ExistsInFileSystem(api_name, names_fs)
178 elif version >= 5:
179 # Versions 17 and down to 5 have an extension_api.json file which
180 # contains namespaces for each API that was available at the time. We
181 # can use this file to check for API existence.
182 available = _ExistsInExtensionApi(api_name, features_fs)
183
184 if not available:
185 return version + 1
186 version -= 1
187
188 def _GetAvailableChannelForVersion(self, api_name, version):
189 '''Searches through the _features files for a given |version| and returns
190 the channel that the given API is determined to be available on.
191 '''
192 features_fs, names_fs = self._CreateFeaturesAndNamesFileSystems(version)
193 channel = (_GetChannelFromApiFeatures(api_name, features_fs)
194 or _GetChannelFromPermissionFeatures(api_name, features_fs)
195 or _GetChannelFromManifestFeatures(api_name, features_fs))
196 if channel is None and _ExistsInFileSystem(api_name, names_fs):
197 # If an API is not represented in any of the _features files, but exists
198 # in the filesystem, then assume it is available in this version.
199 # The windows API is an example of this.
200 return self._branch_utility.GetChannelForVersion(version)
201
202 for channel_info in self._branch_utility.GetAllChannelInfo():
not at google - send to devlin 2013/06/21 00:54:11 forgot to delete this :)
epeterson 2013/06/22 01:42:40 Oops.
203 if version == channel_info.version:
204 channel = channel_info.channel
205 break
206 return channel
207
208 def GetApiAvailability(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 api_info = self._json_cache.GetFromFile(_API_AVAILABILITIES).get(api_name)
219 if api_info is not None:
220 channel = api_info.get('channel')
221 return AvailabilityInfo(
222 channel,
223 api_info.get('version') if channel == _STABLE else None)
224
225 # Check for the API in the development channels.
226 availability = None
227 for channel_info in self._branch_utility.GetAllChannelInfo():
228 available_channel = self._GetAvailableChannelForVersion(
229 api_name,
230 channel_info.version)
231 # If the |available_channel| for the API is the same as, or older than,
232 # the channel we're checking, then the API is available on this channel.
233 if (available_channel is not None and
234 BranchUtility.NewestChannel((available_channel, channel_info.channel))
235 == channel_info.channel):
236 availability = AvailabilityInfo(channel_info.channel,
237 channel_info.version)
238 break
239
240 # The API should at least be available on trunk. It's a bug otherwise.
241 assert availability, 'No availability found for %s' % api_name
242
243 # If the API is in stable, find the chrome version in which it became
244 # stable.
245 if availability.channel == _STABLE:
246 availability.version = self._FindEarliestStableAvailability(
247 api_name,
248 availability.version)
249
250 self._object_store.Set(api_name, availability)
251 return availability
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698