Index: tools/telemetry/catapult_base/dependency_manager/dependency_manager.py |
diff --git a/tools/telemetry/catapult_base/dependency_manager/dependency_manager.py b/tools/telemetry/catapult_base/dependency_manager/dependency_manager.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..147c5c4f066b171005afef1809fa82f99fce9a97 |
--- /dev/null |
+++ b/tools/telemetry/catapult_base/dependency_manager/dependency_manager.py |
@@ -0,0 +1,172 @@ |
+# Copyright 2015 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 os |
+import sys |
+ |
+sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname( |
+ os.path.abspath(__file__))))) |
+from catapult_base.dependency_manager import base_config |
+from catapult_base.dependency_manager import exceptions |
+ |
+ |
+DEFAULT_TYPE = 'default' |
+ |
+ |
+class DependencyManager(object): |
+ def __init__(self, configs, supported_config_types=None): |
+ """Manages file dependencies found locally or in cloud_storage. |
+ |
+ Args: |
+ configs: A list of instances of BaseConfig or it's subclasses, passed |
+ in decreasing order of precedence. |
+ supported_config_types: A list of whitelisted config_types. |
+ No restrictions if None is specified. |
+ |
+ Raises: |
+ ValueError: If |configs| is not a list of instances of BaseConfig or |
+ its subclasses. |
+ UnsupportedConfigFormatError: If supported_config_types is specified and |
+ configs contains a config not in the supported config_types. |
+ |
+ Example: DependencyManager([config1, config2, config3]) |
+ No requirements on the type of Config, and any dependencies that have |
+ local files for the same platform will first look in those from |
+ config1, then those from config2, and finally those from config3. |
+ """ |
+ if configs is None or type(configs) != list: |
+ raise ValueError( |
+ 'Must supply a list of config files to DependencyManager') |
+ # self._lookup_dict is a dictionary with the following format: |
+ # { dependency1: {platform1: dependency_info1, |
+ # platform2: dependency_info2} |
+ # dependency2: {platform1: dependency_info3, |
+ # ...} |
+ # ...} |
+ # |
+ # Where the dependencies and platforms are strings, and the |
+ # dependency_info's are DependencyInfo instances. |
+ self._lookup_dict = {} |
+ self.supported_configs = supported_config_types or [] |
+ for config in configs: |
+ self._UpdateDependencies(config) |
+ |
+ def FetchPath(self, dependency, platform): |
+ """Get a path to an executable for |dependency|, downloading as needed. |
+ |
+ A path to a default executable may be returned if a platform specific |
+ version is not specified in the config(s). |
+ |
+ Args: |
+ dependency: Name of the desired dependency, as given in the config(s) |
+ used in this DependencyManager. |
+ platform: Name of the platform the dependency will run on. Often of the |
+ form 'os_architecture'. Must match those specified in the config(s) |
+ used in this DependencyManager. |
+ Returns: |
+ A path to an executable of |dependency| that will run on |platform|, |
+ downloading from cloud storage if needed. |
+ |
+ Raises: |
+ NoPathFoundError: If a local copy of the executable cannot be found and |
+ a remote path could not be downloaded from cloud_storage. |
+ CredentialsError: If cloud_storage credentials aren't configured. |
+ PermissionError: If cloud_storage credentials are configured, but not |
+ with an account that has permission to download the remote file. |
+ NotFoundError: If the remote file does not exist where expected in |
+ cloud_storage. |
+ ServerError: If an internal server error is hit while downloading the |
+ remote file. |
+ CloudStorageError: If another error occured while downloading the remote |
+ path. |
+ FileNotFoundError: If an attempted download was otherwise unsuccessful. |
+ |
+ """ |
+ dependency_info = self._GetDependencyInfo(dependency, platform) |
+ if not dependency_info: |
+ raise exceptions.NoPathFoundError(dependency, platform) |
+ path = dependency_info.GetLocalPath() |
+ if not path or not os.path.exists(path): |
+ path = dependency_info.GetRemotePath() |
+ if not path or not os.path.exists(path): |
+ raise exceptions.NoPathFoundError(dependency, platform) |
+ return path |
+ |
+ def LocalPath(self, dependency, platform): |
+ """Get a path to a locally stored executable for |dependency|. |
+ |
+ A path to a default executable may be returned if a platform specific |
+ version is not specified in the config(s). |
+ Will not download the executable. |
+ |
+ Args: |
+ dependency: Name of the desired dependency, as given in the config(s) |
+ used in this DependencyManager. |
+ platform: Name of the platform the dependency will run on. Often of the |
+ form 'os_architecture'. Must match those specified in the config(s) |
+ used in this DependencyManager. |
+ Returns: |
+ A path to an executable for |dependency| that will run on |platform|. |
+ |
+ Raises: |
+ NoPathFoundError: If a local copy of the executable cannot be found. |
+ """ |
+ dependency_info = self._GetDependencyInfo(dependency, platform) |
+ if not dependency_info: |
+ raise exceptions.NoPathFoundError(dependency, platform) |
+ local_path = dependency_info.GetLocalPath() |
+ if not local_path or not os.path.exists(local_path): |
+ raise exceptions.NoPathFoundError(dependency, platform) |
+ return local_path |
+ |
+ def _UpdateDependencies(self, config): |
+ """Add the dependency information stored in |config| to this instance. |
+ |
+ Args: |
+ config: An instances of BaseConfig or a subclasses. |
+ |
+ Raises: |
+ UnsupportedConfigFormatError: If supported_config_types was specified |
+ and config is not in the supported config_types. |
+ """ |
+ if not isinstance(config, base_config.BaseConfig): |
+ raise ValueError('Must use a BaseConfig or subclass instance with the ' |
+ 'DependencyManager.') |
+ if (self.supported_configs and |
+ config.GetConfigType() not in self.supported_configs): |
+ raise exceptions.UnsupportedConfigFormatError(config.GetConfigType(), |
+ config.config_path) |
+ for dep_info in config.IterDependencyInfo(): |
+ dependency = dep_info.dependency |
+ platform = dep_info.platform |
+ if dependency not in self._lookup_dict: |
+ self._lookup_dict[dependency] = {} |
+ if platform not in self._lookup_dict[dependency]: |
+ self._lookup_dict[dependency][platform] = dep_info |
+ else: |
+ self._lookup_dict[dependency][platform].Update(dep_info) |
+ |
+ |
+ def _GetDependencyInfo(self, dependency, platform): |
+ """Get information for |dependency| on |platform|, or a default if needed. |
+ |
+ Args: |
+ dependency: Name of the desired dependency, as given in the config(s) |
+ used in this DependencyManager. |
+ platform: Name of the platform the dependency will run on. Often of the |
+ form 'os_architecture'. Must match those specified in the config(s) |
+ used in this DependencyManager. |
+ |
+ Returns: The dependency_info for |dependency| on |platform| if it exists. |
+ Or the default version of |dependency| if it exists, or None if neither |
+ exist. |
+ """ |
+ if not self._lookup_dict or dependency not in self._lookup_dict: |
+ return None |
+ dependency_dict = self._lookup_dict[dependency] |
+ device_type = platform |
+ if not device_type in dependency_dict: |
+ device_type = DEFAULT_TYPE |
+ return dependency_dict.get(device_type) |
+ |