I can't really take the article fully seriously when they are like "uv cant do this. Well actually it can but you gotta use an extra flag." It reads rather PEBKAC.
As someone part of the problem with a couple ancient packages in PyPI that are calver (though arguably I'd be surprised if many depended on them), I wonder if it is too late for more of the Python ecosystem to shift more directly to semver by default/everywhere as some of the other ecosystems now are.
Maybe when uv knows the project isn’t a library it could default to upper bounds?
> uv add pydantic --bounds major
So not really sure what he's complaining about
His point is that the cli experience of uv is badly designed. And having upper bounds an opt-in is another point he makes in that.
Also, the overuse of the term "clickbait" for anything we don't agree with is ...clickbait.
> So not really sure what he's complaining about
It says it on the bloody title of his post: UV's "package management UX is a mess".
He's complaining that upper bounds by default would the good choice, and that UV potentially nuking your deps by default is bad. And that the whole UX around uv updates is bad in general, for which he gives several examples.
We can argue if he's right or wrong about each of those, but it's pretty clear what he complains about.
For libraries, having loose bounds might mean that users upgrade and hit issues due to a lack of an upper bound. But given how lightly maintained most projects are, the risk of upper bounds simply getting in the way are higher IMO.
(Put an upper bound if you know of an issue, of course!)
It's a bit tricky though. Django deps in particular tend to want to explicitly check support for newer versions, but the more I think about it the more I ask myself if this is the right strategy
And then we'd have to run uv again with an argument to not have upper bounds. Oh, the humanity!
As opposed, to it nuking our dependencies with incompatible packages.
Doesn't sound like the unsafe option should be the default.
I mean, it may not actually work, but that's what it's for.
I solved this issue a few months ago. Created a tool that essentially allows the use of multiple envs at once, with their own versions of packages at any level.
program
├── dependency_a
│ └── dependency_c (1.0.0)
└── dependency_b
└── dependency_c (2.0.0)
Otherwise, you've created a magic layer hack to enable multi-version dependency chains in a mono-version dependency chain language. monolith -> openai
monolith -> langchain-openai -> openai
openai, thus, is both a direct and indirect dependency. langchain-openai recently had a vulnerability, and the patch fix is only after a major upgrade to openai. Thus, to upgrade langchain-openai here, I also need to upgrade monolith's use of openai. (From v1 to v2.)I'm not sure I'd agree with that characterization. The point of semver is that you can assume that certain types of bumps won't include certain types of changes, not that you assume that the types of changes that can happen in a type of bump will happen. A major version bump not breaking anything is completely valid semver, and breaking one function (which plenty of users might not use, or might use in a way that doesn't get broken) in an API with thousands is still technically only valid in a major version bump (outside of specific exceptions like the major version being 0).
It's a subtle difference, and I'm optimistic that it's something you understand, but misunderstandings of semver seem so common that I can't help but feel like precision when discussing it is important. I've encountered so many smart people who misunderstand aspects of semver (and not just minutia like "what constraints are there on tags after the version numbers"), and almost all of them seemed to have stemmed from people learning a few of basic tenets of it and inferring how to fill in the large gaps in a way that isn't at all how its specified. The semver specification is pretty clear in my opinion even about where some of the edge cases someone less informed might assume, and if we don't agree on that as the definition, I don't know how we avoid the (completely realistic) scenario where everyone in the room has an idea of what "semver" means that's maybe 80% compatible with the spec, but the 80% is different for each of them, and trying to resolve disagreements when people don't agree about what words mean is really hard.
… an assumption that something happened is not a definitive statement that it did happen, only that we're assuming it did, because it could happen, or perhaps here, that because the major was bumped, that it is legal, according to the contract given, for it to have possibly happened in a way that we depended on. They're not saying that it will/must; "assume a major version is incompatible" is not at odds with what you've written.
Semver says “major version MUST be incremented if any backward incompatible changes are introduced to the public API.” You’re correct that it doesn’t say “major version MUST NOT be incremented if there are no backward incompatible changes”, so technically that is possible — but it would be a very odd thing to do.
There's a lot of packages in the Python ecosystem that use time based versioning rather than semver (literally `year.minor`) and closed ranges cause untold problems.
uv's default being to always select the latest version seems to be what Clojure's tools.deps does.