Skip to content

Commit

Permalink
Fix --lock for multiplatform via sdists. (#1689)
Browse files Browse the repository at this point in the history
Previously multiplatform PEXes built via a `--lock` that selected sdists
would fail to gather the wheel built from the sdist for each applicable
platform.

Fixes #1688
  • Loading branch information
jsirois authored Mar 24, 2022
1 parent fa91ee4 commit 8c824c1
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 22 deletions.
38 changes: 16 additions & 22 deletions pex/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,7 @@
from pex.util import CacheHelper, DistributionHelper

if TYPE_CHECKING:
from typing import (
DefaultDict,
Dict,
Iterable,
Iterator,
List,
Mapping,
Optional,
Sequence,
Tuple,
)
from typing import DefaultDict, Iterable, Iterator, List, Mapping, Optional, Sequence, Tuple

import attr # vendor:skip

Expand Down Expand Up @@ -82,7 +72,7 @@ def lock(self):
DistMetadata.for_dist(install_request.wheel_path)
for install_request in itertools.chain(
tuple(self.download_result.install_requests()),
build_results.values(),
itertools.chain.from_iterable(build_results.values()),
)
)
locked_resolve = LockedResolve.create(
Expand Down Expand Up @@ -544,9 +534,11 @@ def _categorize_build_requests(
build_requests, # type: Iterable[BuildRequest]
dist_root, # type: str
):
# type: (...) -> Tuple[Iterable[BuildRequest], Dict[str, InstallRequest]]
# type: (...) -> Tuple[Iterable[BuildRequest], DefaultDict[str, OrderedSet[InstallRequest]]]
unsatisfied_build_requests = []
build_results = {} # type: Dict[str, InstallRequest]
build_results = defaultdict(
OrderedSet
) # type: DefaultDict[str, OrderedSet[InstallRequest]]
for build_request in build_requests:
build_result = build_request.result(dist_root)
if not build_result.is_built:
Expand All @@ -560,7 +552,7 @@ def _categorize_build_requests(
build_request.source_path, build_result.dist_dir
)
)
build_results[build_request.source_path] = build_result.finalize_build()
build_results[build_request.source_path].add(build_result.finalize_build())
return unsatisfied_build_requests, build_results

def _spawn_wheel_build(
Expand Down Expand Up @@ -589,7 +581,7 @@ def build_wheels(
workspace=None, # type: Optional[str]
max_parallel_jobs=None, # type: Optional[int]
):
# type: (...) -> Mapping[str, InstallRequest]
# type: (...) -> Mapping[str, OrderedSet[InstallRequest]]

if not build_requests:
# Nothing to build or install.
Expand All @@ -613,7 +605,7 @@ def build_wheels(
error_handler=Raise(Untranslatable),
max_jobs=max_parallel_jobs,
):
build_results[build_result.request.source_path] = build_result.finalize_build()
build_results[build_result.request.source_path].add(build_result.finalize_build())

return build_results

Expand Down Expand Up @@ -718,7 +710,7 @@ def install_distributions(
workspace=workspace,
max_parallel_jobs=max_parallel_jobs,
)
to_install.extend(build_results.values())
to_install.extend(itertools.chain.from_iterable(build_results.values()))

# 2. All requirements are now in wheel form: calculate any missing direct requirement
# project names from the wheel names.
Expand All @@ -734,8 +726,8 @@ def iter_direct_requirements():
yield requirement.requirement
continue

install_req = build_results.get(requirement.path)
if install_req is None:
install_reqs = build_results.get(requirement.path)
if not install_reqs:
raise AssertionError(
"Failed to compute a project name for {requirement}. No corresponding "
"wheel was found from amongst:\n{install_requests}".format(
Expand All @@ -747,12 +739,14 @@ def iter_direct_requirements():
wheel_path=build_result.wheel_path,
fingerprint=build_result.fingerprint,
)
for path, build_result in build_results.items()
for path, build_results in build_results.items()
for build_result in build_results
)
),
)
)
yield requirement.as_requirement(dist=install_req.wheel_path)
for install_req in install_reqs:
yield requirement.as_requirement(dist=install_req.wheel_path)

direct_requirements_by_project_name = defaultdict(
OrderedSet
Expand Down
66 changes: 66 additions & 0 deletions tests/integration/cli/commands/test_issue_1688.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Copyright 2022 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

import os
import subprocess

from pex.cli.testing import run_pex3
from pex.interpreter import PythonInterpreter
from pex.pex_info import PexInfo
from pex.resolve import lockfile
from pex.testing import run_pex_command
from pex.typing import TYPE_CHECKING

if TYPE_CHECKING:
from typing import Any


def test_multiplatform_sdist(
tmpdir, # type: Any
py27, # type: PythonInterpreter
py37, # type: PythonInterpreter
py310, # type: PythonInterpreter
):
# type: (...) -> None

all_interpreters = (py27, py37, py310)
python_path = ":".join((interp.binary for interp in all_interpreters))
interpreter_selection_args = [
"--python-path",
python_path,
"--interpreter-constraint",
">=2.7,<3.11",
]

lock = os.path.join(str(tmpdir), "lock")
run_pex3(
"lock",
"create",
"--style",
"universal",
"--no-wheel",
"psutil==5.9.0",
"-o",
lock,
*interpreter_selection_args
).assert_success()
lock_file = lockfile.load(lock)
assert 1 == len(lock_file.locked_resolves), "Expected 1 resolve for universal style."
locked_resolve = lock_file.locked_resolves[0]
assert 1 == len(
locked_resolve.locked_requirements
), "Expected 1 locked requirement since psutil has no dependencies"
locked_requirement = locked_resolve.locked_requirements[0]
assert 0 == len(
locked_requirement.additional_artifacts
), "Expected just a single sdist artifact since we specified --no-wheel."
assert locked_requirement.artifact.url.endswith(".tar.gz"), "Expected a locked sdist URL."

pex = os.path.join(str(tmpdir), "pex")
run_pex_command(args=["--lock", lock, "-o", pex] + interpreter_selection_args).assert_success()

assert 3 == len(
PexInfo.from_pex(pex).distributions
), "Expected a unique platform-specific wheel to be built for each interpreter"
for interp in all_interpreters:
subprocess.check_call(args=[interp.binary, pex, "-c", "import psutil"])

0 comments on commit 8c824c1

Please sign in to comment.