uv icon indicating copy to clipboard operation
uv copied to clipboard

[Feature Request] Support `--upgrade-package` with a specific version

Open hongqn opened this issue 1 year ago • 16 comments

pip-compile --upgrade-package allows you to upgrade a specific package to a specific version instead of the latest version. For example, you can use the following command to update the django package to the latest version and requests to version 2.0.0:

pip-compile --upgrade-package django --upgrade-package requests==2.0.0

Currently, when using uv pip compile --upgrade-package requests==2.0.0, it produces the following error:

❯ uv --version
uv 0.1.10
❯ uv pip compile --upgrade-package requests==2.0.0
error: invalid value 'requests==2.0.0' for '--upgrade-package <UPGRADE_PACKAGE>': Not a valid package or extra name: "requests==2.0.0". Names must start and end with a letter or digit and may only contain -, _, ., and alphanumeric characters.

For more information, try '--help'.

Additionally, pip-compile also supports the following form:

pip-compile --upgrade --upgrade-package 'requests<3.0'

It would be great if uv supports this feature as well.

hongqn avatar Feb 25 '24 12:02 hongqn

Seems reasonable, thanks for the clear request :)

zanieb avatar Feb 25 '24 16:02 zanieb

I don’t think I fully understand what the semantics of this are, especially for the variant that’s a bound rather than a specific version. What happens if the resolution can’t succeed using the given range / version? Is it ignored? (I’m hesitant to support the variant that takes a bound, it just adds a lot of complexity. Should that not be a constraint provided via a constraint file?)

charliermarsh avatar Feb 25 '24 16:02 charliermarsh

I was also wondering if constraints could / should be used for this purpose. It does seem weird to provide arbitrary bounds on a single invocation.

zanieb avatar Feb 25 '24 16:02 zanieb

In my scenario, this need often arises due to security reasons: the dependency package has discovered security vulnerabilities and needs to be upgraded, but we hope to stay as close as possible to the version already deployed in production in order to avoid introducing faults.

hongqn avatar Feb 29 '24 07:02 hongqn

But, should / could that not be provided as a constraint file?

charliermarsh avatar Feb 29 '24 16:02 charliermarsh

should it? for me I tend to do something like pip-compile --output-file out.txt in.txt -P package~=X.Y

Where I treat the existing out.txt as the current state of the venv. When I run a command like this, I'm kinda asking a question:

  • what package if any fits this constraint, given my current venv?

sometimes the answer is yes, with no issues (and write a new out.txt) sometimes the answer is no, unpin other packages (and fail to write a new out.txt) and maybe it's "irredeemable constraints" (and fail to write a new out.txt)

Usually my in.txt doesn't usually have version pins (that's what out.txt is for), unless I know for certain new versions would break something. Are you suggesting alternative to using bounded -P args is to temporarily add bounds to in.txt?

Redoubts avatar Feb 29 '24 21:02 Redoubts

Any update on this one?

ewianda avatar May 13 '24 12:05 ewianda

No updates right now.

charliermarsh avatar May 13 '24 14:05 charliermarsh

What is the expected workflow for upgrading a package?

I'm in the process of migrating pip-tools to uv workflows. Previously, I'd use pip-compile requirements.in --upgrade-package boto3==x -o requirements.txt, which would take requirements.txt (as constriants) into account and make sure that boto3 is upgraded. If it can't be, it would upgrade necessary transient packages to make boto3 upgradeable to the version I had.

Right now, I use uv pip compile -c requirements.txt requirements.in -P boto3 -o requirements.txt, but it does nothing (there is newer version). If I remove boto3 manually from requirements.txt, it also does nothing (probably because of some transitive deps).

mariokostelac avatar May 29 '24 12:05 mariokostelac

Conceptually, is this the same as --upgrade-package boto3 with boto3==x as a constraint? I'm trying to understand the semantics of --upgrade-package boto3==x.

charliermarsh avatar Jun 03 '24 20:06 charliermarsh

Not quite, -U allows all requirements in the output to be updated but -P specifically operates with the new constraint and updates only the minimal set of requirements in the output to satisfy that additional constraint. If the new constraint can’t be satisfied with existing input constraints, pip compile errors.

mark-thm avatar Jun 03 '24 20:06 mark-thm

Thanks! Though I'm wondering specifically about the use of -P with a version specifier.

charliermarsh avatar Jun 03 '24 20:06 charliermarsh

Yep, so expectation of -P foo==1.2.3 is:

  • apply an additional constraint of “foo==1.2.3”
  • allow only output requirements necessary to meet this constraint to upgrade
  • still meet all input constraints

mark-thm avatar Jun 03 '24 23:06 mark-thm

Hi, just driving by to mention that this shows up when we try to have dependabot run updates using uv instead of pip-compile so a potential big impact from having it work exactly the same as pip-compile https://github.com/astral-sh/uv/issues/1964

avilaton avatar Jul 03 '24 19:07 avilaton

Dependabot will try to do exactly this

uv pip compile --build-isolation --output-file=requirements.txt \
    --no-emit-index-url \
    --no-annotate \
    --no-header \
    -P attrs==18.0.1 pyproject.toml

and uv complains with

error: invalid value 'attrs==13.0.1' for '--upgrade-package <UPGRADE_PACKAGE>': Not a valid package or extra name: "attrs==13.0.1". Names must start and end with a letter or digit and may only contain -, _, ., and alphanumeric characters.

avilaton avatar Jul 03 '24 19:07 avilaton

That’s helpful to know, thanks. We should probably support it then.

charliermarsh avatar Jul 03 '24 20:07 charliermarsh

It is my first time dearing to even look at a rust codebase but I would like to help. If you have a sample PR in which similar work has occurred, a few pointers or any guidance you think would help me find a way to send a PR for this, I'll take a shot at it. The carbon footprint of dependabot using pip-tools makes this work any effort.

avilaton avatar Jul 09 '24 12:07 avilaton

My initial thinking is something like: change Upgrade in package_options.rs to Packages(FxHashSet<Requirement>) rather than Packages(FxHashSet<PackageName>). Then, at some point, iterate over those requirements and add them to constraints, then proceed as usual.

charliermarsh avatar Jul 09 '24 21:07 charliermarsh