diff --git a/.appveyor.yml b/.appveyor.yml index 154dd95f1..873ac0c11 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -21,6 +21,8 @@ environment: PIP: 19.2.3 - TOXENV: py27-pip19.3 PIP: 19.3 + - TOXENV: py27-pip20.0 + PIP: 20.0 - TOXENV: py27-pipmaster PIP: master - TOXENV: py27-piplatest-coverage @@ -44,6 +46,8 @@ environment: PIP: 19.2.3 - TOXENV: py35-pip19.3 PIP: 19.3 + - TOXENV: py35-pip20.0 + PIP: 20.0 - TOXENV: py35-pipmaster PIP: master - TOXENV: py35-piplatest @@ -67,6 +71,8 @@ environment: PIP: 19.2.3 - TOXENV: py36-pip19.3 PIP: 19.3 + - TOXENV: py36-pip20.0 + PIP: 20.0 - TOXENV: py36-pipmaster PIP: master - TOXENV: py36-piplatest @@ -90,6 +96,8 @@ environment: PIP: 19.2.3 - TOXENV: py37-pip19.3 PIP: 19.3 + - TOXENV: py37-pip20.0 + PIP: 20.0 - TOXENV: py37-pipmaster-coverage PIP: master - TOXENV: py37-piplatest-coverage @@ -111,6 +119,8 @@ environment: PIP: 19.2.3 - TOXENV: py38-pip19.3-coverage PIP: 19.3 + - TOXENV: py38-pip20.0 + PIP: 20.0 - TOXENV: py38-pipmaster-coverage PIP: master - TOXENV: py38-piplatest-coverage diff --git a/.travis.yml b/.travis.yml index 39464d1b7..d56a8e77d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,6 +19,7 @@ env: - PIP=19.1 - PIP=19.2.3 - PIP=19.3 + - PIP=20.0 - PIP=latest - PIP=master diff --git a/piptools/_compat/__init__.py b/piptools/_compat/__init__.py index eccbf365c..a26e53185 100644 --- a/piptools/_compat/__init__.py +++ b/piptools/_compat/__init__.py @@ -16,12 +16,13 @@ PackageFinder, PyPI, RequirementSet, - RequirementTracker, Resolver, Wheel, WheelCache, cmdoptions, get_installed_distributions, + get_requirement_tracker, + global_tempdir_manager, install_req_from_editable, install_req_from_line, is_dir_url, diff --git a/piptools/_compat/pip_compat.py b/piptools/_compat/pip_compat.py index 67da3071d..1e45d3a9c 100644 --- a/piptools/_compat/pip_compat.py +++ b/piptools/_compat/pip_compat.py @@ -18,6 +18,16 @@ def RequirementTracker(): yield +# Introduced in pip 20.0 +try: + from pip._internal.utils.temp_dir import global_tempdir_manager +except ImportError: + + @contextmanager + def global_tempdir_manager(): + yield + + def do_import(module_path, subimport=None, old_path=None): old_path = old_path or module_path prefixes = ["pip._internal", "pip"] @@ -50,7 +60,7 @@ def do_import(module_path, subimport=None, old_path=None): PackageFinder = do_import("index.package_finder", "PackageFinder", old_path="index") FormatControl = do_import("models.format_control", "FormatControl", old_path="index") InstallCommand = do_import("commands.install", "InstallCommand") -Wheel = do_import("wheel", "Wheel") +Wheel = do_import("models.wheel", "Wheel", old_path="wheel") cmdoptions = do_import("cli.cmdoptions", old_path="cmdoptions") get_installed_distributions = do_import( "utils.misc", "get_installed_distributions", old_path="utils" @@ -96,3 +106,12 @@ def is_dir_url(link): return _is_dir_url(link) return link.is_existing_dir() + + +def get_requirement_tracker(): + if PIP_VERSION[:2] <= (19, 3): + return RequirementTracker() + + from pip._internal.req import req_tracker + + return req_tracker.get_requirement_tracker() diff --git a/piptools/repositories/pypi.py b/piptools/repositories/pypi.py index caa3be73a..2aa2a1df6 100644 --- a/piptools/repositories/pypi.py +++ b/piptools/repositories/pypi.py @@ -14,12 +14,13 @@ Link, PyPI, RequirementSet, - RequirementTracker, Resolver as PipResolver, TemporaryDirectory, Wheel, WheelCache, contextlib, + get_requirement_tracker, + global_tempdir_manager, is_dir_url, is_file_url, is_vcs_url, @@ -59,11 +60,11 @@ def __init__(self, pip_args, cache_dir, build_isolation=False): # Use pip's parser for pip.conf management and defaults. # General options (find_links, index_url, extra_index_url, trusted_host, # and pre) are deferred to pip. - command = create_install_command() - self.options, _ = command.parse_args(pip_args) + self.command = create_install_command() + self.options, _ = self.command.parse_args(pip_args) - self.session = command._build_session(self.options) - self.finder = command._build_package_finder( + self.session = self.command._build_session(self.options) + self.finder = self.command._build_package_finder( options=self.options, session=self.session ) @@ -148,9 +149,14 @@ def find_best_match(self, ireq, prereleases=None): ) best_candidate = best_candidate_result.best_candidate + if PIP_VERSION[:2] <= (19, 3): + best_candidate_name = best_candidate.project + else: + best_candidate_name = best_candidate.name + # Turn the candidate into a pinned InstallRequirement return make_install_requirement( - best_candidate.project, + best_candidate_name, best_candidate.version, ireq.extras, constraint=ireq.constraint, @@ -203,28 +209,44 @@ def resolve_reqs(self, download_dir, ireq, wheel_cache): resolver_kwargs["make_install_req"] = make_install_req if PIP_VERSION >= (20,): - preparer_kwargs["session"] = self.session del resolver_kwargs["session"] + del preparer_kwargs["progress_bar"] resolver = None preparer = None - with RequirementTracker() as req_tracker: + + if PIP_VERSION[:2] <= (19, 3): + tmp_dir_cm = contextlib.nullcontext() + else: + from pip._internal.utils.temp_dir import TempDirectory + + tmp_dir_cm = TempDirectory(kind="req-tracker") + + with get_requirement_tracker() as req_tracker, tmp_dir_cm as temp_build_dir: # Pip 18 uses a requirement tracker to prevent fork bombs if req_tracker: preparer_kwargs["req_tracker"] = req_tracker - preparer = RequirementPreparer(**preparer_kwargs) + + if PIP_VERSION[:2] <= (19, 3): + preparer = RequirementPreparer(**preparer_kwargs) + else: + preparer = self.command.make_requirement_preparer( + temp_build_dir=temp_build_dir, + options=self.options, + req_tracker=req_tracker, + session=self.session, + finder=self.finder, + use_user_site=self.options.use_user_site, + ) + resolver_kwargs["preparer"] = preparer reqset = RequirementSet() ireq.is_direct = True reqset.add_requirement(ireq) resolver = PipResolver(**resolver_kwargs) - require_hashes = False - if PIP_VERSION < (20,): - resolver.require_hashes = require_hashes - results = resolver._resolve_one(reqset, ireq) - else: - results = resolver._resolve_one(reqset, ireq, require_hashes) + resolver.require_hashes = False + results = resolver._resolve_one(reqset, ireq) reqset.cleanup_files() @@ -265,9 +287,10 @@ def get_dependencies(self, ireq): wheel_cache = WheelCache(self._cache_dir, self.options.format_control) prev_tracker = os.environ.get("PIP_REQ_TRACKER") try: - self._dependencies_cache[ireq] = self.resolve_reqs( - download_dir, ireq, wheel_cache - ) + with global_tempdir_manager(): + self._dependencies_cache[ireq] = self.resolve_reqs( + download_dir, ireq, wheel_cache + ) finally: if "PIP_REQ_TRACKER" in os.environ: if prev_tracker: diff --git a/tox.ini b/tox.ini index 6130a78de..29c0720d1 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] envlist = # NOTE: keep this in sync with the env list in .travis.yml for tox-travis. - py{27,35,36,37,38,py,py3}-pip{8.1.1,9.0.1,9.0.3,10.0.1,18.0,19.0.3,19.1,19.2.3,19.3,latest,master}-coverage + py{27,35,36,37,38,py,py3}-pip{8.1.1,9.0.1,9.0.3,10.0.1,18.0,19.0.3,19.1,19.2.3,19.3,20.0,latest,master}-coverage checkqa readme skip_missing_interpreters = True @@ -18,6 +18,7 @@ deps = pip19.1: pip==19.1 pip19.2.3: pip==19.2.3 pip19.3: pip==19.3 + pip20.0: -e git+https://github.com/pypa/pip.git@master#egg=pip mock pytest!=5.1.2 pytest-rerunfailures @@ -34,6 +35,7 @@ setenv = pip19.1: PIP==19.1 pip19.2.3: PIP==19.2.3 pip19.3: PIP==19.3 + pip20.0: PIP==20.0 coverage: PYTEST_ADDOPTS=--strict --doctest-modules --cov --cov-report=term-missing {env:PYTEST_ADDOPTS:} commands_pre = @@ -65,5 +67,6 @@ PIP = 19.1: pip19.1 19.2.3: pip19.2.3 19.3: pip19.3 + 20.0: pip20.0 latest: piplatest master: pipmaster