The entire purpose of semver is to give you a way to resolve that conundrum. New major version = assume it's incompatible.
I mean, it may not actually work, but that's what it's for.
> The entire purpose of semver is to give you a way to resolve that conundrum. New major version = assume it's incompatible.
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.
There isn't a good way to know if a given package is using semver though.
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.
Semantic versioning is about versioning individual dependencies, no? The issue here seems to be about transitive dependencies, where different versions of the same package is used by multiple packages which depend on it.
uv's default being to always select the latest version seems to be what Clojure's tools.deps does.
The use or adherence to semver isn't the problem here. As you say, if a package follows semver, it's easy enough for the package managers to automatically update to newer compatible versions. The problem is when you want to have two different incompatible versions of the same package `foo` in the same program, because then you have to figure out what `import foo` means. You might say "just don't do that", but that package could be an indirect dependency of several of your direct dependencies. Some languages handle this natively, e.g. in Rust it just works if you have multiple versions of the same library in different parts of your dependency tree (and you'll get a compilation error if you try to pass a type from one version into a function of an incompatible version). But Python does not handle this use case very well.