Skip to content

Commit

Permalink
feat: build[uv] (pypa#1856)
Browse files Browse the repository at this point in the history
  • Loading branch information
henryiii authored Jun 9, 2024
1 parent c37e5a2 commit 384c8d5
Show file tree
Hide file tree
Showing 14 changed files with 397 additions and 178 deletions.
7 changes: 5 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ jobs:
with:
python-version: ${{ matrix.python_version }}

- uses: yezz123/setup-uv@v4

# Install podman on this CI instance for podman tests on linux
# Snippet from: https://github.com/redhat-actions/podman-login/blob/main/.github/workflows/example.yml
- name: Install latest podman
Expand All @@ -69,7 +71,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install ".[test]"
uv pip install --system ".[test]"
- name: Generate a sample project
run: |
Expand All @@ -82,6 +84,7 @@ jobs:
output-dir: wheelhouse
env:
CIBW_ARCHS_MACOS: x86_64 universal2 arm64
CIBW_BUILD_FRONTEND: 'build[uv]'
CIBW_FREE_THREADED_SUPPORT: 1
CIBW_PRERELEASE_PYTHONS: 1

Expand Down Expand Up @@ -161,7 +164,7 @@ jobs:
with:
python-version: "3.x"
- name: Install dependencies
run: python -m pip install ".[test]"
run: python -m pip install ".[test,uv]"

- name: Set up QEMU
id: qemu
Expand Down
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ repos:
- types-jinja2
- types-pyyaml
- types-requests
- uv
- validate-pyproject
- id: mypy
name: mypy 3.12
Expand Down
8 changes: 5 additions & 3 deletions bin/generate_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,21 @@
type: string_array
build-frontend:
default: default
description: Set the tool to use to build, either "pip" (default for now) or "build"
description: Set the tool to use to build, either "pip" (default for now), "build", or "build[uv]"
oneOf:
- enum: [pip, build, default]
- enum: [pip, build, "build[uv]", default]
- type: string
pattern: '^pip; ?args:'
- type: string
pattern: '^build; ?args:'
- type: string
pattern: '^build\[uv\]; ?args:'
- type: object
additionalProperties: false
required: [name]
properties:
name:
enum: [pip, build]
enum: [pip, build, "build[uv]"]
args:
type: array
items:
Expand Down
55 changes: 37 additions & 18 deletions cibuildwheel/linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,8 @@ def build_in_container(
log.build_start(config.identifier)
build_options = options.build_options(config.identifier)
build_frontend = build_options.build_frontend or BuildFrontendConfig("pip")
use_uv = build_frontend.name == "build[uv]" and Version(config.version) >= Version("3.8")
pip = ["uv", "pip"] if use_uv else ["pip"]

dependency_constraint_flags: list[PathOrStr] = []

Expand Down Expand Up @@ -229,13 +231,22 @@ def build_in_container(
)
sys.exit(1)

which_pip = container.call(["which", "pip"], env=env, capture_output=True).strip()
if PurePosixPath(which_pip) != python_bin / "pip":
print(
"cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.",
file=sys.stderr,
)
sys.exit(1)
if use_uv:
which_uv = container.call(["which", "uv"], env=env, capture_output=True).strip()
if not which_uv:
print(
"cibuildwheel: uv not found on PATH. You must use a supported manylinux or musllinux environment with uv.",
file=sys.stderr,
)
sys.exit(1)
else:
which_pip = container.call(["which", "pip"], env=env, capture_output=True).strip()
if PurePosixPath(which_pip) != python_bin / "pip":
print(
"cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.",
file=sys.stderr,
)
sys.exit(1)

compatible_wheel = find_compatible_wheel(built_wheels, config.identifier)
if compatible_wheel:
Expand Down Expand Up @@ -279,10 +290,12 @@ def build_in_container(
],
env=env,
)
elif build_frontend.name == "build":
elif build_frontend.name == "build" or build_frontend.name == "build[uv]":
if not 0 <= build_options.build_verbosity < 2:
msg = f"build_verbosity {build_options.build_verbosity} is not supported for build frontend. Ignoring."
log.warning(msg)
if use_uv:
extra_flags += ["--installer=uv"]
container.call(
[
"python",
Expand Down Expand Up @@ -327,26 +340,32 @@ def build_in_container(

# set up a virtual environment to install and test from, to make sure
# there are no dependencies that were pulled in at build time.
container.call(["pip", "install", "virtualenv", *dependency_constraint_flags], env=env)
if not use_uv:
container.call(
["pip", "install", "virtualenv", *dependency_constraint_flags], env=env
)

testing_temp_dir = PurePosixPath(
container.call(["mktemp", "-d"], capture_output=True).strip()
)
venv_dir = testing_temp_dir / "venv"

# Use embedded dependencies from virtualenv to ensure determinism
venv_args = ["--no-periodic-update", "--pip=embed"]
# In Python<3.12, setuptools & wheel are installed as well
if Version(config.version) < Version("3.12"):
venv_args.extend(("--setuptools=embed", "--wheel=embed"))
container.call(["python", "-m", "virtualenv", *venv_args, venv_dir], env=env)
if use_uv:
container.call(["uv", "venv", venv_dir], env=env)
else:
# Use embedded dependencies from virtualenv to ensure determinism
venv_args = ["--no-periodic-update", "--pip=embed"]
# In Python<3.12, setuptools & wheel are installed as well
if Version(config.version) < Version("3.12"):
venv_args.extend(("--setuptools=embed", "--wheel=embed"))
container.call(["python", "-m", "virtualenv", *venv_args, venv_dir], env=env)

virtualenv_env = env.copy()
virtualenv_env["PATH"] = f"{venv_dir / 'bin'}:{virtualenv_env['PATH']}"
virtualenv_env["VIRTUAL_ENV"] = str(venv_dir)

# TODO remove me once virtualenv provides pip>=24.1b1
if config.version == "3.13":
if config.version == "3.13" and not use_uv:
container.call(["pip", "install", "pip>=24.1b1"], env=virtualenv_env)

if build_options.before_test:
Expand All @@ -365,13 +384,13 @@ def build_in_container(
# Let's just pick the first one.
wheel_to_test = repaired_wheels[0]
container.call(
["pip", "install", str(wheel_to_test) + build_options.test_extras],
[*pip, "install", str(wheel_to_test) + build_options.test_extras],
env=virtualenv_env,
)

# Install any requirements to run the tests
if build_options.test_requires:
container.call(["pip", "install", *build_options.test_requires], env=virtualenv_env)
container.call([*pip, "install", *build_options.test_requires], env=virtualenv_env)

# Run the tests from a different directory
test_command_prepared = prepare_command(
Expand Down
Loading

0 comments on commit 384c8d5

Please sign in to comment.