From a6cb9eb6a41a95d33775391d69c6853b208a9aa9 Mon Sep 17 00:00:00 2001 From: Sergei Trofimov Date: Tue, 10 Oct 2017 09:45:40 +0100 Subject: [PATCH 1/3] instrument/energy: Fix backend parameter passing - Convert parameter keys to identifiers before applying - Raise an error if passed a param invalid for a backend. --- wa/instrumentation/energy_measurement.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/wa/instrumentation/energy_measurement.py b/wa/instrumentation/energy_measurement.py index 53db357a..ede78ad9 100644 --- a/wa/instrumentation/energy_measurement.py +++ b/wa/instrumentation/energy_measurement.py @@ -30,7 +30,7 @@ from wa import Instrument, Parameter from wa.framework import pluginloader from wa.framework.plugin import Plugin from wa.framework.exception import ConfigError, InstrumentError -from wa.utils.types import list_of_strings, list_of_ints, list_or_string, obj_dict +from wa.utils.types import list_of_strings, list_of_ints, list_or_string, obj_dict, identifier class EnergyInstrumentBackend(Plugin): @@ -249,10 +249,15 @@ class EnergyMeasurement(Instrument): self.backend = self.loader.get_plugin(self.instrument) self.params = obj_dict() + instrument_parameters = {identifier(k): v + for k, v in self.instrument_parameters.iteritems()} supported_params = self.backend.get_parameters() for name, param in supported_params.iteritems(): - value = self.instrument_parameters.get(name) + value = instrument_parameters.pop(name, None) param.set_value(self.params, value) + if instrument_parameters: + msg = 'Unexpected parameters for backend "{}": {}' + raise ConfigError(msg.format(self.instrument, instrument_parameters)) self.backend.validate_parameters(self.params) def initialize(self, context): From 5f7c64b08931135c02d57ccc6da1904524ba6e04 Mon Sep 17 00:00:00 2001 From: Sergei Trofimov Date: Tue, 10 Oct 2017 11:27:09 +0100 Subject: [PATCH 2/3] framework/resource: add match_path method This method is used to partially match a resource; its implementation cannot rely on the resource file actually being present and must match against the specified path alone. match() implementation now defaults to match_path(), as for most resource types, the path is sufficient to uniquely match a resource. --- wa/framework/resource.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/wa/framework/resource.py b/wa/framework/resource.py index ec8d36bc..b137fae9 100644 --- a/wa/framework/resource.py +++ b/wa/framework/resource.py @@ -72,6 +72,9 @@ class Resource(object): self.owner = owner def match(self, path): + return self.match_path(path) + + def match_path(self, path): raise NotImplementedError() def __str__(self): @@ -86,7 +89,7 @@ class File(Resource): super(File, self).__init__(owner) self.path = path - def match(self, path): + def match_path(self, path): return self.path == path def __str__(self): @@ -102,7 +105,7 @@ class Executable(Resource): self.abi = abi self.filename = filename - def match(self, path): + def match_path(self, path): return self.filename == os.path.basename(path) def __str__(self): @@ -118,7 +121,7 @@ class ReventFile(Resource): self.stage = stage self.target = target - def match(self, path): + def match_path(self, path): filename = os.path.basename(path) parts = filename.split('.') if len(parts) > 2: @@ -133,7 +136,7 @@ class JarFile(Resource): kind = 'jar' - def match(self, path): + def match_path(self, path): # An owner always has at most one jar file, so # always match return True @@ -154,6 +157,10 @@ class ApkFile(Resource): self.exact_abi = exact_abi self.supported_abi = supported_abi + def match_path(self, path): + ext = os.path.splitext(path)[1].lower() + return ext == '.apk' + def match(self, path): name_matches = True version_matches = True From 1daec4f2c52f327e66c88de25ed84bdce8d7b7a1 Mon Sep 17 00:00:00 2001 From: Sergei Trofimov Date: Tue, 10 Oct 2017 11:32:52 +0100 Subject: [PATCH 3/3] framework/getters: fix http getter APK resolution Fully matching an APK resource requires the file to be present locally, so that its metadata can be queries. HTTP getter was matching against a remote path so the match was failing. The matching now happens in two stages == first partial path-only matches are established. Secondly, all partial matches are downloaded and final match occurs against downloaded files. --- wa/framework/getters.py | 43 ++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/wa/framework/getters.py b/wa/framework/getters.py index e2ca572c..e4572070 100644 --- a/wa/framework/getters.py +++ b/wa/framework/getters.py @@ -71,6 +71,14 @@ def get_generic_resource(resource, files): return matches[0] +def get_path_matches(resource, files): + matches = [] + for f in files: + if resource.match_path(f): + matches.append(f) + return matches + + def get_from_location(basepath, resource): if resource.kind == 'file': path = os.path.join(basepath, resource.path) @@ -204,10 +212,15 @@ class Http(ResourceGetter): return # TODO: add support for unowned resources if not self.index: self.index = self.fetch_index() - asset = self.resolve_resource(resource) - if not asset: - return - return self.download_asset(asset, resource.owner.name) + if resource.kind == 'apk': + # APKs must always be downloaded to run ApkInfo for version + # information. + return self.resolve_apk(resource) + else: + asset = self.resolve_resource(resource) + if not asset: + return + return self.download_asset(asset, resource.owner.name) def fetch_index(self): if not self.url: @@ -251,6 +264,20 @@ class Http(ResourceGetter): auth = None return requests.get(url, auth=auth, stream=stream) + def resolve_apk(self, resource): + assets = self.index.get(resource.owner.name, {}) + if not assets: + return None + asset_map = {a['path']: a for a in assets} + paths = get_path_matches(resource, asset_map.keys()) + local_paths = [] + for path in paths: + local_paths.append(self.download_asset(asset_map[path], + resource.owner.name)) + for path in local_paths: + if resource.match(path): + return path + def resolve_resource(self, resource): # pylint: disable=too-many-branches,too-many-locals assets = self.index.get(resource.owner.name, {}) @@ -258,13 +285,7 @@ class Http(ResourceGetter): return {} asset_map = {a['path']: a for a in assets} - if resource.kind in ['apk', 'jar', 'revent']: - if resource.kind == 'apk' and resource.version: - # TODO: modify the index format to attach version info to the - # APK entries. - msg = 'Versions of APKs cannot be fetched over HTTP at this time' - self.logger.warning(msg) - return {} + if resource.kind in ['jar', 'revent']: path = get_generic_resource(resource, asset_map.keys()) if path: return asset_map[path]