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

Should uv add set lower version bounds? #5178

Closed
ibraheemdev opened this issue Jul 18, 2024 · 11 comments · Fixed by #5688
Closed

Should uv add set lower version bounds? #5178

ibraheemdev opened this issue Jul 18, 2024 · 11 comments · Fixed by #5688
Assignees
Labels
enhancement New feature or request preview Experimental behavior

Comments

@ibraheemdev
Copy link
Member

ibraheemdev commented Jul 18, 2024

Currently, uv add foo adds "foo" as a requirement with no version constraint. Should it instead perform a resolution and require "foo>X"?

There's a lot of resistance against upper bounds so we probably don't want to add them by default (though I don't have much context about this problem myself), but lower bounds are reasonable to include.

@ibraheemdev ibraheemdev added the preview Experimental behavior label Jul 18, 2024
@zanieb
Copy link
Member

zanieb commented Jul 18, 2024

I think this is a great default. cc @konstin

@baggiponte
Copy link

Most package managers add lower bounds (though I don't know how they select the version to solve for). I was bitten by this today because in CI I cannot use uv lockfiles and I resorted to export with --resolution=lowest-direct and a version 0.1.0 of some library was installed (lol).

My 2 cents: no upper boundaries. Python is not JS: we don't have peer dependencies. Distributing a library with <= can have unintended consequences. I always cite these two articles since the author probably needs no introduction.

@zanieb
Copy link
Member

zanieb commented Jul 18, 2024

We've already had extensive discussion on upper bounds and feel similarly.

We also added a warning for missing lower bounds with that resolution mode in #2797 / #5142

@charliermarsh
Copy link
Member

What do we want the lower bound to be... The version we resolve to, or the minimum compatible version in the resolution?

@nathanjmcdougall
Copy link
Contributor

nathanjmcdougall commented Jul 19, 2024

Adding a lower bound carries the implication that the codebase isn't compatible with earlier versions. If we resolve to the latest version of the package, this ends up being a quite strict assumption and can force a dependent installing your package to upgrade unnecessarily. This might be desirable initially until the codebase has actually been tested on older versions of packages; but it is arguably too cautious, especially when using uv add on mature packages which are relatively feature complete.

On the other hand, if a user starts by adding a single dependency (e.g. uv add numpy) then the minimum compatible version will often be a very old release indeed. If this is used as a lower bound, it could be misleading, since it would imply the package has been tested/confirmed to work correctly down to that lower bound. It also becomes subject to the arbitrariness of the order in which the user adds dependencies one-by-one.

Personally, I think using the version we resolve to is more sensible. Perhaps only include the Major.Minor parts of a SemVer version.

@konstin
Copy link
Member

konstin commented Jul 19, 2024

I've been thinking that as a follow-up to adding a lower bounds with uv add, we could have a script that bisects lower bounds for you:

Say you added numpy, which defaults to numpy>=2.0.0, and then run uv losen-bounds --lower numpy "python -m pytest".

From the resolution, we know that there is a >=1.25.0 from another dependency (this feature is more important when we don't have that lower bound, but i need to bound my example to not be too big to write out). numpy then has the following versions remaining:

2.0.0 2.0.0rc2 2.0.0rc1 2.0.0b1 1.26.4 1.26.3 1.26.2 1.26.1 1.26.0 1.26.0rc1 1.26.0b1 1.25.2 1.25.1 1.25.0

We remove prerelease versions since they are not selected:

2.0.0 1.26.4 1.26.3 1.26.2 1.26.1 1.26.0 1.25.2 1.25.1 1.25.0

We start with 1.25.0, pytest fails. We try 1.26.1, pytest succeeds. We try 1.25.2, pytest fails. We try 1.26.0, pytest suceeds. We write >=1.26.0 to your pyproject.toml.

@zanieb zanieb added the enhancement New feature or request label Jul 22, 2024
@charliermarsh charliermarsh self-assigned this Jul 23, 2024
@charliermarsh
Copy link
Member

I can do it.

@charliermarsh
Copy link
Member

This is a bit of a pain because we need to add the requirement before resolving, then resolve, then update the pyproject.toml with the resolved version. But there are some tricky cases... like, what if we resolve multiple versions of the package, etc.

@zanieb
Copy link
Member

zanieb commented Jul 24, 2024

I wouldn't be hurt if we dropped this from the release-ready milestone.

@charliermarsh
Copy link
Member

I will try to do this tomorrow.

charliermarsh added a commit that referenced this issue Aug 1, 2024
@notatallshaw
Copy link
Contributor

I'm a bit late to the conversation, but one heuristic you might find as a helpful fallback if users of projects have not setup or do not use pytest, is if the dependency provides wheels, what's the earliest version of the dependency that has compatible wheels with some version of Python that matches those wheels.

E.g. For numpy if your project is Python-Requires >= 3.9 you might be able to determine that numpy >= 1.19.3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request preview Experimental behavior
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

7 participants