diff --git a/pex/resolver.py b/pex/resolver.py index 99a412490..feb4980f4 100644 --- a/pex/resolver.py +++ b/pex/resolver.py @@ -3,6 +3,7 @@ from __future__ import print_function +import itertools import os import shutil import time @@ -238,20 +239,26 @@ def __init__(self, cache, cache_ttl, *args, **kw): # Short-circuiting package iterator. def package_iterator(self, resolvable, existing=None): - iterator = Iterator(fetchers=[Fetcher([self.__cache])]) - packages = self.filter_packages_by_interpreter( - resolvable.compatible(iterator), self._interpreter, self._platform) - - if packages: - if resolvable.exact: - return packages - - if self.__cache_ttl: - packages = self.filter_packages_by_ttl(packages, self.__cache_ttl) - if packages: + packages = [] + if self.__cache_ttl or resolvable.exact: + iterator = Iterator(fetchers=[Fetcher([self.__cache])]) + packages = self.filter_packages_by_interpreter( + resolvable.compatible(iterator), + self._interpreter, + self._platform + ) + + if packages: + if resolvable.exact: return packages - return super(CachingResolver, self).package_iterator(resolvable, existing=existing) + if self.__cache_ttl: + packages = self.filter_packages_by_ttl(packages, self.__cache_ttl) + + return itertools.chain( + packages, + super(CachingResolver, self).package_iterator(resolvable, existing=existing) + ) # Caching sandwich. def build(self, package, options): diff --git a/tests/test_resolver.py b/tests/test_resolver.py index 757191df3..a2dc8a07b 100644 --- a/tests/test_resolver.py +++ b/tests/test_resolver.py @@ -7,6 +7,7 @@ from twitter.common.contextutil import temporary_dir from pex.common import safe_copy +from pex.crawler import Crawler from pex.fetcher import Fetcher from pex.package import EggPackage, SourcePackage from pex.resolvable import ResolvableRequirement @@ -48,6 +49,29 @@ def test_diamond_local_resolve_cached(): assert len(dists) == 2 +def test_cached_dependency_pinned_unpinned_resolution_multi_run(): + # This exercises the issue described here: https://github.com/pantsbuild/pex/issues/178 + project1_0_0 = make_sdist(name='project', version='1.0.0') + project1_1_0 = make_sdist(name='project', version='1.1.0') + + with temporary_dir() as td: + for sdist in (project1_0_0, project1_1_0): + safe_copy(sdist, os.path.join(td, os.path.basename(sdist))) + fetchers = [Fetcher([td])] + with temporary_dir() as cd: + # First run, pinning 1.0.0 in the cache + dists = resolve(['project', 'project==1.0.0'], fetchers=fetchers, cache=cd, cache_ttl=1000) + assert len(dists) == 1 + assert dists[0].version == '1.0.0' + # This simulates separate invocations of pex but allows us to keep the same tmp cache dir + Crawler.reset_cache() + # Second, run, the unbounded 'project' req will find the 1.0.0 in the cache. But should also + # return SourcePackages found in td + dists = resolve(['project', 'project==1.1.0'], fetchers=fetchers, cache=cd, cache_ttl=1000) + assert len(dists) == 1 + assert dists[0].version == '1.1.0' + + def test_resolve_prereleases(): stable_dep = make_sdist(name='dep', version='2.0.0') prerelease_dep = make_sdist(name='dep', version='3.0.0rc3')