#packaging
1 messages ยท Page 2 of 1
I was going to say:
would it be useful to put this into prek
then I checked and Henry has already opened https://github.com/j178/prek/issues/1864 ๐
The downside to having it in prek is it wouldn't run in prek -a. ๐ But I think it should be there too. I've hand (well, with an LLM to start it) written a Python-based pre-commit check in https://github.com/pypa/packaging/pull/1133. I had to fall back on querying the GitHub API because something is weird with typo's tags, I think they get force pushed as part of the release process changelog generation, if I had to guess. The autoupdater for both tools doesn't have a problem.
The moment I hit post to say something is reviewable is the moment I spot half a dozen things I don't like
That is my lived experience as well. Usually I spot the issues immediately after I open the PR.
The moment someone approves it is when I usually find things I don't like. ๐
FWIW, since we have defined __all__ and __dir__, I don't think it's necessary to start everything with underscores. I think the classes/functions are okay, to help highlight they are really not public, but the global constants probably don't need underscores unless you really want them to.
(though it doesn't matter too much)
Would it be better to make _BEFORE_LOCALS and _AFTER_LOCALS sentinel objects or enums, instead of ints?
I asked copilot (locally) for a review (just before posting my comments, so it wouldn't be able to cheat), and it gave five suggestions. Three of them are basically my suggestions: magic ints - it literally called them magic ints like I did), dataclasses (though it suggested frozen dataclasses), and move the type alias import. One of them was to modularize large test matrix, which I don't think matters. The last one, though, is that _get_intervals() always recomputes self._intervals, rather than returning the cached value if it is not None.
_get_intervals uses cache on 1120 but not 1119 because it can never be hit, due to the higher level _is_unsatisfiable cache meaning it will never hit
You really need to tell LLMs to actually validate their conclusions
At least, in my experience that makes them produce 10x more useful commentry
This is what it didn't like:
def _get_intervals(self) -> list[_SpecifierInterval]:
"""Compute and cache the intersected interval representation.
Returns an empty list if unsatisfiable, or the intersected interval
list otherwise. ``===`` specs are modeled as full range (no
constraint).
"""
specs = self._specs
# Intersect specs' intervals, with early exit on empty intersection.
result: list[_SpecifierInterval] | None = None
for s in specs:
if result is None:
result = s._to_intervals()
else:
result = _intersect_intervals(result, s._to_intervals())
if not result:
break
assert result is not None # specs is non-empty
self._intervals = result
return result
Ahh, I see, it is never called except in a place where _is_unsatisfiable is already verified. IMO it needs a little comment explaining that. It's just as confusing to a human reader unless they know to check the places it's called.
Yeah, I'll add that
I don't necessarily want it to be smarter than a human (by running code), I want it to see what a human would see, I think. ๐
It's been a fun exercise building the un/satisfiable Specifier lists, some real gotchas in there, can perhaps put on a Python packaging trivia quiz, e.g. why is >1.0a1,<1.0a2 satisfiable?
o_O
I'm very happy with how readable https://github.com/pypa/packaging/pull/1119 turned out, it really clearly shows the complexity of PEP 440
Post releases I think?
Nope ๐
nvm, found more edge cases where this currently fails to produce the correct answer ๐ญ
==1.0.post0.dev0,<1.0.post1 and >=1.0.dev0,<1.0.post1,!=1.0,!=1.0.post0 manage to trigger multiple interesting parts of the spec that I was not modelling correctly ๐
should just raise an ThisIsActuallyStupid("you should be fired from packaging python code") exception
For the morbidly curious on why <V.postN and pre-releases causes issues representing specifiers as intervals: https://github.com/pypa/packaging/pull/1119#issuecomment-4148347905
Ignore everything I wrote, I realized this is a specification issue
I've suggested a behaviour change to packaging, not sure I love it, but I think it identifies a discrepancy with spec: https://github.com/pypa/packaging/pull/1140
Hi! I'm working on a tool that reads an existing wheel's METADATA file, modifies some fields, and writes it back. I'm using packaging.metadata.Metadata.from_email() for reading which seems clearly right, but I'm less sure about the writing side.
packaging v26.0 added Metadata.as_rfc822() which looks like it could work, but I've seen suggestions that packaging wasn't originally intended for writing these files. pyproject_metadata.StandardMetadata is another option but feels more oriented toward pyproject.toml โ METADATA generation rather than mutating an existing file.
What's the recommended approach for round-tripping an existing METADATA file? Is packaging v26.0's serialization considered stable/appropriate for this use case?
Not a packaging maintainer, but IIRC METADATA files are not supposed to be changed, but rather considered a read-only information. Why are you modifying the file?
What is the reason for trying to mutate METADATA files? Those files are generated at build time by build backends such as setuptools, hatchling, etc. and mutating them post install sounds like a big security risk because provenance of the artifacts is now broken.
Thanks for the replies. The purpose is to change the metadata during building compared to what the backends wrote, rather than take existing artifacts and mutate them.
Maybe just change what's in the source in the first place is the better approach?
To answer your question about is round tripping considered stable, I've never touched this code,but I assume not, at best packaging is just intended to read, I think build back ends have their own implementation to write. That may change in the future if we get JSON metadata.
I would look at your backend of choice and implement what it's using, which is probably the email module for the Python standard library
as_rfc822 does write these files, what is it for if not writing an rfc822 file?
Trying to perfectly round trip a metadata file with packaging probably wonโt work in terms of byte for byte round tripping
Yes, it was not originally able to write these files, that's why it wasn't in before 26.0. ๐
It won't byte for byte, no, it has a fixed format.
It should be possible to semantically do it just fine though
It's not really all that useful yet, since we can't read pyproject.toml's yet, but that's something I'm slowly working on.
But for reading, modifying, then writing, as long as sematics is what you care about, I think if it fails it's a bug.
You can sniff those files to figure out some otherwise opaque information about how the backend got its data. I learned this while exploring how to recover extras "accurately". Setuptools will write the fields in different orders depending on whether you really provide an extra or tried to be sneaky with the extra marker in a regular dependency. I wouldn't be surprised if there are more useful cases of looking at the ordering of the fields. Mine is just a weird thing I noticed.
Thanks very much for the helpful info. Will have a closer look at the options for my use case ๐
I'm thinking of adding property based testing to packaging, in theory we should be able to translate statements in the spec to property based tests. I've been using it in a library in writing that might be a new downstream user of packaging and it's great at finding stuff that relates to the spec I implemented
with hypothesis?
Yes
PBT is fantastic for checking against a spec. Plus you can hook hypothesis up to a fuzzer https://hypothesis.readthedocs.io/en/latest/how-to/external-fuzzers.html and get minimized reproducers on failures
ugh I messed up the git history on my PR ๐
hope itโs not too annoying to squash it into one commit
I think all PRs are squashed on packaging
likely the case, I tend to try to do that on all projects I can lol
I've previously struggled with this, probably since I haven't spent enough time around it, but I've found that hypothesis tests tend to take a lot longer and slow down things significantly. Should I be minimizing or adapting periodically or something?
Speed is definitely a weakness. I think this can be mitigated by reducing the size of the model you are checking, making sure that the data you are testing against is generated quickly, run it parallel to your other tests (pytest/-xdist is not good at load balancing long running tests unfortunately), reduce max_examples, etc.
Sorry, I realized I'm not sure if you're talking about PBT in general or specifically for fuzzing. Hopefully the above is not reiterating what you already know ๐
Liam DeVoe told me heโd be at PyCon if youโll be there and want to learn more from one of the experts
re: C implementation of packaging's Version in stdlib - I have noticed some libraries require packaging only to check for versions of extra dependencies (like gunicorn checking for libs: https://github.com/benoitc/gunicorn/blob/3e2167c346aacad77536026a70ca6425f034f9c0/gunicorn/workers/ggevent.py#L11-L18). I think having a stdlib implementation for that, to extend what importlib.metadata offers, would be nice
Property based testing of the whole of PEP 440 (well, the living specification version): https://github.com/pypa/packaging/pull/1144
in the past i've just set an env var on hypothesis tests that controls how long they go. if a change feels risky, you can have it run for much longer. hypothesis.example and experiments db also useful
ai-python a now seemingly defunct project from 2021, pins most of their dependencies, but in early versions of the project instead of using == they mostly used <=V,>=V e.g.
Requires-Dist: setuptools (<=54.2.0,>=54.2.0) ; extra == 'required'
Requires-Dist: spacy (<2.3.5) ; extra == 'required'
Requires-Dist: tensorflow (<2.6,>=2.2.2) ; extra == 'required'
Requires-Dist: termcolor (<=1.1.0,>=1.1.0) ; extra == 'required'
Requires-Dist: toml (<=0.10.2,>=0.10.2) ; extra == 'required'
Requires-Dist: torch (<=1.7.1,>=1.7.1) ; extra == 'required'
Requires-Dist: torchvision (<=0.8.2,>=0.8.2) ; extra == 'required'
Found looking at Henry's search of project requirements:
https://github.com/pypa/packaging/pull/1140#issuecomment-4181987311
This is interesting. Have you considered, if it would make more sense for the spec to change and maybe making a dpo thread to discuss such possibility? <1.0.post1 causing pre-releases to be included seems strange
It seems strange, but if you don't allow it you break the monotonic invariant that if v2 < v1 and v1 matches <V then v2 must match <V
And to be clear <1.0.post1 in packaging can already accept pre-releases of versions <1.0, and it would still not allow pre-releases of 1.0.post1, this change means it would start to allow pre-releases of 1.0 and 1.0.post0
The spec is complicated, there's two parts of the spec this is interacting with at the same time:
The exclusive ordered comparison <V MUST NOT allow a pre-release of the specified version unless the specified version is itself a pre-release.
And:
Pre-releases of any kind, including developmental releases, are implicitly excluded from all version specifiers, unless they are already present on the system, explicitly requested by the user, or if the only available version that satisfies the version specifier is a pre-release.
So a pre-release won't be accepted by default unless there is no other option, so this does not change the result of <1.0.post1 when the options available are 1.0.b1 and 0.9, in that case 0.9 will continue to be chosen, it only changes it if you have <1.0.post1 and your only option is 1.0.b1, prior to this change 1.0.b1 would not be accepted, now it will be accepted.
Oh
My bad, I didn't know the exclusion of prereleases from all specifiers is defined separately
The pre-release exclusion rule has been a real pain to implement and the source of many many bugs, uv has chosen not to comply with this rule, because they use pubgrub-rs as the basis of their resolver, and it considers each version independently, and this rule requires you to have information about the entire set of versions you're considering
I am working on an extension to pubgrub right now, just last night I got this rule working at the resolver level, no complicated provider hacks needed
uv also reinterprets what "explicitly requested by the user" means compared to pip/packaging, specifically packaging interprets this to mean that is a pre-release is part of the specifier version then it is explicitly requested by the user, e.g. <=1.0beta10 includes pre-releases, and pip just inherits this on all dependencies.
uv only accepts this interpretation for requirements that user has explicitly provided (command line, requirements file, your own projects pyproject.toml) it does not consider a dependency or transitive dependency to have a requirement like that to be explicitly requested by the user, which is a fair enough interpretation. But it means some requirements won't work in uv unless the user explicitly allows pre-releases for transative dependencies, e.g. if a dependency specifies ==1.0beta1 on a transitive dependency it will fail by default in uv.
uv also doesn't currently support a way to opt in or out of pre-releases on a per-package basis like pip does: https://pip.pypa.io/en/stable/cli/pip_install/#cmdoption-all-releases (uv bashing over ๐ )
Bashing the spec a little, the main problem with it is conflating two different levels: The core version specification, and tool policies for interacting with versions. Were we to rewrite it today I would want those two things decoupled, one spec would be the pure mathematics of version specifiers, and another spec would be reccomended policies for tools.
I guess this lesson feeds into the wheel variant PEPs, which have indeed been decoupled
also that PEPs should have test suites for their specsโฆ
end-to-end tests with real-world packages
Astral tried to provide a tool agnostic test suite: https://github.com/astral-sh/packse. But I don't think it got any buy in from other tools, and when I spent some time reviewing it certain choices are in fact not agnostic or spec complaint and I never had time to work on it.
It's more like we desperately needed a tool so I made one haha
I would love for it to be generalized into a standard but yeah in its current form it reflects uv's choices and nobody has the time to advance it into an ecosystem utility
It's a great idea, but when it came out I didn't have enough knowledge and experience with Python packaging to be able to fully understand where it's strengths and weaknesses were, I just wanted to wite it up to pip testing and get an immediate win, and it didn't work in a lot of situations so I didn't have the time or knowledge to figure it out
this is still logically equivalent, though, I hope?
Yes, it is!
phew
because that er, "quiz" from the other day (I never saw an answer key) made me wonder...
this one
Honestly, I'm not always sure when something looks like it should be logically equivalent if it actually is
@mental locust, are you perhaps the best person to query about the packaging project? In https://discuss.python.org/t/pep-825-wheel-variants-package-format-split-from-pep-817/106196/46, Paul asked about necessary parse_wheel_filename() changes due to wheel variants changing the filename. My best idea so far would be to soft-deprecate that function to avoid breakage and add a new function that accounts for the variant label. Basically, the old one will continue working for non-variant wheels and will raise an exception for variant wheels, while the new one will work for all wheels. How does that sound?
Can you open an issue on the packaging repo, and we can discuss there, there's already a lot of talk about replacing that function with a new API, so perhaps this could feed into that.
Sure, will do that.
I have an open PR to add a new API, just waiting on trying to match PyPI error messages. That might be perfect to include variants.
I posted a plan for 26.1, comments (or edits from maintainers) welcome. ๐ https://github.com/pypa/packaging/issues/1149
I saw, I've been completely slammed this week so far, I was hoping to update my status on that tonight
I've been hearing exciting stuff around here lately. Been thinking about reaching out to LWN again to write up something about it
#1145 adds a public API for markers, using dataclasses to make objects (internally it continues to use lists of strings recursively to represent nodes, no runtime cost). Opinions on the public API? I'd like a second opinion before adding new API. And/or use .operands.
I only read through #1145 pretty briefly, but it's an API that I would like using. It does seem a little strange that the canonical internal representation is a list of strings, but then a tree can be reconstituted from it. But maybe that's no big deal? I don't know enough about the marker parsing to say if it's cause for concern.
Just as a user giving feedback on "as_ast()", I'd say it looks good.
It's a recursive list of strings, technically. I think using this internally would both be slower, and break the packages using the internals now. Though we could swap the internal representation and generate the "internals", but as I said, I expect it's slower than strings.
Thanks for the feedback! I think it's pretty standard for a node tree.
All the minimum viable release PRโs are in, does anyone want to review the other three or so that might go in or might not?
Itโs really just two, the CLI one can move
I might have time later today to review, I can't guarantee but I'll try
OK, thanks! If you can itโs really the first and last in that list, the CI one can wait till the next release if you want. uv team seems like theyโre OK with fixing marker order to match the spec two, UV inherits some of packagingโs inflexibility, but some of the cases work reverse already there.
Actually, letโs hold off on the marker order one, thereโs some discussion from uv in changing the spec rather than updating the handling
So the new API one is the only one that really needs to be looked at before a release
I added a review to the Marker API, I found a couple of issues you may or may not agree with
I think it's fine to delay this to 26.2, there's also a followup needed to convert the trees back into Marker's. It would also give us some time to see if the projects needing it can test it out.
So I think we are ready for release. Do we want an RC? What sort of timing are people interested in?
Yeah, I think we at least need a PoC PR that can unambiguously turn the output of to_ast back into a marker before accepting, proof of round tripping stability would be a big bonus
I don't think we need and RC, depends how risk averse you are ๐
We needed them last time due to the release mechanism changing, but now that should be stable. This is also about 3x/4x less development time, so should be smaller. Though I'm somewhat risk averse. ๐
I'll prepare the release bump PR, and we can discuss there, I guess. I'll assume 26.1 for now.
thanks again for putting a release together!
Tagged, deployment awaiting approval. ๐
We need to figure out a way that more maintainers can approve deployment
On the pip side the release manager is responsible
I can add people to that list (up to 6, currently at 4, or switch to a team)
I think a team makes sense?
On the pip side the release manager is responsible
That sounds like one person?
Yeah, when I'm the release manager on pip I build and approve the release
It's all done on the CI side with trusted publishing, but I don't need to wait for someone else to sign off on release
I could add myself to the release approval list and sign off on my own release ๐ But I like having it a team effort. If I could require 2 approvals, I'd add myself to the list.
Oh, I can require a different person from the one who made the release to approve, that might be useful.
The current list mirrors the PyPI owners. Since only those people can yank a bad release, I do want to make sure they are aware of releases, in case they have to respond and yank. That was the thought behind the current list.
I think that's definitely useful if you're trying to have the approval be meaningful security
GH's UI around deployments is terrible. It loves to show the diff for the deployment instead of taking you to the page with the approval banner. I found if you click "..." then "view workflow run", you can get there from the normal pages. Otherwise you have to go to actions to find it.
I think that list are also admins on the repo on GitHub, which means if an account was hacked then they could just change the setting and deploy anyway. ๐ But yes, I like the idea.
yeah this is a struggle
I enabled it anyway, might be a small improvement.
Does anyone know how to just run tagged commits with asv? I thought it was possible, but didn't see an easy way and looping over with tag^ caused it to run overnight and run I think every commit from the start to the tag.
Release is live: https://github.com/pypa/packaging/releases/tag/26.1
Approved!
How am I supposed to handle ExceptionGroups in a backwards compatible way? Either the built-in group is raised or a custom packaging "group" exception is raised. The latter is not documented in the API.
Also congrats on the release!
Reading the PR that added exception groups to packaging, it seems like the intention is that users import packaging.errors.ExceptionGroup despite being undocumented.
It's in the documentation but I presume kept opaque intentionally https://packaging.pypa.io/en/stable/metadata.html#packaging.metadata.ExceptionGroup
It's an re-export from packaging.errors ยฏ_(ใ)_/ยฏ
It was originally there, but now it's in errors and re-exported for back-compat
Documentation is probably an oversight
Missing error.rst is a mistake, as is no docs - since it's actually just ExceptionGroup from the stdlib on Python 3.11+, and we build the docs with 3.14 or so, it's empty.
Also, an advanced tip, if you already have an ExceptionGroup backport, you should be able to replace errors.ExceptionGroup with it. I don't think I want to document that, but I do that with pyproject-metadata in scikit-build-core to get the nicer backport.
Ouch, mistake in the pyemscripten implementation, PYEMSCRIPTEN_ABI_VERSION -> PYEMSCRIPTEN_PLATFORM_VERSION. Though I think it actually works better as ABI, as it's not the whole platform.
Do we want to add support for patch releases, or just call the next release 26.2? We need to fix this and get a release out, sadly, it doens't follow the PEP as is.
I'm guessing our release system doesn't support patch relases since it's not semver.
The
packaginglibrary uses calendar-based versioning (YY.N).
Go for 26.2
We went back and forth on this a bit, platform version won out for various reasons, but I donโt think it maps 100% cleanly to either
Agreed, second digit only feels significant because packaging releases so infrequently
It has moved faster in the past. ๐ Is there anything else that is needed for this release? Has someone tried removing dependency-groups in pip yet? (I can quickly try if not). And maybe pylock or direct_url updates in pip?
We could get into a problem if we released YY.N dropping support for Python 3.X then make a new release, then need a "patch" release that fixes the old Python version... (that's why I consider dropping a Python version to always require a minor bump in semver-based packages)
I don't think any PRs have been raised on pip side to use packaging 26.1, I plan to raise one later today, but it's specifier related
There is one for Direct URL
I'd like to replace dependency-groups, but that PR will have to wait until Friday at the earliest
I can make it if you want (I actually already did it assisted by kimi-K2.5, but there are 13 errors locally before editing so I can't be sure it's passing (error handling is the only real change)
@solar wind what about pylock? Doesn't have to be a PR, just wondering if there are any possible things that might break, wondering if you've tested it yet.
pylock is looking good too: https://github.com/pypa/pip/pull/13876
Great!
If nothing comes up, maybe we can release later today?
My dep-group change has a matching number of errors after making it (13), so maybe it's fine. I can make a PR and @solar heron can edit/start from there if it's easier (or not, doesn't matter to me).
I notice I just beat Henry on number of PRs in packaging 26.1: https://github.com/pypa/packaging/pull/1156, 24 vs 22. I keep getting nerd snipped by possible performance improvements.
Also I'm sure a couple of mine a directly based on mistakes I made in earlier PRs, so take away those and I'm certainly less.
My goal is to always be third. ๐
Iโm quite surprised we (which means you) managed another 2x performance boost on top of 26.0
I didn't realize I needed to call dibs if I wanted to do the pip PR for dep groups! ๐
LMK if you need review eyes.
Please do review! I mostly wanted to make sure it worked.
Happy to grant edit privileges if you want them to my fork
Don't worry about edit privs -- but I'll take a look this evening.
I also wanted to make sure it worked. Good opportunity to flag any issues with the exception group swap, which is the only thing I worried about at all.
If nobody gets to it first, I might do a PR to add the missing doc for those, per above.
Oh, merged 3 hours ago. Nevermind! ๐
Tests are passing, except for changelog entry ๐ I might wait till you review to add an entry so it doesn't get merged.
I took a minute during my lunch break and left an approving review. It was way simpler than I expected, having forgotten everything about that code.
Thanks! I think we are about ready for a release, then, will trigger soon.
There are no discussions or projects, should we turn them off?
I think it makes sense to turn off discussions unless someone is actively monitoring them
Another bug to fix for 26.2! ๐ https://github.com/pypa/packaging/pull/1163
Curious to see what other options we have for __reduce__ vs. __getstate__/__setstate__. I'd have gone for the latter when implementing a backward compatible fix, but storing the string and using __reduce__ is simple. Expecially if we want to throw this on some other classes (maybe without the back-compat shim if not requested?)
My only concern is string is slow, if the code isn't complex I'd rather serialize via parts
Proposed a version that's pretty significantly faster than either previous or string versions.
Smaller than the old version too (not as small as string, of course)
I've skimmed over it, looks great to me, won't be able to sit down and give a serious review until late this evening
Do you want to give it a serious review, or do you want me to approve and merge? The PR author might go on to apply this to other classes if it's merged. ๐
I'm happy with you approving and merging
I've added the other core classes that someone might pickle (including me, I pickle to save time reloading when doing my analysis on packages) in three more PRs. Mostly just used a model to apply the same idea in the Version PR, along with instructions for generating the test data. Minor touchup on the logic being a bit too defensive. The last three versions (25.0, 26.0, 26.1) are supported - since movement has been pretty slow for internal changes before 25.0, versions before that probably work, but not tested or guaranteed. Eventually we could drop support for loading pickles before 26.2 - after 26.2 the format should be stable.
It's best to do this now, so we don't end up with too many versions to handle. After that I think we are ready for 26.2. (There's also the ExceptionGroup reexport PR).
I wonder if Metadata should have a way to go back to a RawMetadata (or if it should have some other way to turn a Metadata into a dict).
PyPI merged the pyemscripten support. We should probably move forward. Are others fine with my pickle-safe PRs (especially the Specifier one, since Requirements/Markers depends on it), and the re-export of ExceptionGroup?
(Note: pyodide is going to also set the incorrect parameter to make packaging 26.1 work)
Last two PRs (though I'll probably propose a documentation update too, with a note about pickling. I think we should only ensure 26.2+ pickles are backward compatible, with a potentially temporary support for 25+. Before 25+ is unsupported, but might work.). @mental locust, do you think you could review? BTW, it's intended to be much cleaner when we can use pattern matching. ๐
I'll try and do a fly by review tonight
Thanks!
I'm out sick, but if you ping me directly I can approve any releases if necessary (I'm on the mend, but I'm not sitting on Discord either)
@teal pivot Sorry to hear that! https://github.com/pypa/packaging/actions/runs/24908029589 is awaiting approval. ๐
Approved!
And I happened to have taken a phone call, so I was sitting on Discord still ๐
Nothing seems to have broken down yet. ๐ I'll merge the Python 3.8 drop on Monday if there are no objections.
@slow pagoda Would you mind making a 26.1 and 26.2 announcement on DPO when you have a chance?
Also, I've been thinking about Paul's concern about introducing a public facing VersionRange object and I think I have a good solution, it won't be publicly constructable, it will just be a type that Specifier and SpecifierSet returns from a method like to_range, so it will be clear it is a specifier derivative
ah, only the major version is calendar-bound for packaging, yes?
Yes (deleted previous message I thought I was in the pip channel for a second)
Done!
I'm trying to add a pylock.toml to CPython's docs builds, and getting this error from pip:
WARNING: Using pylock.toml as a requirements source is an experimental feature. It may be removed/changed in a future release without prior warning.
ERROR: Cannot select requirements from pylock file 'pylock.toml': python_full_version '3.15.0a8+' in provided environment does not satisfy the Python version requirement '>=3.12'
https://github.com/python/cpython/actions/runs/25007535303/job/73234353237?pr=149058
looks like it's coming from src/packaging/pylock.py, and it's because CPython's dev builds are versioned like 3.15.0a8+ which isn't strictly a PEP 440 version
should the version check be less strict? other pip installs on dev CPython works
Ah, this hits the fun situation that CPython does not follow PEP 440 Version semantics, please open an issue on pip with a reproducer
ok, to pip or packaging?
Not sure, probably packaging, maybe pip
I would blame CPython for using non-PEP440 versions /hj
CPython's dev version probably predates 440, so it's the PEP's fault ๐
packaging.
oh, literally just submitted https://github.com/pypa/pip/issues/13957 ๐
I can re-open at packaging
Tried it 3 years ago ๐: https://github.com/python/cpython/issues/99968
clearly the solution is to just subclass PEP440Version, CPythonVersion, LegacyVersion...
yeah, I remember that we had such issue in Poetry
time to commit a hostile takeover of PEP 441
Do we need a release? Or could the patch just also be applied to pip (infuriating every distribution that un-vendors pip in the process... Okay, no). Or is it just something that should get in three months? (Basically, it is a pip 26.2 target or 26.1.1 target?)
It's @solar wind call on whether to do a follow up pip release for this or not
I personally don't feel that alone warrants a pip bugfix release. But if people think this is important and you agree to do a packaging release just for that, I don't mind doing a pip 26.1.1.
this likely only affects people using dev versions of CPython to install lockfiles, and we have a workaround of manually removing requires-python from the lockfile, so I agree it probably doesn't need a bugfix release just for this
I've almost got this version range implementation and public API ready, performance is going to be good, but it was way more work than I expected to really ensure the public API acts as expected
Fun fact, there is no way with PEP 440 version specifiers to express you just want the semantic PEP 440 version 1.0, as ==1.0 includes 1.0+local and ===1.0 excludes 1.0.0.
You could use Version("1.0") to represent that (that's what Poetry does in it's own stack) but that's not equivalent to a range and creates a lot of complexity when try to do algebraic operations.
My current solution is that while in general you will create version ranges from Specifier/Sets, there will be a class method to represent this specific point-like range of only being equal to 1.0.
Also, local versions are orderable, so the specifier ==1.0,<1.0+a would solve this, but using local parts on the version specifier for anything other than ==, !=, or === is syntactically illegal
It seems rather annoying that the existing PEP has these kinds of problems, and now we apparently don't want to fix it, or rather even if we "fixed" it by standard policy we would have to allow many years for everyone to get on board.
Yeah, it's not ideal
Would you have a recommended fix for the PEP, if you could make one?
If I could go back in time and change the spec I would say that version specifiers are the natural specifiers on the version, e.g. ==1.0 is the same as V == Version("1.0"), and that all pre-release/post-release/local special behaviour are constraints that tools MAY or SHOULD apply
Having things like Version("1.0a1") < Version("1.0b1") < Version("1.0") but <1.0 matches none of those versions, but <1.0b1 matches Version("1.0a1") is what makes the whole thing incomprehensible to outsiders.
If we'd instead said that <1.0 is the same as V < Version("1.0") but tools should not provide pre-releases by default unless the user explicitly tells the tool that pre-releases should be allowed, or the tool MAY opt into pre-releases if there is no other version that matches the specifier.
Then the mathematics of versions and specifiers would be trivial to reason about, and the guards on pre-releases/sem-ver etc. would be the job of resolvers and installers, not the version spec.
== local versions were meant for downstream users to provide patches over a released version, if == 1.0 didnโt match them, theyโd not be able to do their job (itโs possible their job is bad though!)
Without understanding the use case fully if I could change time I would probably move locals out of the version spec and make them a distribution specific value, like build numbers, but I could be missing something important
If I recall correctly, part of the reason for the pre-release behavior was to make it so less things that used to work, continued to work. Probably we shouldnโt have done that, but I think at the time it was a blocker for getting agreement lol
Yea moving it out of the version probably would have worked tbh!
No doubt, hindsight vs. at the time consensus lead to very different conclusions
I donโt think it occurred to me to do that. I was very mad at the time at dealing with downstream patches and the fact that โ1.0โ meant very different things depending on where you got โ1.0โ from
(To be clear, I have no ego attached to them, so it doesnโt bother me thereโs bad parts ๐ Iโm just happy the bad parts arenโt blocking improvements)
From my work today I've only found one actual breaking of invariants required for PubGrub style resolver , that is ==={non PEP 440 version} explicitly break De Morgan's law when you try and take it's compliment, I'm just going to leave it as an "oh well, things might break when if ==={non PEP 440 version} is involved in backtracking"
It probably wouldn't be too bad to just unsupport that particular thing
it was just there pretty much to support cases where old versions failed to parse completely
that's pretty nice that that's the only issue so far thoug
A concrete example would probably be useful for documentation purposes
Finally happy with my Version Range branches, PRs available:
https://github.com/pypa/packaging/pull/1120
https://github.com/pypa/packaging/pull/1182
This allows set algebra to be done directly with PEP 440 semantics preserved.
On building a universal locker it turns out it's more or less required to have some minimal form of set algebra for markers.
Or, at least, you need to be able to tell if two markers are disjoint under certain conditions, you can effectively hack around it with regexes because the specific scenario doesn't turn up too often, but I guess I'm going to have to more deeply understand what's being proposed in the marker API and what we could do better here