Skip to content

Conversation

charliermarsh
Copy link
Member

@charliermarsh charliermarsh commented Aug 20, 2025

Summary

Right now, if you put upgrade = false in a uv.toml, then pass --upgrade-package numpy on the CLI, we won't upgrade NumPy. This PR fixes that interaction by ensuring that when we "combine", we look at those arguments holistically (i.e., we bundle upgrade and upgrade-package into a single struct, which then goes through the .combine logic), rather than combining upgrade and upgrade-package independently.

If approved, I then need to add the same thing for no-build-isolation, reinstall, no-build, and no-binary.

@charliermarsh charliermarsh added the bug Something isn't working label Aug 20, 2025
@charliermarsh charliermarsh marked this pull request as ready for review August 20, 2025 16:58
@konstin
Copy link
Member

konstin commented Aug 21, 2025

The new behavior makes sense to me!


One thing that I found unexpected is that --no-upgrade causes --upgrade-package to be silently ignored:

$ uv venv -c -p 3.12 && uv-debug pip install numpy==2 && uv-debug pip install --no-upgrade --upgrade-package numpy pandas
Using CPython 3.12.11
Creating virtual environment at: .venv
Activate with: source .venv/bin/activate
Resolved 1 package in 8ms
Installed 1 package in 18ms
 + numpy==2.0.0
Resolved 6 packages in 25ms
Installed 5 packages in 17ms
 + pandas==2.3.1
 + python-dateutil==2.9.0.post0
 + pytz==2025.2
 + six==1.17.0
 + tzdata==2025.2

This is different from --no-build and --no-binary:

$ uv venv -c -p 3.12 && uv-debug pip install numpy==2 && uv-debug pip install --no-build --no-binary numpy pandas
Using CPython 3.12.11
Creating virtual environment at: .venv
Activate with: source .venv/bin/activate
Resolved 1 package in 8ms
Installed 1 package in 15ms
 + numpy==2.0.0
error: the argument '--no-build' cannot be used with '--no-binary <NO_BINARY>'

Usage: uv-debug pip install --no-build <PACKAGE|--requirements <REQUIREMENTS>|--editable <EDITABLE>|--group <GROUP>>

For more information, try '--help'.

When reading the code, my main confusion was that the struct isn't fully usable, the upgrade package list is only consider if neither --upgrade nor --no-upgrade are passed and the upgrade value is None. I was struggling to verify that the merge logic is correct, if we had two Option<UpgradeSelection>, only Some if any upgrade option was passed, the merging logic could be written as:

#[derive(Debug, Clone)]
pub enum UpgradeSelection {
    All,
    None,
    // Packages are relevant, otherwise `Option<UpgradeSelection>` would be `None`.
    Packages(Vec<Requirement>),
}

impl UpgradeSelection {
    #[must_use]
    pub fn combine(self, other: Self) -> Self {
        match self {
            // Setting `--upgrade` or `--no-upgrade` clear previous `--upgrade-package` selections.
            Self::All | Self::None => self,
            Self::Packages(self_packages) => match other {
                // With `--upgrade` previously, `--upgrade-package` is subsumed by upgrading all packages.
                Self::All => other,
                // With `--no-upgrade` previously, and now `--upgrade-package`, upgrade the explicit package list.
                Self::None => Self::Packages(self_packages),
                // If both had `--upgrade-package`, upgrade the combined package list.
                Self::Packages(other_packages) => {
                    let mut combined = self_packages;
                    combined.extend(other_packages);
                    Self::Packages(combined)
                }
            },
        }
    }
}

@zanieb
Copy link
Member

zanieb commented Aug 21, 2025

I agree with

One thing that I found unexpected is that --no-upgrade causes --upgrade-package to be silently ignored

I'd expect --upgrade-package to take precedence.

Otherwise, I'm a +1 on the behavior. It sounds actually doesn't require encoding a relationship between the persistent configuration and the CLI like we were discussing? Or does upgrade-package = ["foo"] with --no-upgrade perform no upgrades?

@charliermarsh
Copy link
Member Author

I thought we had discussed the the no-upgrade / upgrade-package behavior and decided we wanted to change it but that it was breaking? That’s why I left a TODO. It’s easy to change now either way.

Or does upgrade-package = ["foo"] with --no-upgrade perform no upgrades?

Yeah, that doesn’t perform any upgrades (which I think is correct).

@konstin
Copy link
Member

konstin commented Aug 21, 2025

I didn't see a TODO item for that, but delaying the breaking change to 0.9 makes sense, should we add a warning now that --no-upgrade with --upgrade-package doesn't work as expected that becomes an error in 0.9?

@zanieb
Copy link
Member

zanieb commented Aug 21, 2025

