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

env: add support for uv #751

merged 35 commits into from
Mar 10, 2024

Conversation

layday
Copy link
Member

@layday layday commented Mar 5, 2024

No description provided.

pyproject.toml Outdated Show resolved Hide resolved
@layday layday force-pushed the feat-uv-env branch 2 times, most recently from 779e7bc to 77bf09d Compare March 6, 2024 15:21
@layday layday marked this pull request as ready for review March 6, 2024 15:30
@layday layday requested a review from FFY00 as a code owner March 6, 2024 15:30
@layday layday changed the title Add support for uv env: add support for uv Mar 6, 2024
@layday
Copy link
Member Author

layday commented Mar 6, 2024

It looks like uv venv doesn't work with PyPy on Windows.

@gaborbernat
Copy link
Contributor

Yeah astral-sh/uv#2096, there's no official support for PyPy today, and what works, it works by chance.

@webknjaz
Copy link
Member

webknjaz commented Mar 6, 2024

@layday have you considered exposing an API for pluggable build env provisioners?

@layday
Copy link
Member Author

layday commented Mar 6, 2024

Can you go into more detail?

@webknjaz
Copy link
Member

webknjaz commented Mar 6, 2024

Can you go into more detail?

I mean, it'd be nice if third parties could stick their bits of build env provisioning logic through some standardized interface. This could be useful for various downstreams and probably other tools that act as frontends.

19.0 incorrectly interprets Python version constraints
resulting in venv being used in pref to virtualenv when
an outer pip is too old.
@layday
Copy link
Member Author

layday commented Mar 8, 2024

So, this adds an --env-impl flag to the CLI, with the only accepted value being venv+uv. Old semantics apply if omitted.

I've modified the venv selection order slightly so that venv is preferred whenever a valid outer pip is present for a minor speed boost. virtualenv continues to be preferred with an outdated pip.

The DefaultIsolatedEnv is now tested with all venv and installer combos (virtualenv used to be mocked). This revealed the venv module was preferred with packaging 19.0 even when a valid virtualenv was present. The culprit was a marker evaluation bug in packaging which was fixed in 19.1.

Finally, I've added a setuptools existence check before attempting to uninstall the ensurepip copy of setuptools - it was dropped in Python 3.12. Again, this should make using venv without an outer pip slightly faster.

@layday
Copy link
Member Author

layday commented Mar 8, 2024

Currently, we're only looking for a uv executable on $PATH (not attempting an import), but see the discussion happening at wntrblm/nox#795 (comment). Still not importing uv but also looking under sys.prefix for unactivated venvs.

@layday layday closed this Mar 8, 2024
@layday layday reopened this Mar 8, 2024
src/build/env.py Outdated
# find it under ``sys.prefix``, essentially potentially rearranging
# the user's $PATH. We'll only look for uv under the prefix of
# the running interpreter for unactivated venvs then defer to $PATH.
uv_bin = shutil.which('uv', path=sysconfig.get_path('scripts'))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uv comes with a find_uv_bin method we should use that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment explains why it's not being used.

Copy link
Contributor

@gaborbernat gaborbernat Mar 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Guess I agree here with uv's choice, I find it more expected, and disagree with the comment. And even if should uv's strategy be an actual problem, I'd like them to fix it, and not us work around it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is it expected that a uv you've installed in a venv might load a uv from an outer env?

$ python -m venv --without-pip foo
$ VIRTUAL_ENV=foo uv pip install uv
Resolved 1 package in 3ms
Installed 1 package in 5ms
 + uv==0.1.16
$ foo/bin/python -c 'import uv; print(uv.find_uv_bin())'
/Users/dimitris/code/python-packaging/build/foo/bin/uv
$ rm foo/bin/uv
$ foo/bin/python -c 'import uv; print(uv.find_uv_bin())'
/Users/dimitris/.local/bin/uv

If uv should function as a Python package, it has no business looking outside its prefix.

I'd personally prefer to take a standards-based approach than rely on uv's custom script finder. Is this not the correct way to locate a script under the running interpreter's prefix?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Open an issue in UV and we should fix it there then.

Copy link
Member Author

@layday layday Mar 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to only use uv.find_uv_bin.

uv_bin = shutil.which('uv', path=sysconfig.get_path('scripts'))

if not uv_bin:
uv_bin = shutil.which('uv')
Copy link
Contributor

@gaborbernat gaborbernat Mar 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I strongly disagree with such fallback. Either pick uv from sys.prefix or fail. Using a stray executable we pick up from PATH could use surprises for users...

Copy link
Contributor

@henryiii henryiii Mar 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've generally been respecting the design of uv, which is to not install itself in environments, but to be used globally (via https://astral.sh/uv/install.sh, cargo install, homebrew, pipx, pacman, etc) and only target environments. If a user does install uv in build's environment, that should take preference, but I think we should try to use an outer/system uv otherwise. I don't think someone passing --installer=uv would be unhappy if a uv executable on their path is used. (same with cmake, gcc, ninja, patchelf, etc., lots of executables get used from the path)

But willing to go with this if you really prefer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then remove the UV extra because user should install it separately? Having an extra but then taking a global is unexpected to me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A user can install it, say if the system uv is older than they like, or if they just use pipx run build[uv] regardless of if the system has uv or not. But, if they are on a system that does not support binary wheels, like BSDs, they might prefer or even need to install it globally and not use the extra.

(This is very common for binary deps with optional PyPI packages like CMake and ninja)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are not in the business of installing cmake, why is UV any different (other than it happens to be available on pypi, is the same a random binary).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I can accept falling back to the operating system version, but if we do that in my opinion we must print out of warning onto the standard output or error to notify the user that this is happening.

Copy link
Contributor

@henryiii henryiii Mar 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like this would be perfect, I think:

* Creating venv isolated environment...
* Using uv 0.1.16 from /usr/local/bin/uv to install packages...
* Installing packages in isolated environment... (hatch-fancy-pypi-readme, hatch-vcs, hatchling)

(Maybe it could include "found on PATH" or something too?) Whereas if it was from the local environment, it could be simplified:

* Creating venv isolated environment...
* Using uv 0.1.16 to install packages...
* Installing packages in isolated environment... (hatch-fancy-pypi-readme, hatch-vcs, hatchling)

Would that work?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect.

Copy link
Member Author

@layday layday Mar 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reminder that we have a verbosity flag now that will print out the command invoked including the full uv executable path. Printing out the version for a uv executable from $PATH would require an additional subprocess call and parsing the output of -V which is a flimsy affair. This same consideration also applies to an outer pip, although the surprise factor is slightly diminished since we’re only looking in the running interpreter’s Python path.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot 2024-03-10 at 11 08 41

How's this?

@@ -21,6 +20,11 @@
from ._util import check_dependency


Installer = typing.Literal['pip', 'uv']
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we wanna call this uv-pip instead of simply uv?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the --installer implies uv pip. And if they added a “uv install”, we could transparently start using that instead.

@gaborbernat gaborbernat merged commit 327f280 into pypa:main Mar 10, 2024
57 checks passed
@layday
Copy link
Member Author

layday commented Mar 11, 2024

I would’ve preferred to squash both this and the other PR you merged personally. There’s a fair few fix commits and reverts and counter-reverts with attribution ultimately being lost in git blame. I’ll open another PR to add a change log entry.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants