OLD | NEW |
(Empty) | |
| 1 # Copyright 2015 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 os |
| 6 import sys |
| 7 |
| 8 sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname( |
| 9 os.path.abspath(__file__))))) |
| 10 from catapult_base.dependency_manager import base_config |
| 11 from catapult_base.dependency_manager import exceptions |
| 12 |
| 13 |
| 14 DEFAULT_TYPE = 'default' |
| 15 |
| 16 |
| 17 class DependencyManager(object): |
| 18 def __init__(self, configs, supported_config_types=None): |
| 19 """Manages file dependencies found locally or in cloud_storage. |
| 20 |
| 21 Args: |
| 22 configs: A list of instances of BaseConfig or it's subclasses, passed |
| 23 in decreasing order of precedence. |
| 24 supported_config_types: A list of whitelisted config_types. |
| 25 No restrictions if None is specified. |
| 26 |
| 27 Raises: |
| 28 ValueError: If |configs| is not a list of instances of BaseConfig or |
| 29 its subclasses. |
| 30 UnsupportedConfigFormatError: If supported_config_types is specified and |
| 31 configs contains a config not in the supported config_types. |
| 32 |
| 33 Example: DependencyManager([config1, config2, config3]) |
| 34 No requirements on the type of Config, and any dependencies that have |
| 35 local files for the same platform will first look in those from |
| 36 config1, then those from config2, and finally those from config3. |
| 37 """ |
| 38 if configs is None or type(configs) != list: |
| 39 raise ValueError( |
| 40 'Must supply a list of config files to DependencyManager') |
| 41 # self._lookup_dict is a dictionary with the following format: |
| 42 # { dependency1: {platform1: dependency_info1, |
| 43 # platform2: dependency_info2} |
| 44 # dependency2: {platform1: dependency_info3, |
| 45 # ...} |
| 46 # ...} |
| 47 # |
| 48 # Where the dependencies and platforms are strings, and the |
| 49 # dependency_info's are DependencyInfo instances. |
| 50 self._lookup_dict = {} |
| 51 self.supported_configs = supported_config_types or [] |
| 52 for config in configs: |
| 53 self._UpdateDependencies(config) |
| 54 |
| 55 def FetchPath(self, dependency, platform): |
| 56 """Get a path to an executable for |dependency|, downloading as needed. |
| 57 |
| 58 A path to a default executable may be returned if a platform specific |
| 59 version is not specified in the config(s). |
| 60 |
| 61 Args: |
| 62 dependency: Name of the desired dependency, as given in the config(s) |
| 63 used in this DependencyManager. |
| 64 platform: Name of the platform the dependency will run on. Often of the |
| 65 form 'os_architecture'. Must match those specified in the config(s) |
| 66 used in this DependencyManager. |
| 67 Returns: |
| 68 A path to an executable of |dependency| that will run on |platform|, |
| 69 downloading from cloud storage if needed. |
| 70 |
| 71 Raises: |
| 72 NoPathFoundError: If a local copy of the executable cannot be found and |
| 73 a remote path could not be downloaded from cloud_storage. |
| 74 CredentialsError: If cloud_storage credentials aren't configured. |
| 75 PermissionError: If cloud_storage credentials are configured, but not |
| 76 with an account that has permission to download the remote file. |
| 77 NotFoundError: If the remote file does not exist where expected in |
| 78 cloud_storage. |
| 79 ServerError: If an internal server error is hit while downloading the |
| 80 remote file. |
| 81 CloudStorageError: If another error occured while downloading the remote |
| 82 path. |
| 83 FileNotFoundError: If an attempted download was otherwise unsuccessful. |
| 84 |
| 85 """ |
| 86 dependency_info = self._GetDependencyInfo(dependency, platform) |
| 87 if not dependency_info: |
| 88 raise exceptions.NoPathFoundError(dependency, platform) |
| 89 path = dependency_info.GetLocalPath() |
| 90 if not path or not os.path.exists(path): |
| 91 path = dependency_info.GetRemotePath() |
| 92 if not path or not os.path.exists(path): |
| 93 raise exceptions.NoPathFoundError(dependency, platform) |
| 94 return path |
| 95 |
| 96 def LocalPath(self, dependency, platform): |
| 97 """Get a path to a locally stored executable for |dependency|. |
| 98 |
| 99 A path to a default executable may be returned if a platform specific |
| 100 version is not specified in the config(s). |
| 101 Will not download the executable. |
| 102 |
| 103 Args: |
| 104 dependency: Name of the desired dependency, as given in the config(s) |
| 105 used in this DependencyManager. |
| 106 platform: Name of the platform the dependency will run on. Often of the |
| 107 form 'os_architecture'. Must match those specified in the config(s) |
| 108 used in this DependencyManager. |
| 109 Returns: |
| 110 A path to an executable for |dependency| that will run on |platform|. |
| 111 |
| 112 Raises: |
| 113 NoPathFoundError: If a local copy of the executable cannot be found. |
| 114 """ |
| 115 dependency_info = self._GetDependencyInfo(dependency, platform) |
| 116 if not dependency_info: |
| 117 raise exceptions.NoPathFoundError(dependency, platform) |
| 118 local_path = dependency_info.GetLocalPath() |
| 119 if not local_path or not os.path.exists(local_path): |
| 120 raise exceptions.NoPathFoundError(dependency, platform) |
| 121 return local_path |
| 122 |
| 123 def _UpdateDependencies(self, config): |
| 124 """Add the dependency information stored in |config| to this instance. |
| 125 |
| 126 Args: |
| 127 config: An instances of BaseConfig or a subclasses. |
| 128 |
| 129 Raises: |
| 130 UnsupportedConfigFormatError: If supported_config_types was specified |
| 131 and config is not in the supported config_types. |
| 132 """ |
| 133 if not isinstance(config, base_config.BaseConfig): |
| 134 raise ValueError('Must use a BaseConfig or subclass instance with the ' |
| 135 'DependencyManager.') |
| 136 if (self.supported_configs and |
| 137 config.GetConfigType() not in self.supported_configs): |
| 138 raise exceptions.UnsupportedConfigFormatError(config.GetConfigType(), |
| 139 config.config_path) |
| 140 for dep_info in config.IterDependencyInfo(): |
| 141 dependency = dep_info.dependency |
| 142 platform = dep_info.platform |
| 143 if dependency not in self._lookup_dict: |
| 144 self._lookup_dict[dependency] = {} |
| 145 if platform not in self._lookup_dict[dependency]: |
| 146 self._lookup_dict[dependency][platform] = dep_info |
| 147 else: |
| 148 self._lookup_dict[dependency][platform].Update(dep_info) |
| 149 |
| 150 |
| 151 def _GetDependencyInfo(self, dependency, platform): |
| 152 """Get information for |dependency| on |platform|, or a default if needed. |
| 153 |
| 154 Args: |
| 155 dependency: Name of the desired dependency, as given in the config(s) |
| 156 used in this DependencyManager. |
| 157 platform: Name of the platform the dependency will run on. Often of the |
| 158 form 'os_architecture'. Must match those specified in the config(s) |
| 159 used in this DependencyManager. |
| 160 |
| 161 Returns: The dependency_info for |dependency| on |platform| if it exists. |
| 162 Or the default version of |dependency| if it exists, or None if neither |
| 163 exist. |
| 164 """ |
| 165 if not self._lookup_dict or dependency not in self._lookup_dict: |
| 166 return None |
| 167 dependency_dict = self._lookup_dict[dependency] |
| 168 device_type = platform |
| 169 if not device_type in dependency_dict: |
| 170 device_type = DEFAULT_TYPE |
| 171 return dependency_dict.get(device_type) |
| 172 |
OLD | NEW |