Skip to content

Commit

Permalink
Fix bdist_pex to use --project. (#2457)
Browse files Browse the repository at this point in the history
This change produces the same results for existing invocations of
`python setup.py bdist_pex` but allows new uses passing locked project
requirements (either hashed requirement files or Pex lock files) via
`--pex-args`.

Fixes #2412
  • Loading branch information
jsirois authored Jul 8, 2024
1 parent 1d21164 commit 0674b79
Show file tree
Hide file tree
Showing 4 changed files with 270 additions and 2 deletions.
11 changes: 11 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Release Notes

## 2.8.1

This release fixes the `bdist_pex` distutils command to use the
`--project` option introduced by #2455 in the 2.8.0 release. This
change produces the same results for existing invocations of
`python setup.py bdist_pex` but allows new uses passing locked project
requirements (either hashed requirement files or Pex lock files) via
`--pex-args`.

* Fix `bdist_pex` to use `--project`. (#2457)

## 2.8.0

This release adds a new `--override` option to resolves that ultimately
Expand Down
2 changes: 1 addition & 1 deletion pex/distutils/commands/bdist_pex.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def run(self):
target = os.path.join(self.bdist_dir, name + "-" + version + ".pex")
pex_specs.append((name if name in console_scripts else None, target))

args = ["-m", "pex", package_dir] + options.requirements + self.pex_args
args = ["-m", "pex", "--project", package_dir] + options.requirements + self.pex_args
if self.get_log_level() < log.INFO and options.verbosity == 0:
args.append("-v")

Expand Down
2 changes: 1 addition & 1 deletion pex/version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2015 Pex project contributors.
# Licensed under the Apache License, Version 2.0 (see LICENSE).

__version__ = "2.8.0"
__version__ = "2.8.1"
257 changes: 257 additions & 0 deletions tests/integration/test_issue_2412.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
# Copyright 2024 Pex project contributors.
# Licensed under the Apache License, Version 2.0 (see LICENSE).

from __future__ import absolute_import, print_function

import os.path
import subprocess
from textwrap import dedent

import pytest

import testing
from pex.common import safe_open
from pex.interpreter_constraints import InterpreterConstraint
from pex.pep_503 import ProjectName
from pex.requirements import PyPIRequirement, parse_requirement_file
from pex.resolve.lockfile import json_codec
from pex.typing import TYPE_CHECKING
from pex.venv.virtualenv import InstallationChoice, Virtualenv
from testing.cli import run_pex3
from testing.lock import index_lock_artifacts

if TYPE_CHECKING:
from typing import Any


def create_entry_module(project_dir):
# type: (str) -> None

with safe_open(os.path.join(project_dir, "entry.py"), "w") as fp:
fp.write(
dedent(
"""\
#!/usr/bin/env python3
import numpy # not used, just to illustrate a dependency
def main():
print("Hello ", numpy.__version__)
"""
)
)


def create_setup_py(
project_dir, # type: str
**setup_kwargs # type: Any
):
# type: (...) -> None

with safe_open(os.path.join(project_dir, "setup.py"), "w") as fp:
fp.write(
dedent(
"""\
#!/usr/bin/env python3
import setuptools
setuptools.setup(
name="repro",
version="0.0.1",
python_requires="~=3.9",
dependency_links=[],
entry_points={{
"console_scripts": [
"entry = entry:main",
],
}},
py_modules=["entry"],
**{setup_kwargs!r}
)
"""
).format(setup_kwargs=setup_kwargs)
)


@pytest.fixture
def issue_2412_repro_project_dir(tmpdir):
# type: (Any) -> str

project_dir = os.path.join(str(tmpdir), "project")

# This exactly replicates the project setup in the original problem repro repo in
# https://github.com/pex-tool/pex/issues/2412 sans Makefile.
create_entry_module(project_dir)
input_requirements = os.path.join(project_dir, "requirements.in")
with safe_open(input_requirements, "w") as fp:
print("numpy", file=fp)
create_setup_py(project_dir)
return project_dir


def assert_bdist_pex(
project_dir, # type: str
expected_numpy_version, # type: str
):
# type: (...) -> None

assert "Hello {numpy_version}\n".format(
numpy_version=expected_numpy_version
) == subprocess.check_output(args=[os.path.join(project_dir, "dist", "entry")]).decode("utf-8")


skip_if_incompatible_with_repro_project = pytest.mark.skipif(
not InterpreterConstraint.matches("CPython>=3.9,<3.13"),
reason=(
"The repro project under test requires Python ~=3.9 and we further restrict to CPython "
"and place a version ceiling at 3.13 to ensure we resolve numpy wheels and do not need to "
"build the sdist."
),
)


@skip_if_incompatible_with_repro_project
def test_bdist_pex_locked_issue_2412_repro_exact(
issue_2412_repro_project_dir, # type: str
pex_project_dir, # type: str
):
# type: (...) -> None

venv = Virtualenv.create(
venv_dir=os.path.join(issue_2412_repro_project_dir, ".env"),
install_pip=InstallationChoice.UPGRADED,
install_setuptools=InstallationChoice.UPGRADED,
)
subprocess.check_call(
args=[venv.bin_path("pip"), "install", "-U", "pip-tools", pex_project_dir]
)

subprocess.check_call(
args=[
venv.bin_path("pip-compile"),
"--output-file",
"requirements.txt",
"--generate-hashes",
"requirements.in",
],
cwd=issue_2412_repro_project_dir,
)
locked_requirements = list(
parse_requirement_file(os.path.join(issue_2412_repro_project_dir, "requirements.txt"))
)
assert 1 == len(locked_requirements)

locked_requirement = locked_requirements[0]
assert isinstance(locked_requirement, PyPIRequirement)
assert (
"--hash=sha256:" in locked_requirement.line.raw_text
), "Expected the compiled requirements file to include hashes."

locked_numpy_specifiers = list(locked_requirement.requirement.specifier)
assert 1 == len(locked_numpy_specifiers)
locked_numpy_specifier = locked_numpy_specifiers[0]
assert "==" == locked_numpy_specifier.operator
locked_numpy_version = locked_numpy_specifier.version

subprocess.check_call(
args=[
venv.interpreter.binary,
"setup.py",
"bdist_pex",
"--pex-args",
"--disable-cache -vvvv -r requirements.txt --pip-version 24.0",
"--bdist-all",
],
cwd=issue_2412_repro_project_dir,
)
assert_bdist_pex(issue_2412_repro_project_dir, expected_numpy_version=locked_numpy_version)


def assert_bdist_pex_locked(
project_dir, # type: str
lock, # type: str
):
# type: (...) -> None

venv = Virtualenv.create(
venv_dir=os.path.join(project_dir, ".env"),
install_pip=InstallationChoice.YES,
install_setuptools=InstallationChoice.YES,
)
subprocess.check_call(args=[venv.bin_path("pip"), "install", testing.pex_project_dir()])
subprocess.check_call(
args=[
venv.interpreter.binary,
"setup.py",
"bdist_pex",
"--pex-args",
"--disable-cache -vvvv --lock {lock}".format(lock=lock),
"--bdist-all",
],
cwd=project_dir,
)

locked_numpy_version = index_lock_artifacts(json_codec.load(lock))[
ProjectName("numpy")
].pin.version.raw
assert_bdist_pex(project_dir=project_dir, expected_numpy_version=locked_numpy_version)


@skip_if_incompatible_with_repro_project
def test_bdist_pex_locked_issue_2412_repro_pex_lock(
issue_2412_repro_project_dir, # type: str
pex_project_dir, # type: str
):
# type: (...) -> None

lock = os.path.join(issue_2412_repro_project_dir, "lock.json")
run_pex3(
"lock",
"sync",
"-r",
os.path.join(issue_2412_repro_project_dir, "requirements.in"),
"--pip-version",
"24.0",
"--style",
"universal",
"--interpreter-constraint",
"~=3.9",
"--indent",
"2",
"--lock",
lock,
).assert_success()
assert_bdist_pex_locked(project_dir=issue_2412_repro_project_dir, lock=lock)


@skip_if_incompatible_with_repro_project
def test_bdist_pex_locked_issue_2412_repro_pex_lock_inlined_requirements(
tmpdir, # type: str
pex_project_dir, # type: str
):
# type: (...) -> None

project_dir = os.path.join(str(tmpdir), "project")
create_entry_module(project_dir)
create_setup_py(project_dir, install_requires=["numpy"])

lock = os.path.join(project_dir, "lock.json")
run_pex3(
"lock",
"sync",
"--project",
project_dir,
"--pip-version",
"24.0",
"--style",
"universal",
"--interpreter-constraint",
"~=3.9",
"--indent",
"2",
"--lock",
lock,
).assert_success()
assert_bdist_pex_locked(project_dir=project_dir, lock=lock)

0 comments on commit 0674b79

Please sign in to comment.