Index: chrome/common/extensions/docs/server2/availability_data_source.py |
diff --git a/chrome/common/extensions/docs/server2/availability_data_source.py b/chrome/common/extensions/docs/server2/availability_data_source.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a1848567767da9596ba87cdf0baba1dee1ade707 |
--- /dev/null |
+++ b/chrome/common/extensions/docs/server2/availability_data_source.py |
@@ -0,0 +1,249 @@ |
+# Copyright (c) 2013 The Chromium Authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+ |
+import json |
+import logging |
+import os |
+ |
+from appengine_url_fetcher import AppEngineUrlFetcher |
+from caching_file_system import CachingFileSystem |
+from compiled_file_system import CompiledFileSystem |
+from subversion_file_system import SubversionFileSystem |
+import svn_constants |
+from third_party.json_schema_compiler import json_comment_eater |
+from third_party.json_schema_compiler import model |
+import url_constants |
+ |
+# These labels are used for interacting with AvailabilityDataSource's object |
+# store. The format is <label><string> (e.g. svn21, or permissiontabs26) |
+SVN = 'svn' |
+PERMISSION = 'permission' |
+MANIFEST = 'manifest' |
+FILESYS = 'filesys' |
not at google - send to devlin
2013/04/30 18:34:19
use these in category= when creating the object st
epeterson
2013/05/13 02:38:10
Done.
|
+ |
+class AvailabilityDataSource(object): |
+ '''Uses API data sources generated by a ChromeVersionDataSource in order to |
+ search the filesystem for the earliest existence of a specified API throughout |
+ the different versions of Chrome; this constitutes an API's availability. |
+ ''' |
+ class Factory(object): |
+ def __init__(self, |
+ chrome_version_utility, |
+ object_store_creator_factory, |
+ svn_file_system): |
+ self._chrome_version_utility = chrome_version_utility |
+ self._object_store_creator_factory = object_store_creator_factory |
+ json_string = svn_file_system.ReadSingle('%s/api_availabilities.json' |
+ % svn_constants.JSON_PATH) |
+ self._existing_availabilities = json.loads(json_string) |
+ json_string = svn_file_system.ReadSingle('%s/_permission_features.json' |
+ % svn_constants.API_PATH) |
+ self._permission_features = json.loads(json_comment_eater.Nom( |
+ json_string)) |
+ json_string = svn_file_system.ReadSingle('%s/_manifest_features.json' |
+ % svn_constants.API_PATH) |
not at google - send to devlin
2013/04/30 18:34:19
use third_party.json_schema_compiler.json_parse as
epeterson
2013/05/13 02:38:10
Done.
|
+ self._manifest_features = json.loads(json_comment_eater.Nom(json_string)) |
+ |
+ def Create(self): |
+ return AvailabilityDataSource(self._chrome_version_utility, |
+ self._object_store_creator_factory, |
+ self._existing_availabilities, |
+ self._permission_features, |
+ self._manifest_features) |
+ |
+ def __init__(self, |
+ chrome_version_utility, |
+ object_store_creator_factory, |
+ existing_availabilities, |
+ permission_features, |
+ manifest_features): |
+ self._chrome_version_utility = chrome_version_utility |
+ self._object_store_creator_factory = object_store_creator_factory |
+ self._object_store = self._object_store_creator_factory.Create( |
+ AvailabilityDataSource).Create() |
+ self._existing_availabilities = existing_availabilities |
+ self._permission_features = permission_features |
+ self._manifest_features = manifest_features |
+ self._permission_apis = [] |
+ self._manifest_apis = [] |
+ self._orphan_apis = [] |
+ |
+ def _CreateSvnFileSystemForBranch(self, branch): |
not at google - send to devlin
2013/04/30 18:34:19
maybe this should go as a little helper on Subvers
epeterson
2013/05/13 02:38:10
Well, I had done this, and modified the old Create
|
+ '''Creates a file system tied to a specific |branch| of the svn repository. |
+ ''' |
+ if branch == 'trunk': |
+ svn_url = '/'.join((url_constants.SVN_TRUNK_URL, |
+ 'src', |
+ svn_constants.EXTENSIONS_PATH)) |
+ else: |
+ svn_url = '/'.join((url_constants.SVN_BRANCH_URL, |
+ branch, |
+ 'src', |
+ svn_constants.EXTENSIONS_PATH)) |
+ viewvc_url = svn_url.replace(url_constants.SVN_URL, |
+ url_constants.VIEWVC_URL) |
+ |
+ svn_file_system = CachingFileSystem( |
+ SubversionFileSystem(AppEngineUrlFetcher(svn_url), |
+ AppEngineUrlFetcher(viewvc_url)), |
+ self._object_store_creator_factory) |
+ return svn_file_system |
+ |
+ def _GetChannelFromPermissionFeatures(self, api_name, permission_features): |
+ '''Sometimes the API information will be in a dict, other times it will be |
+ in a dict contained in a list. |
+ ''' |
+ api_info = permission_features.get(api_name, None) |
+ if api_info is not None: |
+ try: |
+ channel = api_info.get('channel') |
+ except AttributeError: |
+ channel = api_info[0].get('channel') |
not at google - send to devlin
2013/04/30 18:34:19
don't use exceptions for control flow. Also, if it
epeterson
2013/05/13 02:38:10
Done.
|
+ finally: |
+ return channel |
+ return None |
+ |
+ def _CheckStablePermissionExistence(self, api_name, version): |
+ '''Checks _permission_features.json for the given |version| of Chrome, |
+ and returns a boolean representing whether or not the API was available on |
+ the stable channel in |version|. |
+ ''' |
+ existence = self._object_store.Get('%s%s%s' |
+ % (PERMISSION, api_name, version)).Get() |
+ if existence is not None: |
+ return existence |
+ |
+ svn_file_system = self._object_store.Get('%s%s' % (SVN, version)).Get() |
+ if svn_file_system is None: |
+ svn_file_system = self._CreateSvnFileSystemForBranch( |
+ self._chrome_version_utility.GetBranchNumberForVersion(version)) |
not at google - send to devlin
2013/04/30 18:34:19
I don't think you can put svn file systems in obje
epeterson
2013/05/13 02:38:10
Done.
|
+ self._object_store.Set('%s%s' % (SVN, version), svn_file_system) |
+ |
+ json_string = svn_file_system.ReadSingle('%s/_permission_features.json' |
+ % svn_constants.API_PATH) |
+ permission_features = json.loads(json_comment_eater.Nom(json_string)) |
not at google - send to devlin
2013/04/30 18:34:19
same comment re json parsing i.e. use json_parse.P
epeterson
2013/05/13 02:38:10
Done.
|
+ |
+ existence = 'stable' == self._GetChannelFromPermissionFeatures( |
+ api_name, permission_features) |
+ self._object_store.Set('%s%s%s' % (PERMISSION, api_name, version), |
+ existence) |
+ return existence |
+ |
+ def _CheckManifestExistence(self, api_name, version): |
+ '''As a fallback measure for an API not being found in _permission_features, |
+ check _manifest_features.json for the given |version| of Chrome, |
+ and return a boolean representing whether or not the API was represented |
+ within the file. |
+ ''' |
+ #_manifest_features.json uses unix_hacker_style API names. |
+ api_name = model.UnixName(api_name) |
+ existence = self._object_store.Get('%s%s%s' |
+ % (MANIFEST, api_name, version)).Get() |
+ if existence is not None: |
+ return existence |
+ |
+ svn_file_system = self._object_store.Get('%s%s' % (SVN, version)).Get() |
+ if svn_file_system is None: |
+ svn_file_system = self._CreateSvnFileSystemForBranch( |
+ self._chrome_version_utility.GetBranchNumberForVersion(version)) |
+ self._object_store.Set('%s%s' % (SVN, version), svn_file_system) |
+ |
+ json_string = svn_file_system.ReadSingle('%s/_manifest_features.json' |
+ % svn_constants.API_PATH) |
+ manifest_features = json.loads(json_comment_eater.Nom(json_string)) |
+ if manifest_features.get(api_name, None) is not None: |
+ existence = True |
+ else: |
+ existence = False |
+ self._object_store.Set('%s%s%s' % (MANIFEST, api_name, version), existence) |
+ return existence |
not at google - send to devlin
2013/04/30 18:34:19
similar comments
epeterson
2013/05/13 02:38:10
Done.
|
+ |
+ def _GetAllNames(self, files_dict): |
+ '''Returns extension-less names for a list of files. |
+ ''' |
+ files = files_dict[svn_constants.API_PATH + '/'] |
+ return [model.UnixName(os.path.splitext(f)[0]) for f in files] |
+ |
+ def _CheckFileSystemExistence(self, api_name, version): |
+ '''Uses a list of API names for a given branch to check simply for the |
+ existence of a given API in that branch. |
+ ''' |
+ names = self._object_store.Get('%s%s' % (FILESYS, version)).Get() |
+ if names is not None: |
+ return api_name in names |
+ |
+ svn_file_system = self._object_store.Get('%s%s' % (SVN, version)).Get() |
+ if svn_file_system is None: |
+ svn_file_system = self._CreateSvnFileSystemForBranch( |
+ self._chrome_version_utility.GetBranchNumberForVersion(version)) |
+ self._object_store.Set('%s%s' % (SVN, version), svn_file_system) |
+ names = svn_file_system.Read([svn_constants.API_PATH + '/']).Get() |
+ names = self._GetAllNames(names) |
+ self._object_store.Set('%s%s' % (FILESYS, version), names) |
+ return api_name in names |
not at google - send to devlin
2013/04/30 18:34:19
similar comments
epeterson
2013/05/13 02:38:10
Done.
|
+ |
+ def _FindEarliestStableAvailability(self, api_name, version): |
+ '''Searches in descending order through filesystem caches tied to specific |
+ chrome version numbers and looks for the availability of an API, |api_name|, |
+ on the stable channel. When a version is found where the API is no longer |
+ available on stable, returns the previous version number (the last known |
+ version where the API was stable). |
+ ''' |
+ # The _permission_features.json file (with 'channel' keys) is present only |
+ # in Chrome 20 and onwards. |
+ while version >= 20: |
+ if ((api_name in self._permission_apis |
+ and self._CheckStablePermissionExistence(api_name, str(version))) |
+ or (api_name in self._manifest_apis |
+ and self._CheckManifestExistence(api_name, str(version))) |
+ or (api_name in self._orphan_apis |
+ and self._CheckFileSystemExistence(api_name, str(version)))): |
+ version -= 1 |
+ else: |
+ break |
+ version += 1 |
+ return str(version) |
not at google - send to devlin
2013/04/30 18:34:19
return version + 1 rather than incrementing
epeterson
2013/05/13 02:38:10
Done.
|
+ |
+ def GetAvailability(self, api_name): |
+ '''Determines the availability for an API by testing several scenarios. |
+ (i.e. Is the API experimental? Only available on certain development |
+ channels? If it's stable, when did it first become stable? etc.) |
+ ''' |
+ availability = self._object_store.Get(api_name).Get() |
+ if availability is not None: |
+ return availability |
+ |
+ # Check for a predetermined availability for this API. |
+ availability = self._existing_availabilities.get(api_name, None) |
not at google - send to devlin
2013/04/30 18:34:19
the None is implicit in get()
epeterson
2013/05/13 02:38:10
Done.
|
+ if availability is not None: |
+ self._object_store.Set(api_name, availability) |
+ return availability |
+ |
+ latest_version = str(self._chrome_version_utility.GetLatestVersionNumber()) |
+ channel = self._GetChannelFromPermissionFeatures(api_name, |
+ self._permission_features) |
+ if channel is not None: |
+ # This is a special case: availability can be any of the dev channels, |
+ # to check for this information instead of checking just for existence. |
+ availability = channel |
+ self._permission_apis.append(api_name) |
+ elif self._CheckManifestExistence(api_name, latest_version): |
+ availability = 'stable' |
+ self._manifest_apis.append(api_name) |
+ elif self._CheckFileSystemExistence(api_name, latest_version): |
+ availability = 'stable' |
+ self._orphan_apis.append(api_name) |
+ else: |
+ logging.warning('%s could not be located. %s\'s availability is None.' |
+ % (api_name, api_name)) |
+ availability = None |
not at google - send to devlin
2013/04/30 18:34:19
throw a FileNotFoundError here or something, so we
epeterson
2013/05/13 02:38:10
Done.
|
+ |
+ if availability != 'stable': |
+ self._object_store.Set(api_name, availability) |
+ return availability |
+ |
+ availability = self._FindEarliestStableAvailability(api_name, |
+ int(latest_version) - 1) |
+ self._object_store.Set(api_name, availability) |
+ return availability |
not at google - send to devlin
2013/04/30 18:34:19
slightly nicer factoring:
if availability == 'sta
epeterson
2013/05/13 02:38:10
Done.
|