I thought we had discussed the the no-upgrade / upgrade-package behavior and decided we wanted to change it but that it was breaking?

Oh I see, I didn't realize you were separating it out into a bug fix and a breaking change. Sorry. I don't mind doing it that way as long as we're aligned on the end behavior.

@zanieb
Copy link
Member

zanieb commented Aug 21, 2025

Or does upgrade-package = ["foo"] with --no-upgrade perform no upgrades?

Yeah, that doesn’t perform any upgrades (which I think is correct).

Yeah that sounds correct to me. It does introduce that complexity of different semantics depending on the origin layer, but it sounds worth it.

@charliermarsh
Copy link
Member Author

Ah sorry, I guess the TODO is in the test suite (specifically, above the test with the undesirable result) and was introduced in the PR that added the tests, so doesn't appear here.

@charliermarsh
Copy link
Member Author

I didn't see a TODO item for that, but delaying the breaking change to 0.9 makes sense, should we add a warning now that --no-upgrade with --upgrade-package doesn't work as expected that becomes an error in 0.9?

I think I'd rather not do this because I don't want them to be an error in 0.9, I want --upgrade-package to override --no-upgrade.

@zanieb
Copy link
Member

zanieb commented Aug 21, 2025

I think I'd rather not do this because I don't want them to be an error in 0.9, I want --upgrade-package to override --no-upgrade.

I agree.

@konstin
Copy link
Member

konstin commented Aug 21, 2025

That's even better!

@charliermarsh
Copy link
Member Author

(I'll take a look at @konstin's suggested refactor, might make things simpler.)

@charliermarsh charliermarsh force-pushed the charlie/up branch 2 times, most recently from 59f4063 to 0c6ac04 Compare August 21, 2025 13:35
@charliermarsh
Copy link
Member Author

Okay, we now use an Option around the outer struct, etc.

}
}

#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
Copy link
Member

Choose a reason for hiding this comment

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

Should this type be (de)serialize?

Copy link
Member Author

Choose a reason for hiding this comment

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

No, thanks :)

@charliermarsh charliermarsh merged commit 0397595 into main Aug 21, 2025
94 of 95 checks passed
@charliermarsh charliermarsh deleted the charlie/up branch August 21, 2025 15:20
charliermarsh added a commit that referenced this pull request Aug 21, 2025
## Summary

After #15395, I realized that we didn't actually need a separate struct
for this since we now pass it around as an `Option`. (The key change
from #15395 is that when combining, we treat the options as a single
unit.)
charliermarsh added a commit that referenced this pull request Aug 21, 2025
## Summary

This is like #15395, but for `--reinstall` and `--reinstall-package`.
charliermarsh added a commit that referenced this pull request Aug 21, 2025
tmeijn pushed a commit to tmeijn/dotfiles that referenced this pull request Aug 22, 2025
This MR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [astral-sh/uv](https://github.com/astral-sh/uv) | patch | `0.8.12` -> `0.8.13` |

MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot).

**Proposed changes to behavior should be submitted there as MRs.**

---

### Release Notes

<details>
<summary>astral-sh/uv (astral-sh/uv)</summary>

### [`v0.8.13`](https://github.com/astral-sh/uv/blob/HEAD/CHANGELOG.md#0813)

[Compare Source](astral-sh/uv@0.8.12...0.8.13)

##### Enhancements

- Add `--no-install-*` arguments to `uv add` ([#&#8203;15375](astral-sh/uv#15375))
- Initialize Git prior to reading author in `uv init` ([#&#8203;15377](astral-sh/uv#15377))
- Add CUDA 129 to available torch backends ([#&#8203;15416](astral-sh/uv#15416))
- Update Pyodide to 0.28.2 ([#&#8203;15385](astral-sh/uv#15385))

##### Preview features

- Add an experimental `uv format` command ([#&#8203;15017](astral-sh/uv#15017))
- Allow version specifiers in `extra-build-dependencies` if match-runtime is explicitly `false` ([#&#8203;15420](astral-sh/uv#15420))

##### Bug fixes

- Add `triton` to `torch-backend` manifest ([#&#8203;15405](astral-sh/uv#15405))
- Avoid panicking when resolver returns stale distributions ([#&#8203;15389](astral-sh/uv#15389))
- Fix `uv_build` wheel hashes ([#&#8203;15400](astral-sh/uv#15400))
- Treat `--upgrade-package` on the command-line as overriding `upgrade = false` in configuration ([#&#8203;15395](astral-sh/uv#15395))
- Restore DockerHub publishing ([#&#8203;15381](astral-sh/uv#15381))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this MR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS44Mi4xIiwidXBkYXRlZEluVmVyIjoiNDEuODIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiUmVub3ZhdGUgQm90Il19-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants