From e7ea2ea7506e9d3d2e334428d23113fe0a802d75 Mon Sep 17 00:00:00 2001 From: John Sirois Date: Fri, 6 Nov 2020 20:16:12 -0800 Subject: [PATCH 1/2] Fix spurious `InstalledDistribution` env markers. Elide environment markers for arbitrary equality (distributions with non-standard versions). Fixes #940 --- pex/resolver.py | 13 +++++++++++++ pex/testing.py | 5 +++++ tests/test_integration.py | 16 ++++++++++++++++ tests/test_resolver.py | 15 +++++++++++++++ 4 files changed, 49 insertions(+) diff --git a/pex/resolver.py b/pex/resolver.py index 6afa2361a..947adf3ed 100644 --- a/pex/resolver.py +++ b/pex/resolver.py @@ -109,6 +109,19 @@ def __init__(self, markers_by_requirement_key): def to_requirement(self, dist): req = dist.as_requirement() + # pkg_resources.Distribution.as_requirement returns requirements in one of two forms: + # 1.) project_name==version + # 2.) project_name===version + # The latter form is used whenever the distribution's version is non-standard. In those + # cases we cannot append environment markers since `===` indicates a raw version string to + # the right that should not be parsed and instead should be compared literally in full. + # See: + # + https://www.python.org/dev/peps/pep-0440/#arbitrary-equality + # + https://github.com/pantsbuild/pex/issues/940 + operator, _ = req.specs[0] + if operator == "===": + return req + markers = OrderedSet() # Here we map any wheel python requirement to the equivalent environment marker: diff --git a/pex/testing.py b/pex/testing.py index 2d22a9b4b..a825c227d 100644 --- a/pex/testing.py +++ b/pex/testing.py @@ -110,6 +110,7 @@ def make_project( install_reqs=None, # type: Optional[List[str]] extras_require=None, # type: Optional[Dict[str, List[str]]] entry_points=None, # type: Optional[Union[str, Dict[str, List[str]]]] + python_requires=None, # type: Optional[str] ): # type: (...) -> Iterator[str] project_content = { @@ -130,6 +131,7 @@ def make_project( install_requires=%(install_requires)r, extras_require=%(extras_require)r, entry_points=%(entry_points)r, + python_requires=%(python_requires)r, ) """ ), @@ -148,6 +150,7 @@ def make_project( "install_requires": install_reqs or [], "extras_require": extras_require or {}, "entry_points": entry_points or {}, + "python_requires": python_requires, } with temporary_content(project_content, interp=interp) as td: @@ -190,6 +193,7 @@ def built_wheel( install_reqs=None, # type: Optional[List[str]] extras_require=None, # type: Optional[Dict[str, List[str]]] interpreter=None, # type: Optional[PythonInterpreter] + python_requires=None, # type: Optional[str] **kwargs # type: Any ): # type: (...) -> Iterator[str] @@ -199,6 +203,7 @@ def built_wheel( zip_safe=zip_safe, install_reqs=install_reqs, extras_require=extras_require, + python_requires=python_requires, ) as td: builder = WheelBuilder(td, interpreter=interpreter, **kwargs) yield builder.bdist() diff --git a/tests/test_integration.py b/tests/test_integration.py index 07429c234..cc81484f3 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -2393,3 +2393,19 @@ def test_tmpdir_file(tmp_workdir): result.assert_failure() assert tmpdir_file in result.error assert "is not a directory" in result.error + + +def test_resolve_arbitrary_equality_issues_940(): + # type: () -> None + with temporary_dir() as tmpdir, built_wheel( + name="foo", + version="1.0.2-fba4511", + python_requires=">=2.7,<=3.9,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*", + ) as whl: + pex_file = os.path.join(tmpdir, "pex") + results = run_pex_command(args=["-o", pex_file, whl]) + results.assert_success() + + stdout, returncode = run_simple_pex(pex_file, args=["-c", "import foo"]) + assert returncode == 0 + assert stdout == b"" diff --git a/tests/test_resolver.py b/tests/test_resolver.py index f97ffdb22..c85166faa 100644 --- a/tests/test_resolver.py +++ b/tests/test_resolver.py @@ -507,3 +507,18 @@ def test_install_invalid_local_distribution(): install( [LocalDistribution.create(project1_wheel, fingerprint=valid_local_sdist.fingerprint)] ) + + +def test_resolve_arbitrary_equality_issues_940(): + # type: () -> None + dist = create_sdist( + name="foo", + version="1.0.2-fba4511", + python_requires=">=2.7,<=3.9,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*", + ) + resolved_distributions = local_resolve_multi(requirements=[dist]) + + assert len(resolved_distributions) == 1 + requirement = resolved_distributions[0].requirement + assert [("===", "1.0.2-fba4511")] == requirement.specs + assert requirement.marker is None From 1f739d3825b051080ca4a24ff51063d70dc0c5f0 Mon Sep 17 00:00:00 2001 From: John Sirois Date: Sat, 7 Nov 2020 07:06:25 -0800 Subject: [PATCH 2/2] Fix tmp_workdir fixture to restore CWD. This was previously leaving CWD to a deleted tmp dir poisoning downstream tests. --- tests/test_integration.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_integration.py b/tests/test_integration.py index cc81484f3..72d7b02ec 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -2348,9 +2348,13 @@ def create_platform_pex(args): @pytest.fixture def tmp_workdir(): # type: () -> Iterator[str] + cwd = os.getcwd() with temporary_dir() as tmpdir: os.chdir(tmpdir) - yield os.path.realpath(tmpdir) + try: + yield os.path.realpath(tmpdir) + finally: + os.chdir(cwd) def test_tmpdir_absolute(tmp_workdir):