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

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

Issue 12996003: Dynamically generate a heading for Extension Docs API pages (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fixing up Offline/Online Access - Attempting to rework availability algorithm Created 7 years, 7 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 chrome_version_utility import ChromeVersionUtility
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 = 'api_availabilities.json'
16 EXTENSION_API = 'extension_api.json'
17 MANIFEST_FEATURES = '_manifest_features.json'
18 PERMISSION_FEATURES = '_permission_features.json'
not at google - send to devlin 2013/05/15 08:15:00 these are all private so should start with a _, an
epeterson 2013/06/02 00:25:50 Done.
19
20 def _GetChannelFromPermissionFeature(permission_feature):
21 '''Handles finding API channel information from _permission_features.json.
22 Sometimes this info will be in a dict, but other times it will be
23 in a dict contained in a list.
24 '''
25 channel = None
26 if permission_feature is not None:
27 if isinstance(permission_feature, collections.Mapping):
28 channel = permission_feature.get('channel')
29 else:
30 channel = BranchUtility.NewestChannel(entry.get('channel')
31 for entry in permission_feature)
32 return channel
33
34 class AvailabilityFinder(object):
35 '''Uses API data sources generated by a ChromeVersionDataSource in order to
36 search the filesystem for the earliest existence of a specified API throughout
37 the different versions of Chrome; this constitutes an API's availability.
38 '''
39 class AvailabilityInfo(object):
not at google - send to devlin 2013/05/15 08:15:00 this object is picklable, so it can be directly pu
epeterson 2013/06/02 00:25:50 Done. (I had tried this initially, but I think it
40 def __init__(self, channel, version):
41 self.channel = channel
42 self.version = version
43
44 class Factory(object):
45 def __init__(self,
46 chrome_version_utility,
47 object_store_creator,
48 compiled_host_fs_factory,
49 create_file_system):
50 self._chrome_version_utility = chrome_version_utility
51 self._object_store_creator = object_store_creator
52 self._compiled_host_fs_factory = compiled_host_fs_factory
53 self._create_file_system = create_file_system
54
55 def Create(self):
56 return AvailabilityFinder(self._chrome_version_utility,
57 self._object_store_creator,
58 self._compiled_host_fs_factory,
59 self._create_file_system)
60
61 def __init__(self,
62 chrome_version_utility,
63 object_store_creator,
64 compiled_host_fs_factory,
65 create_file_system):
66 self._chrome_version_utility = chrome_version_utility
67 self._object_store_creator = object_store_creator
68 self._json_cache = compiled_host_fs_factory.Create(self._ParseForCompiledFS,
not at google - send to devlin 2013/05/15 08:15:00 just pass in json_parse.Parse, no need to wrap it
epeterson 2013/06/02 00:25:50 compiled filesystems send a path and file data to
69 AvailabilityFinder,
70 'json-cache')
71 self._branch_utility = BranchUtility.Create(object_store_creator)
72 self._create_file_system = create_file_system
73 self._object_store = object_store_creator.Create(AvailabilityFinder)
74 self._permission_apis = []
not at google - send to devlin 2013/05/15 08:15:00 as I mentioned in IRC - holding onto these (permis
epeterson 2013/06/02 00:25:50 The intent was to use these to force APIs that wer
75 self._manifest_apis = []
76 self._orphan_apis = []
77
78 def _ParseForCompiledFS(self, path, json):
79 '''Wrapper method to be passed to compiled_file_systems upon creation.
80 '''
81 return json_parse.Parse(json)
82
83 def _CreateCompiledFS(self, branch):
84 # TODO(epeterson) Move create_file_system logic into ChromeVersionUtility.
not at google - send to devlin 2013/05/15 08:15:00 don't worry about it
85 compiled_fs_factory = CompiledFileSystem.Factory(
86 self._create_file_system(branch),
87 self._object_store_creator)
88 return compiled_fs_factory.Create(self._ParseForCompiledFS,
not at google - send to devlin 2013/05/15 08:15:00 ditto
epeterson 2013/06/02 00:25:50 Done.
89 AvailabilityFinder,
90 'branch')
91
92 def _GetExistingAvailability(self, api_name, compiled_fs):
93 api_info = compiled_fs.GetFromFile(
94 '%s/%s' % (svn_constants.JSON_PATH, API_AVAILABILITIES)).get(api_name)
95 if api_info is not None:
96 return [api_info.get('channel'), api_info.get('version')]
97 return None
not at google - send to devlin 2013/05/15 08:15:00 as I mentioned, AvailabilityFinder is picklable, s
epeterson 2013/06/02 00:25:50 Done.
98
99 def _GetPermissionFeature(self, api_name, compiled_fs):
100 return compiled_fs.GetFromFile(
101 '%s/%s' % (svn_constants.API_PATH, PERMISSION_FEATURES)).get(api_name)
102
103 def _GetManifestFeature(self, api_name, compiled_fs):
104 return compiled_fs.GetFromFile(
105 '%s/%s' % (svn_constants.API_PATH, MANIFEST_FEATURES)).get(api_name)
106
107 def _GetJSONForExtensionAPI(self, api_name, compiled_fs):
108 return compiled_fs.GetFromFile(
109 '%s/%s' % (svn_constants.API_PATH, EXTENSION_API))
110
111 def _ReadJson(self, svn_file_system, path):
not at google - send to devlin 2013/05/15 08:15:00 not used
epeterson 2013/06/02 00:25:50 Done.
112 return json_parse.Parse(svn_file_system.ReadSingle(path))
113
not at google - send to devlin 2013/05/15 08:15:00 extra \n
epeterson 2013/06/02 00:25:50 Done.
114
115 def _CheckStablePermissionExistence(self, api_name, version):
116 '''Checks _permission_features.json for the given |version| of Chrome,
117 and returns a boolean representing whether or not the API was available on
118 the stable channel in |version|.
119 '''
120 compiled_fs = self._CreateCompiledFS(
121 self._chrome_version_utility.GetBranchNumberForVersion(version))
122 permission_feature = self._GetPermissionFeature(api_name, compiled_fs)
123 existence = 'stable' == _GetChannelFromPermissionFeature(permission_feature)
124 return existence
125
126 def _CheckChannelPermissionExistence(self, api_name, version, channel):
127 compiled_fs = self._CreateCompiledFS(
128 self._chrome_version_utility.GetBranchNumberForVersion(version))
129 permission_feature = self._GetPermissionFeature(api_name, compiled_fs)
130 existence = channel == _GetChannelFromPermissionFeature(permission_feature)
131 return existence
132
not at google - send to devlin 2013/05/15 08:15:00 extra \n
epeterson 2013/06/02 00:25:50 Done.
133
134 def _CheckSimplePermissionExistence(self, api_name, version):
not at google - send to devlin 2013/05/15 08:15:00 looks like this method can be combined with checks
epeterson 2013/06/02 00:25:50 Logic has been moved up above.
135 compiled_fs = self._CreateCompiledFS(
136 self._chrome_version_utility.GetBranchNumberForVersion(version))
137 permission_feature = self._GetPermissionFeature(api_name, compiled_fs)
138 return permission_feature is not None
139
140 def _CheckManifestExistence(self, api_name, version):
not at google - send to devlin 2013/05/15 08:15:00 _ExistsInManifestFeatures, take a file system
epeterson 2013/06/02 00:25:50 Done.
141 '''As a fallback measure for an API not being found in _permission_features,
142 check _manifest_features.json for the given |version| of Chrome,
143 and return a boolean representing whether or not the API was represented
144 within the file.
145 '''
146 #_manifest_features.json uses unix_hacker_style API names.
147 api_name = model.UnixName(api_name)
148 compiled_fs = self._CreateCompiledFS(
149 self._chrome_version_utility.GetBranchNumberForVersion(version))
150 manifest_feature = self._GetManifestFeature(api_name, compiled_fs)
151 return manifest_feature is not None
152
153 def _GetAllNames(self, files):
154 '''Returns extension-less names for a list of files.
155 '''
156 return [os.path.splitext(f)[0] for f in files]
157
158 def _CheckFileSystemExistence(self, api_name, version):
not at google - send to devlin 2013/05/15 08:15:00 _ExistsInFileSystem, take a file system
epeterson 2013/06/02 00:25:50 Done.
159 '''Uses a list of API names for a given branch to check simply for the
160 existence of a given API in that branch.
161 '''
162 names = self._object_store.Get(str(version)).Get()
not at google - send to devlin 2013/05/15 08:15:00 shouldn't need to stringify version here
epeterson 2013/06/02 00:25:50 This has been removed completely. AvailabilityFind
163 if names is not None:
164 return api_name in names
165 svn_file_system = self._create_file_system(
not at google - send to devlin 2013/05/15 08:15:00 use the compiled file system for this instead?
epeterson 2013/06/02 00:25:50 Done, although the logic's been moved into a funct
166 self._chrome_version_utility.GetBranchNumberForVersion(version))
167 names = self._GetAllNames(
168 svn_file_system.ReadSingle('%s/' % svn_constants.API_PATH))
169 self._object_store.Set(str(version), names)
170 return api_name in names
171
172 def _CheckExtensionAPIExistence(self, api_name, version):
173 '''Parses the extension_api.json file (available in earlier versions of
174 chrome) for an API namespace. If this is successfully found, then the API
175 is considered to have been 'stable' for the given version.
176 '''
177 # The extension_api.json files only appear from version 5 to version 17.
178 assert(version <= 17 and version >= 5)
179
180 compiled_fs = self._CreateCompiledFS(
181 self._chrome_version_utility.GetBranchNumberForVersion(version))
182 try:
183 extension_api = self._GetJSONForExtensionAPI(api_name, compiled_fs)
184 api_rows = [row.get('namespace') for row in extension_api
185 if 'namespace' in row]
186 return True if api_name in api_rows else False
187 except FileNotFoundError as e:
188 # This should only happen on preview.py, since extension_api.json is no
189 # longer present in current versions.
190 return False
191
192 def _FindEarliestAvailability(self, api_name, version):
193 '''Searches in descending order through filesystem caches tied to specific
194 chrome version numbers and looks for the availability of an API, |api_name|,
195 on the stable channel. When a version is found where the API is no longer
196 available on stable, returns the previous version number (the last known
197 version where the API was stable).
198 '''
199 # The _permission_features.json file (with 'channel' keys) is present only
200 # in Chrome 20 and onwards. We'll also check the _manifest_features.json if
201 # an API isn't found in _permission_features, and, failing that, we'll check
202 # for the API's existence in the filesystem.
203 while version >= 20:
204 if ((api_name in self._permission_apis
205 and self._CheckChannelPermissionExistence(api_name,
206 version,
207 'stable'))
208 or (api_name in self._manifest_apis
209 and self._CheckManifestExistence(api_name, version))
210 or (api_name in self._orphan_apis
211 and self._CheckFileSystemExistence(api_name, version))):
212 version -= 1
213 else:
214 return version + 1
not at google - send to devlin 2013/05/15 08:15:00 pls see earlier comments about simplifying the ret
215 # These versions are a little troublesome. Version 19 has
216 # _permission_features.json, but it lacks 'channel' information. Version 18
217 # doesn't have either of the json files. For now, we're using a simple check
218 # for filesystem existence here.
219 while version >= 18:
220 if self._CheckFileSystemExistence(api_name, version):
221 version -= 1
222 else:
223 return version + 1
224 # Versions 17 and earlier have an extension_api.json file which contains
225 # namespaces for each API that was available at the time. We can use this
226 # file to check for existence; however, we no longer have
227 # file system access after version 5, so the search stops there.
228 while version >= 5 and self._CheckExtensionAPIExistence(api_name, version):
229 version -= 1
230 return version + 1
231
232 def _IsAvailable(self, api_name, version):
not at google - send to devlin 2013/05/15 08:15:00 yes, except pass this an api and a file system, no
epeterson 2013/06/02 00:25:50 The functions for checking existence now take a fi
233 return ((api_name in self._permission_apis
234 and self._CheckSimplePermissionExistence(api_name, version))
235 or (api_name in self._manifest_apis
236 and self._CheckManifestExistence(api_name, version))
237 or (api_name in self._orphan_apis
238 and self._CheckFileSystemExistence(api_name, version)))
239
240 def GetApiAvailability(self, api_name):
241 '''Determines the availability for an API by testing several scenarios.
242 (i.e. Is the API experimental? Only available on certain development
243 channels? If it's stable, when did it first become stable? etc.)
244 '''
245 availability = self._object_store.Get(api_name).Get()
246 if availability is not None:
247 return self.AvailabilityInfo(availability[0], availability[1])
248
249 # Check for a predetermined availability for this API.
250 availability = self._GetExistingAvailability(api_name, self._json_cache)
251 if availability is not None:
252 self._object_store.Set(api_name, availability)
253 return self.AvailabilityInfo(availability[0], availability[1])
not at google - send to devlin 2013/05/15 08:15:00 this block of code seems to be doing the same as t
epeterson 2013/06/02 00:25:50 The previous block is checking the object_store, w
254
255 latest_version = self._chrome_version_utility.GetLatestVersionNumber()
256
257 # TODO(epeterson) This shouldn't be done upfront
not at google - send to devlin 2013/05/15 08:15:00 yeah I think this block of code can basically be d
epeterson 2013/06/02 00:25:50 Done.
258 # There are three different scenarios for APIs in Chrome 20 and up. They
259 # can be in _permission_features, _manifest_features, or neither of the two.
260 # APIs are added to a respective list depending on which of these scenarios
261 # they fall into within the trunk filesystem.
262 if self._CheckSimplePermissionExistence(api_name, 'trunk'):
263 # This is a special case: availability can be any of the dev channels.
264 # First, check for this case before checking for stable existence.
265 self._permission_apis.append(api_name)
266 elif self._CheckManifestExistence(api_name, 'trunk'):
267 self._manifest_apis.append(api_name)
268 elif self._CheckFileSystemExistence(api_name, 'trunk'):
269 self._orphan_apis.append(api_name)
270 else:
271 raise FileNotFoundError(
272 'The API schema for %s could not be located.' % api_name)
273
274 # Channel: availability[0]; Version: availability[1]
275 availability = ['trunk', latest_version]
276 for channel in BranchUtility.GetAllChannelNames():
277 channel_info = self._branch_utility.GetChannelInfo(channel)
278 # TODO(epeterson) unicode to ints in branch_utility
279 if self._IsAvailable(api_name, availability[1]):
280 availability[0] = channel
281 if channel_info.version == 'trunk':
282 availability[1] = 'trunk'
283 else:
284 availability[1] = int(channel_info.version)
285 break
not at google - send to devlin 2013/05/15 08:15:00 this all looks very complicated? I think it can b
epeterson 2013/06/02 00:25:50 Done.
286 if availability[0] == 'stable':
287 availability[1] = self._FindEarliestAvailability(
288 api_name,
289 int(availability[1]))
290
291 self._object_store.Set(api_name, availability)
292 return self.AvailabilityInfo(availability[0], availability[1])
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698