Skip to content

Update the tilde version specifier warning to include more context#14335

Merged
zanieb merged 1 commit intomainfrom
zb/tilde-spec
Jul 2, 2025
Merged

Update the tilde version specifier warning to include more context#14335
zanieb merged 1 commit intomainfrom
zb/tilde-spec

Conversation

@zanieb
Copy link
Member

@zanieb zanieb commented Jun 27, 2025

Follows #14008

@zanieb zanieb added the internal A refactor or improvement that is not user-facing label Jun 27, 2025
Comment on lines 74 to 79
if let Some((lo_b, hi_b)) = release_specifier_to_range(spec.clone())
.bounding_range()
.map(|(l, u)| (l.cloned(), u.cloned()))
{
let lo_spec = LowerBound::new(lo_b).specifier().unwrap();
let hi_spec = UpperBound::new(hi_b).specifier().unwrap();
Copy link
Member Author

Choose a reason for hiding this comment

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

I found this hard to follow and there's a lot of indirection that can be avoided for the case we care about.

@zanieb zanieb temporarily deployed to uv-test-registries June 27, 2025 21:46 — with GitHub Actions Inactive
@zanieb zanieb temporarily deployed to uv-test-registries June 27, 2025 21:56 — with GitHub Actions Inactive
@zanieb
Copy link
Member Author

zanieb commented Jun 27, 2025

cc @aaron-ang — curious if you have any thoughts here

@aaron-ang
Copy link
Contributor

aaron-ang commented Jun 27, 2025

cc @aaron-ang — curious if you have any thoughts here

I was trying to reuse as much code as possible., but your implementation is definitely much cleaner!

@zanieb zanieb marked this pull request as ready for review June 27, 2025 23:12
@zanieb zanieb temporarily deployed to uv-test-registries June 27, 2025 23:14 — with GitHub Actions Inactive
@potiuk
Copy link

potiuk commented Jul 1, 2025

I think this warning added in #14008 is quite confusing and @zanieb maybe you can fix it here?

First if all it is very unclear what version it relates to - especially when you have a workspace.
We started to receive this warning in uv sync recently in airflow:

image

I looked at it and it really makes no sense ~=3.10 is perfectly valid specification, meaning ~=3.10,<4 and this is really what we mean in our specification of providers in our workspace, because our providers should be compatible with generally any future version of Python. Unless we specifically forbid it, anyhow in Airflow we upper bind Airflow itself, and it won't have upper-binding moved until we either check that all provider work with the new upper-binidng or we apply upper-binding to them. Also the message does not say how to fix it ... Is it ~=3.10,<4 explicitly or >=3.10,<4 -> we are not even sure what we are asked for.

So I thought (creatively) it is about something else - namly ~=3.10,<3.13 we had - because I thought it's about unnecessary <4 implicltly added. But I replaced all of those with >=3.10,<3.13 and we still got the same warning. So I am pretty sure it's about ~=3.10 in our provider's pyproject.tomls (deep into our workspace and many of those). Those are the only places left .. But it still does not seem to make a lot of sense - is this really the intention to warn when someone specified ~=3.10 ? What would be a "better" way of writing it (this one is nice and short).

Or maybe it's simply a but in the logic?

Or maybe I completely missed something ? (could be ... I was staring at the numbers for a while).

@zanieb
Copy link
Member Author

zanieb commented Jul 1, 2025

Thanks for the feedback! I'll take a deeper look, but briefly... the intent is to warn on ~=3.10 because most users will assume that's locking to >=3.10,<3.11 not >=3.10,<4 — for the former, you'd want ~=3.10.0 and for the latter you'd want it to remain the same (but it's probably better to disambiguate with the longer-form specification of >=3.10,<4). I can't think of a case where you'd want to write ~=3.10,<4, as that would expand to >=3.10,<4,<4?

@potiuk
Copy link

potiuk commented Jul 1, 2025

because most users will assume that's locking to >=3.10,<3.11 not >=3.10,<

Interesting - I never had that impression to be honest ~=3.10 is quite clearly specified in https://packaging.python.org/en/latest/specifications/version-specifiers/#compatible-release with good examples and I personally never had any confusion or problem with it. And to be honest it's first time I hear it is confusing, so I am not sure if the most statement is correct :D. Do you have some non-anecdotal data to back the most statement up?

for the latter you'd want it to remain the same

Well, Yeah. Actually I *would prefer to be able to still say ~=3.10 as it is very nice, short and concise. And the ~= is weird enough a syntax to raise an eyebrow of people to consider what it is. And it does seem like departing from the agreed/standard scheme to suggest to change it . Seems like even if someone really wants to keep it (I've seen it already used in quite a number of packages, so that calls for them to change and adapt their processes). For one - in our case that version is automatically generated and we would have to redo some of our release processes (including some tests and automation) to change it - plus the specs will still remain in the old packages so that might create even more confusion to be honest.

Is there a way to silence it ?

@potiuk
Copy link

potiuk commented Jul 1, 2025

And BTW. Regardless from the result of the discussion - at the very least - the message should explain what the problem is and how to solve it :)

@zanieb
Copy link
Member Author

zanieb commented Jul 1, 2025

We have a couple comments, e.g.:

Part of the problem is that the .0 is implied for some uses, e.g., ==3.13 means ==3.13.0, but this does not apply for the tilde operator. Additionally, the behavior is not consistent across ecosystems, e.g., in Cargo, ~1.2 is equivalent to >=1.2.0, <1.3.0 (see https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#tilde-requirements).

I'll certainly be considering reducing the visibility of the warning, but we'd need to hear more feedback that it's doing more harm than good.

@zanieb
Copy link
Member Author

zanieb commented Jul 1, 2025

I'm also updating the error message to be more informative

@zanieb zanieb changed the title Refactor tilde version specifier handling Update the tilde version specifier error message Jul 1, 2025
@zanieb zanieb added enhancement New feature or improvement to existing functionality and removed internal A refactor or improvement that is not user-facing labels Jul 1, 2025
@zanieb zanieb changed the title Update the tilde version specifier error message Update the tilde version specifier warning to include more context Jul 1, 2025
/// or `~=3.13.0`.
#[derive(Clone, Debug)]
pub struct TildeVersionSpecifier<'a> {
inner: Cow<'a, VersionSpecifier>,
Copy link
Member Author

@zanieb zanieb Jul 1, 2025

Choose a reason for hiding this comment

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

Alas I wrote this as using a borrowed version specifier then wanted to use bounding_specifiers alongside a with_patch utility to improve error messages and needed the ability to own the specifier. It's not a big deal, but that's why it is this way.

Copy link
Member

Choose a reason for hiding this comment

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

imo we can just always clone those outside the resolver, it's not worth the complexity

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 annoying part is that I didn't want to take an owned value for the None case and didn't want to hide a clone. I think if I knew I'd need an owned value later I would have just done it anyway, but I don't expect it to be a big deal to maintain now that it's there.

) -> Option<Self> {
// Convert to PubGrub range and perform an intersection.
let range = specifiers
.map(|specs| {
Copy link
Member Author

Choose a reason for hiding this comment

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

This moved up a level, where was have access to the project and group names the specifiers come from

@zanieb zanieb requested a review from konstin July 1, 2025 22:07
@zanieb zanieb temporarily deployed to uv-test-registries July 1, 2025 22:14 — with GitHub Actions Inactive
@potiuk
Copy link

potiuk commented Jul 2, 2025

Part of the problem is that the .0 is implied for some uses, e.g., ==3.13 means ==3.13.0, but this does not apply for the tilde operator. Additionally, the behavior is not consistent across ecosystems, e.g., in Cargo, ~1.2 is equivalent to >=1.2.0, <1.3.0 (see https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#tilde-requirements).

Yep. I understand the potential ambiguity, and that some people might get confused when they are writing their own specification and that's fine and warning by default is good. This is good for people who switch between ecosystems for sure and for newcomers. So I am perfectly fine with having a warning with more explanation in this case by default (and pointing to the right place where it happens).

But also I think you have to consider the ripple effect it might have for "seasoned" people - who will suddenly get the warning in their projects, and the only option they have is to change their specification (which might go well into many thousands workflows - It would be worth doing some checks on Github/PyPi to see how many packages already specify ~X.Y and my educated guess will be A LOT. So I think a good solution would be to allow - by uv configuration in pyproject.toml file (ideally in the workspace one) to silence the warning - AKA "We know what we are doing".

That would be way easier to handle by the "seasoned" maintainers (especially if the warning message will hint at the possibilty of disabling it for those who know what they do) - rather than trying to "force ambiguity correction on all maintainers" :). Souds a bit "harsh" - especially that it will affect contributors. If some maintainers do not use uv for development environment, and some of their contributors will - this will cause a tension, because uv users will have warning where non-uv users will not and maintainers will be "pressured" to change their specifications if if they know what they do and chose it consciously.

@ashb
Copy link

ashb commented Jul 2, 2025

I personally don't think the difference in behaviuor across ecosystems is a factor here -- we are dealing with Python dependency specifiers so people need to know how Python ecosystem behaves here, which to me should follow the behaviour set out in the PyPA-maintained Version Specifiers - § Compatible release

For example, the following groups of version clauses are equivalent:

~= 2.2
>= 2.2, == 2.*

~= 1.4.5
>= 1.4.5, == 1.4.*

/// or `~=3.13.0`.
#[derive(Clone, Debug)]
pub struct TildeVersionSpecifier<'a> {
inner: Cow<'a, VersionSpecifier>,
Copy link
Member

Choose a reason for hiding this comment

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

imo we can just always clone those outside the resolver, it's not worth the complexity

@zanieb zanieb merged commit 43f67a4 into main Jul 2, 2025
170 of 171 checks passed
@zanieb zanieb deleted the zb/tilde-spec branch July 2, 2025 14:08
tmeijn pushed a commit to tmeijn/dotfiles that referenced this pull request Jul 6, 2025
This MR contains the following updates:

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

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.7.19`](https://github.com/astral-sh/uv/blob/HEAD/CHANGELOG.md#0719)

[Compare Source](astral-sh/uv@0.7.18...0.7.19)

The **[uv build backend](https://docs.astral.sh/uv/concepts/build-backend/) is now stable**, and considered ready for production use.

The uv build backend is a great choice for pure Python projects. It has reasonable defaults, with the goal of requiring zero configuration for most users, but provides flexible configuration to accommodate most Python project structures. It integrates tightly with uv, to improve messaging and user experience. It validates project metadata and structures, preventing common mistakes. And, finally, it's very fast — `uv sync` on a new project (from `uv init`) is 10-30x faster than with other build backends.

To use uv as a build backend in an existing project, add `uv_build` to the `[build-system]` section in your `pyproject.toml`:

```toml
[build-system]
requires = ["uv_build>=0.7.19,<0.8.0"]
build-backend = "uv_build"
```

In a future release, it will replace `hatchling` as the default in `uv init`. As before, uv will remain compatible with all standards-compliant build backends.

##### Python

- Add PGO distributions of Python for aarch64 Linux, which are more optimized for better performance

See the [python-build-standalone release](https://github.com/astral-sh/python-build-standalone/releases/tag/20250702) for more details.

##### Enhancements

- Ignore Python patch version for `--universal` pip compile ([#&#8203;14405](astral-sh/uv#14405))
- Update the tilde version specifier warning to include more context ([#&#8203;14335](astral-sh/uv#14335))
- Clarify behavior and hint on tool install when no executables are available ([#&#8203;14423](astral-sh/uv#14423))

##### Bug fixes

- Make project and interpreter lock acquisition non-fatal ([#&#8203;14404](astral-sh/uv#14404))
- Includes `sys.prefix` in cached environment keys to avoid `--with` collisions across projects ([#&#8203;14403](astral-sh/uv#14403))

##### Documentation

- Add a migration guide from pip to uv projects ([#&#8203;12382](astral-sh/uv#12382))

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

[Compare Source](astral-sh/uv@0.7.17...0.7.18)

##### Python

- Added arm64 Windows Python 3.11, 3.12, 3.13, and 3.14

  These are not downloaded by default, since x86-64 Python has broader ecosystem support on Windows.
  However, they can be requested with `cpython-<version>-windows-aarch64`.

See the [python-build-standalone release](https://github.com/astral-sh/python-build-standalone/releases/tag/20250630) for more details.

##### Enhancements

- Keep track of retries in `ManagedPythonDownload::fetch_with_retry` ([#&#8203;14378](astral-sh/uv#14378))
- Reuse build (virtual) environments across resolution and installation ([#&#8203;14338](astral-sh/uv#14338))
- Improve trace message for cached Python interpreter query ([#&#8203;14328](astral-sh/uv#14328))
- Use parsed URLs for conflicting URL error message ([#&#8203;14380](astral-sh/uv#14380))

##### Preview features

- Ignore invalid build backend settings when not building ([#&#8203;14372](astral-sh/uv#14372))

##### Bug fixes

- Fix equals-star and tilde-equals with `python_version` and `python_full_version` ([#&#8203;14271](astral-sh/uv#14271))
- Include the canonical path in the interpreter query cache key ([#&#8203;14331](astral-sh/uv#14331))
- Only drop build directories on program exit ([#&#8203;14304](astral-sh/uv#14304))
- Error instead of panic on conflict between global and subcommand flags ([#&#8203;14368](astral-sh/uv#14368))
- Consistently normalize trailing slashes on URLs with no path segments ([#&#8203;14349](astral-sh/uv#14349))

##### Documentation

- Add instructions for publishing to JFrog's Artifactory ([#&#8203;14253](astral-sh/uv#14253))
- Edits to the build backend documentation ([#&#8203;14376](astral-sh/uv#14376))

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

[Compare Source](astral-sh/uv@0.7.16...0.7.17)

##### Bug fixes

- Apply build constraints when resolving `--with` dependencies ([#&#8203;14340](astral-sh/uv#14340))
- Drop trailing slashes when converting index URL from URL ([#&#8203;14346](astral-sh/uv#14346))
- Ignore `UV_PYTHON_CACHE_DIR` when empty ([#&#8203;14336](astral-sh/uv#14336))
- Fix error message ordering for `pyvenv.cfg` version conflict ([#&#8203;14329](astral-sh/uv#14329))

</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:eyJjcmVhdGVkSW5WZXIiOiI0MC42Mi4xIiwidXBkYXRlZEluVmVyIjoiNDAuNjIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiUmVub3ZhdGUgQm90Il19-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or improvement to existing functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants