Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

env: add support for uv #751

Merged
merged 35 commits into from Mar 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
2f50d8d
Add `uv` extra
layday Mar 5, 2024
24d9efe
Use argparse shortcut for dist args
layday Mar 5, 2024
a57609f
Rename `--config-setting` dest to be in the plural
layday Mar 5, 2024
db6069c
Add provision for `env` param to custom subp runner
layday Mar 5, 2024
dd1c3f6
Refactor env impl, adding support for uv
layday Mar 5, 2024
a66b179
Add `--env-impl` flag to CLI
layday Mar 5, 2024
7084cf4
Use `os.name` as is done by the stdlib sysconfig and venv mods
layday Mar 6, 2024
fd3563e
Update tests
layday Mar 5, 2024
0c6c261
Require uv and virtualenv for testing
layday Mar 6, 2024
5a3609f
Add more env tests
layday Mar 6, 2024
641170c
Back out of reading `pyvenv.cfg`
layday Mar 6, 2024
c6d86d2
Create uv envs with venv
layday Mar 6, 2024
2e65e58
Do not expose pip implementations
layday Mar 6, 2024
959f116
Update tests
layday Mar 6, 2024
f775815
Run integration tests against uv
layday Mar 7, 2024
7d77fd2
Merge pip impls
layday Mar 7, 2024
b8672ac
Test with latest version of virtualenv for the time being
layday Mar 7, 2024
36d3f92
Do not attempt uv import
layday Mar 8, 2024
7ddc5e3
Warn when using uv with PyPy
layday Mar 8, 2024
3cc7568
Update tests
layday Mar 8, 2024
3ac6a95
Update markers for PyPy
layday Mar 8, 2024
619126b
Fix coverage
layday Mar 8, 2024
430ad00
Test with original `has_virtualenv`
layday Mar 8, 2024
0680ae7
Bump minimum version of packaging
layday Mar 8, 2024
6844fb7
Test pip and uv verbosity flags
layday Mar 8, 2024
7d3ecf6
Simplify `test_pip_needs_upgrade_mac_os_11`
layday Mar 8, 2024
e04d030
Restore the uv extra, look for uv under `sys.prefix`
layday Mar 8, 2024
49d1379
Fix spelling of 'PyPy'
layday Mar 9, 2024
bf9681f
Rm PyPy support warning
layday Mar 9, 2024
468350e
Fix xfailing test, only xfail on win
layday Mar 9, 2024
deabd67
Rename `env_impl` param to `installer`
layday Mar 9, 2024
3766079
Defer to `uv.find_uv_bin`
layday Mar 9, 2024
1408af4
Fix test param
layday Mar 9, 2024
c5040f4
Restore uv `$PATH` lookup with logging message
layday Mar 10, 2024
a27bb85
Help mypy narrow
layday Mar 10, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 6 additions & 1 deletion pyproject.toml
Expand Up @@ -33,7 +33,7 @@ urls.issues = "https://github.com/pypa/build/issues"
urls.source = "https://github.com/pypa/build"

