def test_resolver_blacklist(): if PY2: blacklist = {'project2': '<3'} required_project = "project2;python_version>'3'" else: blacklist = {'project2': '>3'} required_project = "project2;python_version<'3'" project1 = make_sdist(name='project1', version='1.0.0', install_reqs=[required_project]) project2 = make_sdist(name='project2', version='1.1.0') with temporary_dir() as td: safe_copy(project1, os.path.join(td, os.path.basename(project1))) safe_copy(project2, os.path.join(td, os.path.basename(project2))) fetchers = [Fetcher([td])] dists = resolve(['project1'], fetchers=fetchers) assert len(dists) == 2 dists = resolve(['project1'], fetchers=fetchers, pkg_blacklist=blacklist) assert len(dists) == 1
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' # Third run, if exact resolvable and inexact resolvable, and cache_ttl is expired, exact # resolvable should pull from pypi as well since inexact will and the resulting # resolvable_set.merge() would fail. Crawler.reset_cache() time.sleep(1) dists = resolve(['project', 'project==1.1.0'], fetchers=fetchers, cache=cd, cache_ttl=1) assert len(dists) == 1 assert dists[0].version == '1.1.0'
def test_resolve_prereleases_cached(): stable_dep = make_sdist(name='dep', version='2.0.0') prerelease_dep = make_sdist(name='dep', version='3.0.0rc3') with temporary_dir() as td: for sdist in (stable_dep, prerelease_dep): safe_copy(sdist, os.path.join(td, os.path.basename(sdist))) fetchers = [Fetcher([td])] with temporary_dir() as cd: def assert_resolve(dep, expected_version, **resolve_kwargs): dists = list( resolve_multi([dep], cache=cd, cache_ttl=1000, **resolve_kwargs) ) assert 1 == len(dists) dist = dists[0] assert expected_version == dist.version Crawler.reset_cache() # First do a run to load it into the cache. assert_resolve('dep>=1,<4', '3.0.0rc3', allow_prereleases=True, fetchers=fetchers) # This simulates running from another pex command. The Crawler cache actually caches an empty # cache so this fails in the same "process". Crawler.reset_cache() # Now assert that we can get it from the cache by removing the source. assert_resolve('dep>=1,<4', '3.0.0rc3', allow_prereleases=True, fetchers=[]) # It should also be able to resolve without allow_prereleases, if explicitly requested. Crawler.reset_cache() assert_resolve('dep>=1.rc1,<4', '3.0.0rc3', fetchers=[])
def test_resolve_prereleases_multiple_set(): stable_dep = make_sdist(name='dep', version='2.0.0') prerelease_dep1 = make_sdist(name='dep', version='3.0.0rc3') prerelease_dep2 = make_sdist(name='dep', version='3.0.0rc4') prerelease_dep3 = make_sdist(name='dep', version='3.0.0rc5') with temporary_dir() as td: for sdist in (stable_dep, prerelease_dep1, prerelease_dep2, prerelease_dep3): safe_copy(sdist, os.path.join(td, os.path.basename(sdist))) fetchers = [Fetcher([td])] def assert_resolve(expected_version, **resolve_kwargs): dists = resolve( [ 'dep>=3.0.0rc1', 'dep==3.0.0rc4', ], fetchers=fetchers, **resolve_kwargs) assert 1 == len(dists) dist = dists[0] assert expected_version == dist.version # This should resolve with explicit prerelease being set or implicitly. assert_resolve('3.0.0rc4', allow_prereleases=True) assert_resolve('3.0.0rc4')
def test_diamond_local_resolve_cached(): # This exercises the issue described here: https://github.com/pantsbuild/pex/issues/120 project1_sdist = make_sdist(name='project1', install_reqs=['project2<1.0.0']) project2_sdist = make_sdist(name='project2') with temporary_dir() as dd: for sdist in (project1_sdist, project2_sdist): safe_copy(sdist, os.path.join(dd, os.path.basename(sdist))) fetchers = [Fetcher([dd])] with temporary_dir() as cd: dists = resolve(['project1', 'project2'], fetchers=fetchers, cache=cd, cache_ttl=1000) assert len(dists) == 2
def test_resolve_extra_sdist(): project1_sdist = make_sdist(name='project1', version='1.0.0', extras_require={'foo': ['project2']}) project2_sdist = make_sdist(name='project2', version='2.0.0') with temporary_dir() as td: for sdist in (project1_sdist, project2_sdist): safe_copy(sdist, os.path.join(td, os.path.basename(sdist))) fetchers = [Fetcher([td])] resolved_dists = do_resolve_multi(['project1[foo]'], fetchers=fetchers) assert ({_parse_requirement(req) for req in ('project1[foo]', 'project2; extra=="foo"')} == {_parse_requirement(resolved_dist.requirement) for resolved_dist in resolved_dists})
def test_resolve_prereleases_and_no_version(): prerelease_dep = make_sdist(name='dep', version='3.0.0rc3') with temporary_dir() as td: safe_copy(prerelease_dep, os.path.join(td, os.path.basename(prerelease_dep))) fetchers = [Fetcher([td])] def assert_resolve(deps, expected_version, **resolve_kwargs): dists = list( resolve_multi(deps, fetchers=fetchers, **resolve_kwargs) ) assert 1 == len(dists) dist = dists[0] assert expected_version == dist.version # When allow_prereleases is specified, the requirement (from two dependencies) # for a specific pre-release version and no version specified, accepts the pre-release # version correctly. assert_resolve(['dep==3.0.0rc3', 'dep'], '3.0.0rc3', allow_prereleases=True) # Without allow_prereleases set, the pre-release version is rejected. # This used to be an issue when a command-line use did not pass the `--pre` option # correctly into the API call for resolve_multi() from build_pex() in pex.py. with pytest.raises(Unsatisfiable): assert_resolve(['dep==3.0.0rc3', 'dep'], '3.0.0rc3')
def test_ambiguous_transitive_resolvable(): # If an unbounded or larger bounded resolvable is resolved first, and a # transitive resolvable is resolved later in another round, Error(Ambiguous resolvable) can be # raised because foo pulls in foo-2.0.0 and bar->foo==1.0.0 pulls in foo-1.0.0. foo1_0 = make_sdist(name='foo', version='1.0.0') foo2_0 = make_sdist(name='foo', version='2.0.0') bar1_0 = make_sdist(name='bar', version='1.0.0', install_reqs=['foo==1.0.0']) with temporary_dir() as td: for sdist in (foo1_0, foo2_0, bar1_0): safe_copy(sdist, os.path.join(td, os.path.basename(sdist))) fetchers = [Fetcher([td])] with temporary_dir() as cd: dists = resolve(['foo', 'bar'], fetchers=fetchers, cache=cd, cache_ttl=1000) assert len(dists) == 2 assert dists[0].version == '1.0.0'
def test_simple_local_resolve(): project_sdist = make_sdist(name='project') with temporary_dir() as td: safe_copy(project_sdist, os.path.join(td, os.path.basename(project_sdist))) fetchers = [Fetcher([td])] dists = resolve(['project'], fetchers=fetchers) assert len(dists) == 1
def test_intransitive(): foo1_0 = make_sdist(name='foo', version='1.0.0') # The nonexistent req ensures that we are actually not acting transitively (as that would fail). bar1_0 = make_sdist(name='bar', version='1.0.0', install_reqs=['nonexistent==1.0.0']) with temporary_dir() as td: for sdist in (foo1_0, bar1_0): safe_copy(sdist, os.path.join(td, os.path.basename(sdist))) fetchers = [Fetcher([td])] with temporary_dir() as cd: resolved_dists = do_resolve_multi(['foo', 'bar'], fetchers=fetchers, cache=cd, cache_ttl=1000, transitive=False) assert len(resolved_dists) == 2
def test_simple_local_resolve(): project_sdist = make_sdist(name='project') with temporary_dir() as td: safe_copy(project_sdist, os.path.join(td, os.path.basename(project_sdist))) fetchers = [Fetcher([td])] dists = list(resolve_multi(['project'], fetchers=fetchers)) assert len(dists) == 1
def test_clp_prereleases_resolver(): prerelease_dep = make_sdist(name='dep', version='1.2.3b1') with temporary_dir() as td: safe_copy(prerelease_dep, os.path.join(td, os.path.basename(prerelease_dep))) fetcher = Fetcher([td]) # When no specific options are specified, allow_prereleases is None parser, resolver_options_builder = configure_clp() assert resolver_options_builder._allow_prereleases is None # When we specify `--pre`, allow_prereleases is True options, reqs = parser.parse_args(args=['--pre', 'dep==1.2.3b1', 'dep']) assert resolver_options_builder._allow_prereleases # We need to use our own fetcher instead of PyPI resolver_options_builder._fetchers.insert(0, fetcher) ##### # The resolver created during processing of command line options (configure_clp) # is not actually passed into the API call (resolve_multi) from build_pex(). # Instead, resolve_multi() calls resolve() where a new ResolverOptionsBuilder instance # is created. The only way to supply our own fetcher to that new instance is to patch it # here in the test so that it can fetch our test package (dep-1.2.3b1). Hence, this class # below and the change in the `pex.resolver` module where the patched object resides. # import pex.resolver class BuilderWithFetcher(ResolverOptionsBuilder): def __init__(self, fetchers=None, allow_all_external=False, allow_external=None, allow_unverified=None, allow_prereleases=None, precedence=None, context=None ): super(BuilderWithFetcher, self).__init__(fetchers=fetchers, allow_all_external=allow_all_external, allow_external=allow_external, allow_unverified=allow_unverified, allow_prereleases=allow_prereleases, precedence=precedence, context=context) self._fetchers.insert(0, fetcher) # end stub ##### # Without a corresponding fix in pex.py, this test failed for a dependency requirement of # dep==1.2.3b1 from one package and just dep (any version accepted) from another package. # The failure was an exit from build_pex() with the message: # # Could not satisfy all requirements for dep==1.2.3b1: # dep==1.2.3b1, dep # # With a correct behavior the assert line is reached and pex_builder object created. with mock.patch.object(pex.resolver, 'ResolverOptionsBuilder', BuilderWithFetcher): pex_builder = build_pex(reqs, options, resolver_options_builder) assert pex_builder is not None
def test_resolve_prereleases(): stable_dep = make_sdist(name='dep', version='2.0.0') prerelease_dep = make_sdist(name='dep', version='3.0.0rc3') with temporary_dir() as td: for sdist in (stable_dep, prerelease_dep): safe_copy(sdist, os.path.join(td, os.path.basename(sdist))) fetchers = [Fetcher([td])] def assert_resolve(expected_version, **resolve_kwargs): dists = resolve(['dep>=1,<4'], fetchers=fetchers, **resolve_kwargs) assert 1 == len(dists) dist = dists[0] assert expected_version == dist.version assert_resolve('2.0.0') assert_resolve('2.0.0', allow_prereleases=False) assert_resolve('3.0.0rc3', allow_prereleases=True)
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 = list( resolve_multi(['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 = list( resolve_multi(['project', 'project==1.1.0'], fetchers=fetchers, cache=cd, cache_ttl=1000) ) assert len(dists) == 1 assert dists[0].version == '1.1.0' # Third run, if exact resolvable and inexact resolvable, and cache_ttl is expired, exact # resolvable should pull from pypi as well since inexact will and the resulting # resolvable_set.merge() would fail. Crawler.reset_cache() time.sleep(1) dists = list( resolve_multi(['project', 'project==1.1.0'], fetchers=fetchers, cache=cd, cache_ttl=1) ) assert len(dists) == 1 assert dists[0].version == '1.1.0'
def test_ambiguous_transitive_resolvable(): # If an unbounded or larger bounded resolvable is resolved first, and a # transitive resolvable is resolved later in another round, Error(Ambiguous resolvable) can be # raised because foo pulls in foo-2.0.0 and bar->foo==1.0.0 pulls in foo-1.0.0. foo1_0 = make_sdist(name='foo', version='1.0.0') foo2_0 = make_sdist(name='foo', version='2.0.0') bar1_0 = make_sdist(name='bar', version='1.0.0', install_reqs=['foo==1.0.0']) with temporary_dir() as td: for sdist in (foo1_0, foo2_0, bar1_0): safe_copy(sdist, os.path.join(td, os.path.basename(sdist))) fetchers = [Fetcher([td])] with temporary_dir() as cd: dists = list( resolve_multi(['foo', 'bar'], fetchers=fetchers, cache=cd, cache_ttl=1000) ) assert len(dists) == 2 assert dists[0].version == '1.0.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') with temporary_dir() as td: for sdist in (stable_dep, prerelease_dep): safe_copy(sdist, os.path.join(td, os.path.basename(sdist))) fetchers = [Fetcher([td])] def assert_resolve(expected_version, **resolve_kwargs): dists = list( resolve_multi(['dep>=1,<4'], fetchers=fetchers, **resolve_kwargs) ) assert 1 == len(dists) dist = dists[0] assert expected_version == dist.version assert_resolve('2.0.0') assert_resolve('2.0.0', allow_prereleases=False) assert_resolve('3.0.0rc3', allow_prereleases=True)
def test_resolve_prereleases_multiple_set(): stable_dep = make_sdist(name='dep', version='2.0.0') prerelease_dep1 = make_sdist(name='dep', version='3.0.0rc3') prerelease_dep2 = make_sdist(name='dep', version='3.0.0rc4') prerelease_dep3 = make_sdist(name='dep', version='3.0.0rc5') with temporary_dir() as td: for sdist in (stable_dep, prerelease_dep1, prerelease_dep2, prerelease_dep3): safe_copy(sdist, os.path.join(td, os.path.basename(sdist))) fetchers = [Fetcher([td])] def assert_resolve(expected_version, **resolve_kwargs): dists = list( resolve_multi(['dep>=3.0.0rc1', 'dep==3.0.0rc4'], fetchers=fetchers, **resolve_kwargs) ) assert 1 == len(dists) dist = dists[0] assert expected_version == dist.version # This should resolve with explicit prerelease being set or implicitly. assert_resolve('3.0.0rc4', allow_prereleases=True) assert_resolve('3.0.0rc4')
def test_clp_prereleases_resolver(): prerelease_dep = make_sdist(name='dep', version='1.2.3b1') with temporary_dir() as td: safe_copy(prerelease_dep, os.path.join(td, os.path.basename(prerelease_dep))) fetcher = Fetcher([td]) # When no specific options are specified, allow_prereleases is None parser, resolver_options_builder = configure_clp() assert resolver_options_builder._allow_prereleases is None # When we specify `--pre`, allow_prereleases is True options, reqs = parser.parse_args( args=['--pre', 'dep==1.2.3b1', 'dep']) assert resolver_options_builder._allow_prereleases # We need to use our own fetcher instead of PyPI resolver_options_builder._fetchers.insert(0, fetcher) ##### # The resolver created during processing of command line options (configure_clp) # is not actually passed into the API call (resolve_multi) from build_pex(). # Instead, resolve_multi() calls resolve() where a new ResolverOptionsBuilder instance # is created. The only way to supply our own fetcher to that new instance is to patch it # here in the test so that it can fetch our test package (dep-1.2.3b1). Hence, this class # below and the change in the `pex.resolver` module where the patched object resides. # import pex.resolver class BuilderWithFetcher(ResolverOptionsBuilder): def __init__(self, fetchers=None, allow_all_external=False, allow_external=None, allow_unverified=None, allow_prereleases=None, use_manylinux=None, precedence=None, context=None): super(BuilderWithFetcher, self).__init__(fetchers=fetchers, allow_all_external=allow_all_external, allow_external=allow_external, allow_unverified=allow_unverified, allow_prereleases=allow_prereleases, use_manylinux=None, precedence=precedence, context=context) self._fetchers.insert(0, fetcher) # end stub ##### # Without a corresponding fix in pex.py, this test failed for a dependency requirement of # dep==1.2.3b1 from one package and just dep (any version accepted) from another package. # The failure was an exit from build_pex() with the message: # # Could not satisfy all requirements for dep==1.2.3b1: # dep==1.2.3b1, dep # # With a correct behavior the assert line is reached and pex_builder object created. with mock.patch.object(pex.resolver, 'ResolverOptionsBuilder', BuilderWithFetcher): pex_builder = build_pex(reqs, options, resolver_options_builder) assert pex_builder is not None