From cdd26f9adc5fef8a836998b694be2d069d3ffd23 Mon Sep 17 00:00:00 2001 From: John Sirois Date: Wed, 2 Jun 2021 10:19:38 -0700 Subject: [PATCH] Fix Pex emitting warnings about its Pip PEX venv. (#1351) Previously we'd see: ``` rm -rf ~/.pex $ pex pex -cpex -opex.pex /home/jsirois/.venv/pex/lib/python3.9/site-packages/pex/tools/commands/venv.py:141: PEXWarning: Encountered collision building venv at /home/jsirois/.pex/venvs/short/4bd8ddd4 from /home/jsirois/.pex/pip.pex/46820cb5af0dcf9295a4e7f30184cc0e9fa063dc: 1. /home/jsirois/.pex/venvs/7fbdc9731d96d42f0de338b2dfbc6bbed1c938e7/832e95f85413646e8b7b056ab4e60414f83d7397.01034f69a38943809913ffdfd28dd5bf/lib/python3.9/site-packages/constraints.txt was provided by: /home/jsirois/.pex/pip.pex/46820cb5af0dcf9295a4e7f30184cc0e9fa063dc/.deps/setuptools/constraints.txt /home/jsirois/.pex/pip.pex/46820cb5af0dcf9295a4e7f30184cc0e9fa063dc/.deps/wheel/constraints.txt pex_warnings.warn(message) ``` --- pex/third_party/__init__.py | 13 ++++++++++++- pex/vendor/__init__.py | 6 +++++- tests/test_pip.py | 35 +++++++++++++++++++++++++++++++++++ tests/test_third_party.py | 12 ++++++++++++ 4 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 tests/test_pip.py diff --git a/pex/third_party/__init__.py b/pex/third_party/__init__.py index eb7c35ea2..53679c5a1 100644 --- a/pex/third_party/__init__.py +++ b/pex/third_party/__init__.py @@ -353,6 +353,13 @@ def isolated(): module = "pex" + # These files are only used for running `tox -evendor` and should not pollute either the + # PEX_ROOT or built PEXs. + vendor_lockfiles = tuple( + os.path.join(os.path.relpath(vendor_spec.relpath, module), "constraints.txt") + for vendor_spec in vendor.iter_vendor_specs() + ) + # TODO(John Sirois): Unify with `pex.util.DistributionHelper.access_zipped_assets`. def recursive_copy(srcdir, dstdir): os.mkdir(dstdir) @@ -370,7 +377,11 @@ def recursive_copy(srcdir, dstdir): if os.path.basename(src_entry) == "__pycache__": continue recursive_copy(src_entry, dst_entry) - elif not entry_name.endswith(".pyc") and not is_pyc_temporary_file(entry_name): + elif ( + not entry_name.endswith(".pyc") + and not is_pyc_temporary_file(entry_name) + and src_entry not in vendor_lockfiles + ): with open(dst_entry, "wb") as fp: with closing(resource_stream(module, src_entry)) as resource: shutil.copyfileobj(resource, fp) diff --git a/pex/vendor/__init__.py b/pex/vendor/__init__.py index 92be011bd..d458ff2c2 100644 --- a/pex/vendor/__init__.py +++ b/pex/vendor/__init__.py @@ -9,6 +9,10 @@ from pex.common import filter_pyc_dirs, filter_pyc_files, touch from pex.compatibility import urlparse from pex.tracer import TRACER +from pex.typing import TYPE_CHECKING + +if TYPE_CHECKING: + from typing import Iterator _PACKAGE_COMPONENTS = __name__.split(".") @@ -100,10 +104,10 @@ def create_packages(self): def iter_vendor_specs(): + # type: () -> Iterator[VendorSpec] """Iterate specifications for code vendored by pex. :return: An iterator over specs of all vendored code. - :rtype: :class:`collection.Iterator` of :class:`VendorSpec` """ # We use this for a better @dataclass that is also Python2.7 and PyPy compatible. # N.B.: The `[testenv:typecheck]` section in `tox.ini` should have its deps list updated to diff --git a/tests/test_pip.py b/tests/test_pip.py new file mode 100644 index 000000000..da98a8efd --- /dev/null +++ b/tests/test_pip.py @@ -0,0 +1,35 @@ +# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). + +from __future__ import absolute_import + +import os +import warnings + +from pex.interpreter import PythonInterpreter +from pex.pip import Pip +from pex.typing import TYPE_CHECKING +from pex.variables import ENV + +if TYPE_CHECKING: + from typing import Any + + +def test_no_duplicate_constraints_pex_warnings(tmpdir): + # type: (Any) -> None + pex_root = os.path.join(str(tmpdir), "pex_root") + pip_root = os.path.join(str(tmpdir), "pip_root") + interpreter = PythonInterpreter.get() + platform = interpreter.platform + + with ENV.patch(PEX_ROOT=pex_root), warnings.catch_warnings(record=True) as events: + pip = Pip.create(path=pip_root, interpreter=interpreter) + + pip.spawn_debug( + platform=platform.platform, impl=platform.impl, version=platform.version, abi=platform.abi + ).wait() + + assert 0 == len([event for event in events if "constraints.txt" in str(event)]), ( + "Expected no duplicate constraints warnings to be emitted when creating a Pip venv but " + "found\n{}".format("\n".join(map(str, events))) + ) diff --git a/tests/test_third_party.py b/tests/test_third_party.py index 7e4d0ac2b..d4b43b29d 100644 --- a/tests/test_third_party.py +++ b/tests/test_third_party.py @@ -34,6 +34,18 @@ def test_isolated_pex_root(): assert pex_root == os.path.commonprefix([pex_root, devendored_chroot]) +def test_isolated_vendoring_constraints_omitted(): + # type: () -> None + with temporary_pex_root() as (pex_root, _): + devendored_chroot = os.path.realpath(third_party.isolated().chroot_path) + assert [] == [ + os.path.join(root, file) + for root, _, files in os.walk(devendored_chroot) + for file in files + if file == "constraints.txt" + ] + + def test_isolated_idempotent_inprocess(): # type: () -> None with temporary_pex_root():