dependencies = [
"packaging >= 19.0",
"packaging >= 19.1",
"pyproject_hooks",
# not actually a runtime dependency, only supplied as there is not "recommended dependency" support
'colorama; os_name == "nt"',
Expand All @@ -50,6 +50,7 @@ docs = [
"sphinx-issues >= 3.0.0",
]
test = [
"build[uv, virtualenv]",
"filelock >= 3",
"pytest >= 6.2.4",
"pytest-cov >= 2.12",
Expand All @@ -63,11 +64,15 @@ test = [
'setuptools >= 67.8.0; python_version >= "3.12"',
]
typing = [
"build[uv]",
"importlib-metadata >= 5.1",
"mypy ~= 1.5.0",
"tomli",
"typing-extensions >= 3.7.4.3",
]
uv = [
"uv >= 0.1.15",
]
virtualenv = [
"virtualenv >= 20.0.35",
]
Expand Down
51 changes: 34 additions & 17 deletions src/build/__main__.py
Expand Up @@ -22,6 +22,7 @@
import build

from . import ProjectBuilder, _ctx
from . import env as _env
from ._exceptions import BuildBackendException, BuildException, FailedProcessError
from ._types import ConfigSettings, Distribution, StrPath
from .env import DefaultIsolatedEnv
Expand Down Expand Up @@ -124,8 +125,9 @@ def _build_in_isolated_env(
outdir: StrPath,
distribution: Distribution,
config_settings: ConfigSettings | None,
installer: _env.Installer,
) -> str:
with DefaultIsolatedEnv() as env:
with DefaultIsolatedEnv(installer=installer) as env:
builder = ProjectBuilder.from_isolated_env(env, srcdir)
# first install the build dependencies
env.install(builder.build_system_requires)
Expand Down Expand Up @@ -160,9 +162,10 @@ def _build(
distribution: Distribution,
config_settings: ConfigSettings | None,
skip_dependency_check: bool,
installer: _env.Installer,
) -> str:
if isolation:
return _build_in_isolated_env(srcdir, outdir, distribution, config_settings)
return _build_in_isolated_env(srcdir, outdir, distribution, config_settings, installer)
else:
return _build_in_current_env(srcdir, outdir, distribution, config_settings, skip_dependency_check)

Expand Down Expand Up @@ -216,6 +219,7 @@ def build_package(
config_settings: ConfigSettings | None = None,
isolation: bool = True,
skip_dependency_check: bool = False,
installer: _env.Installer = 'pip',
) -> Sequence[str]:
"""
Run the build process.
Expand All @@ -229,7 +233,7 @@ def build_package(
"""
built: list[str] = []
for distribution in distributions:
out = _build(isolation, srcdir, outdir, distribution, config_settings, skip_dependency_check)
out = _build(isolation, srcdir, outdir, distribution, config_settings, skip_dependency_check, installer)
built.append(os.path.basename(out))
return built

Expand All @@ -241,6 +245,7 @@ def build_package_via_sdist(
config_settings: ConfigSettings | None = None,
isolation: bool = True,
skip_dependency_check: bool = False,
installer: _env.Installer = 'pip',
) -> Sequence[str]:
"""
Build a sdist and then the specified distributions from it.
Expand All @@ -258,7 +263,7 @@ def build_package_via_sdist(
msg = 'Only binary distributions are allowed but sdist was specified'
raise ValueError(msg)

sdist = _build(isolation, srcdir, outdir, 'sdist', config_settings, skip_dependency_check)
sdist = _build(isolation, srcdir, outdir, 'sdist', config_settings, skip_dependency_check, installer)

sdist_name = os.path.basename(sdist)
sdist_out = tempfile.mkdtemp(prefix='build-via-sdist-')
Expand All @@ -271,7 +276,7 @@ def build_package_via_sdist(
_ctx.log(f'Building {_natural_language_list(distributions)} from sdist')
srcdir = os.path.join(sdist_out, sdist_name[: -len('.tar.gz')])
for distribution in distributions:
out = _build(isolation, srcdir, outdir, distribution, config_settings, skip_dependency_check)
out = _build(isolation, srcdir, outdir, distribution, config_settings, skip_dependency_check, installer)
built.append(os.path.basename(out))
finally:
shutil.rmtree(sdist_out, ignore_errors=True)
Expand Down Expand Up @@ -329,13 +334,17 @@ def main_parser() -> argparse.ArgumentParser:
parser.add_argument(
'--sdist',
'-s',
action='store_true',
dest='distributions',
action='append_const',
const='sdist',
help='build a source distribution (disables the default behavior)',
)
parser.add_argument(
'--wheel',
'-w',
action='store_true',
dest='distributions',
action='append_const',
const='wheel',
help='build a wheel (disables the default behavior)',
)
parser.add_argument(
Expand All @@ -351,16 +360,23 @@ def main_parser() -> argparse.ArgumentParser:
action='store_true',
help='do not check that build dependencies are installed',
)
parser.add_argument(
env_group = parser.add_mutually_exclusive_group()
env_group.add_argument(
'--no-isolation',
'-n',
action='store_true',
help='disable building the project in an isolated virtual environment. '
'Build dependencies must be installed separately when this option is used',
)
env_group.add_argument(
'--installer',
choices=_env.INSTALLERS,
help='Python package installer to use (defaults to pip)',
)
parser.add_argument(
'--config-setting',
'-C',
dest='config_settings',
action='append',
help='settings to pass to the backend. Multiple settings can be provided. '
'Settings beginning with a hyphen will erroneously be interpreted as options to build if separated '
Expand All @@ -384,11 +400,10 @@ def main(cli_args: Sequence[str], prog: str | None = None) -> None:

_setup_cli(verbosity=args.verbosity)

distributions: list[Distribution] = []
config_settings = {}

if args.config_setting:
for arg in args.config_setting:
if args.config_settings:
for arg in args.config_settings:
setting, _, value = arg.partition('=')
if setting not in config_settings:
config_settings[setting] = value
Expand All @@ -398,14 +413,10 @@ def main(cli_args: Sequence[str], prog: str | None = None) -> None:

config_settings[setting].append(value)

if args.sdist:
distributions.append('sdist')
if args.wheel:
distributions.append('wheel')

# outdir is relative to srcdir only if omitted.
outdir = os.path.join(args.srcdir, 'dist') if args.outdir is None else args.outdir

distributions: list[Distribution] = args.distributions
if distributions:
build_call = build_package
else:
Expand All @@ -414,7 +425,13 @@ def main(cli_args: Sequence[str], prog: str | None = None) -> None:

with _handle_build_error():
built = build_call(
args.srcdir, outdir, distributions, config_settings, not args.no_isolation, args.skip_dependency_check
args.srcdir,
outdir,
distributions,
config_settings,
not args.no_isolation,
args.skip_dependency_check,
args.installer,
)
artifact_list = _natural_language_list(
['{underline}{}{reset}{bold}{green}'.format(artifact, **_styles.get()) for artifact in built]
Expand Down
8 changes: 4 additions & 4 deletions src/build/_ctx.py
Expand Up @@ -5,7 +5,7 @@
import subprocess
import typing

from collections.abc import Sequence
from collections.abc import Mapping, Sequence
from functools import partial

from ._types import StrPath
Expand Down Expand Up @@ -40,7 +40,7 @@ def log_subprocess_error(error: subprocess.CalledProcessError) -> None:
log(stream.decode() if isinstance(stream, bytes) else stream, origin=('subprocess', stream_name))


def run_subprocess(cmd: Sequence[StrPath]) -> None:
def run_subprocess(cmd: Sequence[StrPath], env: Mapping[str, str] | None = None) -> None:
verbosity = VERBOSITY.get()

if verbosity:
Expand All @@ -53,7 +53,7 @@ def log_stream(stream_name: str, stream: typing.IO[str]) -> None:
log(line, origin=('subprocess', stream_name))

with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor, subprocess.Popen(
cmd, encoding='utf-8', stdout=subprocess.PIPE, stderr=subprocess.PIPE
cmd, encoding='utf-8', env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE
) as process:
log(subprocess.list2cmdline(cmd), origin=('subprocess', 'cmd'))

Expand All @@ -69,7 +69,7 @@ def log_stream(stream_name: str, stream: typing.IO[str]) -> None:

else:
try:
subprocess.run(cmd, capture_output=True, check=True)
subprocess.run(cmd, capture_output=True, check=True, env=env)
except subprocess.CalledProcessError as error:
log_subprocess_error(error)
raise
Expand Down