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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: chrome/common/extensions/docs/server2/availability_finder.py
diff --git a/chrome/common/extensions/docs/server2/availability_finder.py b/chrome/common/extensions/docs/server2/availability_finder.py
new file mode 100644
index 0000000000000000000000000000000000000000..2926523cb9c4230bd6765e37550e2f22ba7c414c
--- /dev/null
+++ b/chrome/common/extensions/docs/server2/availability_finder.py
@@ -0,0 +1,292 @@
+# 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 collections
+import os
+
+from branch_utility import BranchUtility
+from chrome_version_utility import ChromeVersionUtility
+from compiled_file_system import CompiledFileSystem
+from file_system import FileNotFoundError
+import svn_constants
+from third_party.json_schema_compiler import json_parse, model
+
+API_AVAILABILITIES = 'api_availabilities.json'
+EXTENSION_API = 'extension_api.json'
+MANIFEST_FEATURES = '_manifest_features.json'
+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.
+
+def _GetChannelFromPermissionFeature(permission_feature):
+ '''Handles finding API channel information from _permission_features.json.
+ Sometimes this info will be in a dict, but other times it will be
+ in a dict contained in a list.
+ '''
+ channel = None
+ if permission_feature is not None:
+ if isinstance(permission_feature, collections.Mapping):
+ channel = permission_feature.get('channel')
+ else:
+ channel = BranchUtility.NewestChannel(entry.get('channel')
+ for entry in permission_feature)
+ return channel
+
+class AvailabilityFinder(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 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
+ def __init__(self, channel, version):
+ self.channel = channel
+ self.version = version
+
+ class Factory(object):
+ def __init__(self,
+ chrome_version_utility,
+ object_store_creator,
+ compiled_host_fs_factory,
+ create_file_system):
+ self._chrome_version_utility = chrome_version_utility
+ self._object_store_creator = object_store_creator
+ self._compiled_host_fs_factory = compiled_host_fs_factory
+ self._create_file_system = create_file_system
+
+ def Create(self):
+ return AvailabilityFinder(self._chrome_version_utility,
+ self._object_store_creator,
+ self._compiled_host_fs_factory,
+ self._create_file_system)
+
+ def __init__(self,
+ chrome_version_utility,
+ object_store_creator,
+ compiled_host_fs_factory,
+ create_file_system):
+ self._chrome_version_utility = chrome_version_utility
+ self._object_store_creator = object_store_creator
+ 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
+ AvailabilityFinder,
+ 'json-cache')
+ self._branch_utility = BranchUtility.Create(object_store_creator)
+ self._create_file_system = create_file_system
+ self._object_store = object_store_creator.Create(AvailabilityFinder)
+ 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
+ self._manifest_apis = []
+ self._orphan_apis = []
+
+ def _ParseForCompiledFS(self, path, json):
+ '''Wrapper method to be passed to compiled_file_systems upon creation.
+ '''
+ return json_parse.Parse(json)
+
+ def _CreateCompiledFS(self, branch):
+ # 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
+ compiled_fs_factory = CompiledFileSystem.Factory(
+ self._create_file_system(branch),
+ self._object_store_creator)
+ 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.
+ AvailabilityFinder,
+ 'branch')
+
+ def _GetExistingAvailability(self, api_name, compiled_fs):
+ api_info = compiled_fs.GetFromFile(
+ '%s/%s' % (svn_constants.JSON_PATH, API_AVAILABILITIES)).get(api_name)
+ if api_info is not None:
+ return [api_info.get('channel'), api_info.get('version')]
+ 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.
+
+ def _GetPermissionFeature(self, api_name, compiled_fs):
+ return compiled_fs.GetFromFile(
+ '%s/%s' % (svn_constants.API_PATH, PERMISSION_FEATURES)).get(api_name)
+
+ def _GetManifestFeature(self, api_name, compiled_fs):
+ return compiled_fs.GetFromFile(
+ '%s/%s' % (svn_constants.API_PATH, MANIFEST_FEATURES)).get(api_name)
+
+ def _GetJSONForExtensionAPI(self, api_name, compiled_fs):
+ return compiled_fs.GetFromFile(
+ '%s/%s' % (svn_constants.API_PATH, EXTENSION_API))
+
+ 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.
+ return json_parse.Parse(svn_file_system.ReadSingle(path))
+
not at google - send to devlin 2013/05/15 08:15:00 extra \n
epeterson 2013/06/02 00:25:50 Done.
+
+ 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|.
+ '''
+ compiled_fs = self._CreateCompiledFS(
+ self._chrome_version_utility.GetBranchNumberForVersion(version))
+ permission_feature = self._GetPermissionFeature(api_name, compiled_fs)
+ existence = 'stable' == _GetChannelFromPermissionFeature(permission_feature)
+ return existence
+
+ def _CheckChannelPermissionExistence(self, api_name, version, channel):
+ compiled_fs = self._CreateCompiledFS(
+ self._chrome_version_utility.GetBranchNumberForVersion(version))
+ permission_feature = self._GetPermissionFeature(api_name, compiled_fs)
+ existence = channel == _GetChannelFromPermissionFeature(permission_feature)
+ return existence
+
not at google - send to devlin 2013/05/15 08:15:00 extra \n
epeterson 2013/06/02 00:25:50 Done.
+
+ 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.
+ compiled_fs = self._CreateCompiledFS(
+ self._chrome_version_utility.GetBranchNumberForVersion(version))
+ permission_feature = self._GetPermissionFeature(api_name, compiled_fs)
+ return permission_feature is not None
+
+ 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.
+ '''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)
+ compiled_fs = self._CreateCompiledFS(
+ self._chrome_version_utility.GetBranchNumberForVersion(version))
+ manifest_feature = self._GetManifestFeature(api_name, compiled_fs)
+ return manifest_feature is not None
+
+ def _GetAllNames(self, files):
+ '''Returns extension-less names for a list of files.
+ '''
+ return [os.path.splitext(f)[0] for f in files]
+
+ 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.
+ '''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(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
+ if names is not None:
+ return api_name in names
+ 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
+ self._chrome_version_utility.GetBranchNumberForVersion(version))
+ names = self._GetAllNames(
+ svn_file_system.ReadSingle('%s/' % svn_constants.API_PATH))
+ self._object_store.Set(str(version), names)
+ return api_name in names
+
+ def _CheckExtensionAPIExistence(self, api_name, version):
+ '''Parses the extension_api.json file (available in earlier versions of
+ chrome) for an API namespace. If this is successfully found, then the API
+ is considered to have been 'stable' for the given version.
+ '''
+ # The extension_api.json files only appear from version 5 to version 17.
+ assert(version <= 17 and version >= 5)
+
+ compiled_fs = self._CreateCompiledFS(
+ self._chrome_version_utility.GetBranchNumberForVersion(version))
+ try:
+ extension_api = self._GetJSONForExtensionAPI(api_name, compiled_fs)
+ api_rows = [row.get('namespace') for row in extension_api
+ if 'namespace' in row]
+ return True if api_name in api_rows else False
+ except FileNotFoundError as e:
+ # This should only happen on preview.py, since extension_api.json is no
+ # longer present in current versions.
+ return False
+
+ def _FindEarliestAvailability(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. We'll also check the _manifest_features.json if
+ # an API isn't found in _permission_features, and, failing that, we'll check
+ # for the API's existence in the filesystem.
+ while version >= 20:
+ if ((api_name in self._permission_apis
+ and self._CheckChannelPermissionExistence(api_name,
+ version,
+ 'stable'))
+ or (api_name in self._manifest_apis
+ and self._CheckManifestExistence(api_name, version))
+ or (api_name in self._orphan_apis
+ and self._CheckFileSystemExistence(api_name, version))):
+ version -= 1
+ else:
+ return version + 1
not at google - send to devlin 2013/05/15 08:15:00 pls see earlier comments about simplifying the ret
+ # These versions are a little troublesome. Version 19 has
+ # _permission_features.json, but it lacks 'channel' information. Version 18
+ # doesn't have either of the json files. For now, we're using a simple check
+ # for filesystem existence here.
+ while version >= 18:
+ if self._CheckFileSystemExistence(api_name, version):
+ version -= 1
+ else:
+ return version + 1
+ # Versions 17 and earlier have an extension_api.json file which contains
+ # namespaces for each API that was available at the time. We can use this
+ # file to check for existence; however, we no longer have
+ # file system access after version 5, so the search stops there.
+ while version >= 5 and self._CheckExtensionAPIExistence(api_name, version):
+ version -= 1
+ return version + 1
+
+ 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
+ return ((api_name in self._permission_apis
+ and self._CheckSimplePermissionExistence(api_name, version))
+ or (api_name in self._manifest_apis
+ and self._CheckManifestExistence(api_name, version))
+ or (api_name in self._orphan_apis
+ and self._CheckFileSystemExistence(api_name, version)))
+
+ def GetApiAvailability(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 self.AvailabilityInfo(availability[0], availability[1])
+
+ # Check for a predetermined availability for this API.
+ availability = self._GetExistingAvailability(api_name, self._json_cache)
+ if availability is not None:
+ self._object_store.Set(api_name, availability)
+ 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
+
+ latest_version = self._chrome_version_utility.GetLatestVersionNumber()
+
+ # 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.
+ # There are three different scenarios for APIs in Chrome 20 and up. They
+ # can be in _permission_features, _manifest_features, or neither of the two.
+ # APIs are added to a respective list depending on which of these scenarios
+ # they fall into within the trunk filesystem.
+ if self._CheckSimplePermissionExistence(api_name, 'trunk'):
+ # This is a special case: availability can be any of the dev channels.
+ # First, check for this case before checking for stable existence.
+ self._permission_apis.append(api_name)
+ elif self._CheckManifestExistence(api_name, 'trunk'):
+ self._manifest_apis.append(api_name)
+ elif self._CheckFileSystemExistence(api_name, 'trunk'):
+ self._orphan_apis.append(api_name)
+ else:
+ raise FileNotFoundError(
+ 'The API schema for %s could not be located.' % api_name)
+
+ # Channel: availability[0]; Version: availability[1]
+ availability = ['trunk', latest_version]
+ for channel in BranchUtility.GetAllChannelNames():
+ channel_info = self._branch_utility.GetChannelInfo(channel)
+ # TODO(epeterson) unicode to ints in branch_utility
+ if self._IsAvailable(api_name, availability[1]):
+ availability[0] = channel
+ if channel_info.version == 'trunk':
+ availability[1] = 'trunk'
+ else:
+ availability[1] = int(channel_info.version)
+ 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.
+ if availability[0] == 'stable':
+ availability[1] = self._FindEarliestAvailability(
+ api_name,
+ int(availability[1]))
+
+ self._object_store.Set(api_name, availability)
+ return self.AvailabilityInfo(availability[0], availability[1])

Powered by Google App Engine
This is Rietveld 408576698