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

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

Powered by Google App Engine
This is Rietveld 408576698