From aee7ca9f92851a3fca54fc5cfeef754756c26bbd Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Sat, 26 Feb 2011 15:32:04 +0000 Subject: [PATCH] Added Implementation.is_available and Selection.is_available These test whether the implementation is available locally, removing the duplicated logic in Policy and Solver. --- tests/basetest.py | 8 +++++++- tests/testsat.py | 2 +- zeroinstall/injector/model.py | 19 +++++++++++++++++++ zeroinstall/injector/policy.py | 13 +------------ zeroinstall/injector/selections.py | 8 ++++++++ zeroinstall/injector/solver.py | 28 +++------------------------- zeroinstall/zerostore/__init__.py | 16 +++++++++++++--- 7 files changed, 52 insertions(+), 42 deletions(-) diff --git a/tests/basetest.py b/tests/basetest.py index e30618b..0fa5c10 100755 --- a/tests/basetest.py +++ b/tests/basetest.py @@ -132,10 +132,16 @@ class TestStores: def add_fake(self, digest): self.fake_impls.add(digest) - def lookup_any(self, digests): + def lookup_maybe(self, digests): for d in digests: if d in self.fake_impls: return '/fake_store/' + d + return None + + def lookup_any(self, digests): + path = self.lookup_maybe(digests) + if path: + return path raise NotStored() class TestConfig: diff --git a/tests/testsat.py b/tests/testsat.py index 579654f..b1dfeee 100755 --- a/tests/testsat.py +++ b/tests/testsat.py @@ -14,7 +14,7 @@ import logging logger = logging.getLogger() class Stores: - def lookup_any(self, digests): + def lookup_maybe(self, digests): return "/" stores = Stores() diff --git a/zeroinstall/injector/model.py b/zeroinstall/injector/model.py index bae8496..d747f8e 100644 --- a/zeroinstall/injector/model.py +++ b/zeroinstall/injector/model.py @@ -567,6 +567,14 @@ class Implementation(object): self.commands["run"] = Command(qdom.Element(XMLNS_IFACE, 'command', {'path': path}), None) main = property(_get_main, _set_main) + def is_available(self, stores): + """Is this Implementation available locally? + (a local implementation, an installed distribution package, or a cached ZeroInstallImplementation) + @rtype: bool + @since: 0.53 + """ + raise NotImplementedError("abstract") + class DistributionImplementation(Implementation): """An implementation provided by the distribution. Information such as the version comes from the package manager. @@ -583,6 +591,9 @@ class DistributionImplementation(Implementation): def requires_root_install(self): return not self.installed + def is_available(self, stores): + return self.installed + class ZeroInstallImplementation(Implementation): """An implementation where all the information comes from Zero Install. @ivar digests: a list of "algorith=value" strings (since 0.45) @@ -611,6 +622,14 @@ class ZeroInstallImplementation(Implementation): self.os, self.machine = _split_arch(arch) arch = property(lambda self: _join_arch(self.os, self.machine), set_arch) + def is_available(self, stores): + if self.local_path is not None: + return os.path.exists(self.local_path) + if self.digests: + path = stores.lookup_maybe(self.digests) + return path is not None + return False # (0compile creates fake entries with no digests) + class Interface(object): """An Interface represents some contract of behaviour. @ivar uri: the URI for this interface. diff --git a/zeroinstall/injector/policy.py b/zeroinstall/injector/policy.py index b0956f0..d84bd89 100644 --- a/zeroinstall/injector/policy.py +++ b/zeroinstall/injector/policy.py @@ -307,18 +307,7 @@ class Policy(object): @type impl: model.Implementation @rtype: bool """ - if isinstance(impl, DistributionImplementation): - return impl.installed - if impl.local_path: - return os.path.exists(impl.local_path) - else: - try: - path = self.get_implementation_path(impl) - assert path - return True - except: - pass # OK - return False + return impl.is_available(self.config.stores) def get_uncached_implementations(self): """List all chosen implementations which aren't yet available locally. diff --git a/zeroinstall/injector/selections.py b/zeroinstall/injector/selections.py index 1358f6f..d5cf111 100644 --- a/zeroinstall/injector/selections.py +++ b/zeroinstall/injector/selections.py @@ -43,6 +43,14 @@ class Selection(object): def __repr__(self): return self.id + def is_available(self, stores): + """@since 0.53""" + path = self.local_path + if path is not None: + return os.path.exists(path) + path = stores.lookup_maybe(self.digests) + return path is not None + class ImplSelection(Selection): __slots__ = ['impl', 'dependencies', 'attrs'] diff --git a/zeroinstall/injector/solver.py b/zeroinstall/injector/solver.py index b2e8e4d..df88b28 100644 --- a/zeroinstall/injector/solver.py +++ b/zeroinstall/injector/solver.py @@ -14,28 +14,6 @@ from zeroinstall.zerostore import BadDigest, NotStored from zeroinstall.injector.arch import machine_groups from zeroinstall.injector import model, sat, selections -def _get_cached(stores, impl): - """Check whether an implementation is available locally. - @type impl: model.Implementation - @rtype: bool - """ - if isinstance(impl, model.DistributionImplementation): - return impl.installed - if impl.local_path: - return os.path.exists(impl.local_path) - else: - try: - if not impl.digests: - warn("No digests given for %s!", impl) - return False - path = stores.lookup_any(impl.digests) - assert path - return True - except BadDigest: - return False - except NotStored: - return False - class CommandInfo: def __init__(self, name, command, impl, arch): self.name = name @@ -168,7 +146,7 @@ class SATSolver(Solver): stores = self.config.stores if self.config.network_use != model.network_full: - r = cmp(_get_cached(stores, a), _get_cached(stores, b)) + r = cmp(a.is_available(stores), b.is_available(stores)) if r: return r # Packages that require admin access to install come last @@ -216,7 +194,7 @@ class SATSolver(Solver): # Slightly prefer cached versions if self.config.network_use == model.network_full: - r = cmp(_get_cached(stores, a), _get_cached(stores, b)) + r = cmp(a.is_available(stores), b.is_available(stores)) if r: return r return cmp(a.id, b.id) @@ -305,7 +283,7 @@ class SATSolver(Solver): stability = impl.get_stability() if stability <= model.buggy: return stability.name - if (self.config.network_use == model.network_offline or not impl.download_sources) and not _get_cached(self.config.stores, impl): + if (self.config.network_use == model.network_offline or not impl.download_sources) and not impl.is_available(self.config.stores): if not impl.download_sources: return _("No retrieval methods") return _("Not cached and we are off-line") diff --git a/zeroinstall/zerostore/__init__.py b/zeroinstall/zerostore/__init__.py index 053a977..f931939 100644 --- a/zeroinstall/zerostore/__init__.py +++ b/zeroinstall/zerostore/__init__.py @@ -250,10 +250,21 @@ class Stores(object): self.stores.append(Store(directory)) def lookup(self, digest): + """@deprecated: use lookup_any instead""" return self.lookup_any([digest]) def lookup_any(self, digests): - """Search for digest in all stores.""" + """Search for digest in all stores. + @raises NotStored: if not found""" + path = self.lookup_maybe(digests) + if path: + return path + raise NotStored(_("Item with digests '%(digests)s' not found in stores. Searched:\n- %(stores)s") % + {'digests': digests, 'stores': '\n- '.join([s.dir for s in self.stores])}) + + def lookup_maybe(self, digests): + """Like lookup_any, but return None if it isn't found. + @since: 0.53""" assert digests for digest in digests: assert digest @@ -263,8 +274,7 @@ class Stores(object): path = store.lookup(digest) if path: return path - raise NotStored(_("Item with digests '%(digests)s' not found in stores. Searched:\n- %(stores)s") % - {'digests': digests, 'stores': '\n- '.join([s.dir for s in self.stores])}) + return None def add_dir_to_cache(self, required_digest, dir): """Add to the best writable cache. -- 2.11.4.GIT