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

Dependency resolution: unclear behaviour of --only-group #9211

Closed
ThomasHezard opened this issue Nov 18, 2024 · 17 comments
Closed

Dependency resolution: unclear behaviour of --only-group #9211

ThomasHezard opened this issue Nov 18, 2024 · 17 comments
Labels
question Asking for clarification or support

Comments

@ThomasHezard
Copy link

Hello,

I have an issue with dependency resolution.
My project is configured as follows (I am working on a custom Pytoch Lightning extension, tomli is used only in unit tests):

requires-python = ">=3.8"
dependencies = [
    "google-auth~=2.0",
    "google-cloud-storage~=2.0",
    "lightning~=2.0",
    "loguru~=0.1",
    "mlflow~=2.0",
    "typing-extensions~=4.0"
]
[dependency-groups]
dev = [
    "tomli~=2.0",
]

After locking the environment and building the wheel, the following line fails in my testing script (I want to run the unit tests on the built wheel, not on the source):

uv run --link-mode=copy --python "3.12" --only-dev --with dist/*.whl python -m unittest discover tests "*test*.py"

with the error (confidential data replaced by XXX)

Installed 1 package in 3ms
  × Failed to download and build `numpy==1.24.4`
  ├─▶ Build backend failed to determine requirements with `build_wheel()` (exit status: 1)

  │   [stderr]
  │   Traceback (most recent call last):
  │     File "<string>", line 8, in <module>
  │     File "XXX/.cache/uv/builds-v0/.tmp1p0Hp0/lib/python3.12/site-packages/setuptools/__init__.py", line 10, in <module>
  │       import distutils.core
  │   ModuleNotFoundError: No module named 'distutils'

  ╰─▶ distutils was removed from the standard library in Python 3.12. Consider adding a constraint (like `numpy >1.24.4`) to avoid building a version of numpy that depends on distutils.
  help: `numpy` was included because `XXX` depends on `mlflow==2.17.2` which depends on `numpy`

From what I understand from uv documentation

--only-group only-group  
  Only include dependencies from the specified dependency group.
  May be provided multiple times.
  The project itself will also be omitted.

and given the fact that tomli has no dependency at all (which means numpy is NOT in the dev dependency group), the following command should behave exactly the same:

uv run --link-mode=copy --python "3.12" --no-project --with "tomli~=2.0" --with dist/*.whl python -m unittest discover tests "*test*.py"

However, this command runs perfectly!

I tried the --refresh and --reinstall options in the first version, it does not work.

=> Is this a bug or something I am missing?

If that is unclear I can try to reproduce my issue in a toy project

@bluss
Copy link
Contributor

bluss commented Nov 18, 2024

uv run only does an additive or --inexact sync of dependencies before it runs. It doesn't go out of its way to remove packages like uv sync would do. That could probably be the explanation.

@zanieb
Copy link
Member

zanieb commented Nov 18, 2024

Do you have an up-to-date uv.lock file? We always lock the entire project, --only-group only affects the subset of the lockfile that we use.

@zanieb zanieb added the question Asking for clarification or support label Nov 18, 2024
@ThomasHezard
Copy link
Author

ThomasHezard commented Nov 18, 2024

I'll send the up-to-date lock file as soon as I can (in the subway right now ^^).

I understand that locking locks the entire project.

However, even if the dev group contains strictly nothing else than tomli, uv run --only-dev [...] chooses the numpy version corresponding to the one in the lock file and not the numpy version deduced from my wheel dependencies, like uv run --no-project --with tomli [...] does.
In other words: why does uv run --only-dev [...] look up the version in the lock file for packages that are not in the dev group?

If there is a reason behind this behaviour, documenting it clearly might be helpful 🙏

@zanieb
Copy link
Member

zanieb commented Nov 18, 2024

Oh I see you're using --with dist/*.whl. I can't reproduce this on a first try:

❯ uv build
Building source distribution...
...
Successfully built dist/example-0.1.0.tar.gz and dist/example-0.1.0-py3-none-any.whl
❯ uv run --only-group dev --with dist/*.whl python -c "print('hi')"
Installed 92 packages in 587ms
hi

Nor if I try syncing first

❯ uv sync
Resolved 111 packages in 1ms
Audited 92 packages in 0.06ms
❯ uv run --only-group dev --with dist/*.whl python -c "print('hi')"
Installed 92 packages in 510ms
hi

@zanieb
Copy link
Member

zanieb commented Nov 18, 2024

I'm not quite sure what's going on, perhaps some verbose logs would be helpful.

@ThomasHezard
Copy link
Author

I am only having the issues with --python "3.12", because numpy 1.* fails to install with python 3.12 in my environment.

@zanieb
Copy link
Member

zanieb commented Nov 18, 2024

-p 3.12 doesn't change anything for me, it doesn't resolve to an older version of numpy

❯ uv run --only-group dev --with dist/*.whl -p 3.12 python -c "import importlib.metadata; print(importlib.metadata.version('numpy'))"
2.1.3

@ThomasHezard
Copy link
Author

Do you have requires-python = ">=3.8" in your project?
I noticed that a minimum Python of 3.9 or above solves the issue as the uv lock chooses a more recent numpy version which installs correctly under Python 3.12.

I am finishing the toy project to reproduce, I'll send it to you in a few minutes.

@zanieb
Copy link
Member

zanieb commented Nov 18, 2024

Ah okay I can reproduce with that (I thought I had it, but must have messed up when creating the reproduction).

And I can work around it by adding a constraint

❯ uv run --only-group dev -p 3.12 --with ./dist/*.whl --with 'numpy>1.24.4' python -c ""

or by disabling builds

❯ uv run --only-group dev -p 3.12 --with ./dist/*.whl --frozen --no-build python -c ""

@ThomasHezard
Copy link
Author

Here is a toy project that reproduces the issue:
https://github.com/ThomasHezard/uv-issue-example

The code is dummy, but the dependencies are exactly the same as my project.

@zanieb
Copy link
Member

zanieb commented Nov 18, 2024

We include the numpy version as a preference

DEBUG Selecting: numpy==1.24.4 [preference] (numpy-1.24.4.tar.gz)

I believe we always use the lockfile versions as preferences during project resolution

@ThomasHezard
Copy link
Author

Ah okay I can reproduce with that (I thought I had it, but must have messed up when creating the reproduction).

And I can work around it by adding a constraint

❯ uv run --only-group dev -p 3.12 --with ./dist/*.whl --with 'numpy>1.24.4' python -c ""

or by disabling builds

❯ uv run --only-group dev -p 3.12 --with ./dist/*.whl --frozen --no-build python -c ""

I sent my message at the same time 🙂

I did found some several workarounds indeed, including fixing the numpy version like you did 👍
I'll have a closer look at the --frozen option.

However I would like to understand clearly why I end up with this behaviour in order to be able to setup my projects correctly. From my understanding of the documentation, my two commands should end up with the same environment but they do not at all.

Maybe some change in the documentation of the --only-dev may help. I don't think "Only include dependencies from the specified dependency group. The project itself will also be omitted." describes clearly the behaviour I encountered.

I leave that to the uv team 🙂
And to be clear: this is not a critical issue, even though it bugged me for quite some time today 😉

Hope this may help you or somebody in the future encountering the same issue as me.

@ThomasHezard
Copy link
Author

And by the way, fixing numpy is not enough, as it ends up with incompatible pandas and numpy version, and importing pandas throws an error.

But pandas is struggling a bit with numpy 1.* and 2.*. I often have to fix my numpy version in my environments, especially with older Python version.

@zanieb
Copy link
Member

zanieb commented Nov 18, 2024

I'll have a closer look at the --frozen option.

--no-build is the key option there. --frozen is required to avoid trying to re-validate the lockfile in that case.

I don't think "Only include dependencies from the specified dependency group. The project itself will also be omitted." describes clearly the behaviour I encountered.

Basically this is caveated with: in all uv project operations, we'll prefer versions in the lockfile during resolution for consistency. I don't think this is well documented, we can improve that.

Using --no-project is a bit more than omitting the project itself from the resolution, it's more like "do not discover a project at all" and this ignores the uv.lock entirely.

@ThomasHezard
Copy link
Author

ThomasHezard commented Nov 18, 2024

in all uv project operations, we'll prefer versions in the lockfile during resolution for consistency.

Yes, this is what I'm starting to understand 👍

Thank you very much for the help and reactivity, this is much appreciated 🙏

@konstin
Copy link
Member

konstin commented Nov 18, 2024

I think the problem you're hitting is the following: uv tries to compute a uniersal lockfile for your requires-python = ">=3.8". Since newer numpy versions have a higher requires-python minimum, you're getting the old numpy 1.24.4. But that numpy only support up to Python 3.11, which uv doesn't realize, so you're getting build errors on Python 3.12 and up. You can find a long detailed explanation in #8492 (comment).

The proper solution is #8686, as a workaround you can coerce uv to give you different numpy versions on different Python versions, something like the following, adjust the bounds to your liking:

numpy>=1.24.2; python_version >= "3.8"
numpy>=2.1.0; python_version >= "3.10"

@ThomasHezard
Copy link
Author

The proper solution is #8686

Yes, --multi-version seems exactly what I'm looking for indeed 🤩 👍
This will reproduce more closely the environment I get with uv run --no-project --with dist/*.whl (which is what I am looking in the end).

as a workaround you can coerce uv to give you different numpy versions on different Python versions, something like the following, adjust the bounds to your liking:

numpy>=1.24.2; python_version >= "3.8"
numpy>=2.1.0; python_version >= "3.10"

This is basically the solution I ended up with (in my dev dependencies) 😉

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

No branches or pull requests

5 participants