feat: filenames - #1113
feat: filenames#1113henryiii wants to merge 53 commits into
Conversation
8d36258 to
0e8b7b5
Compare
361d730 to
03ac2ae
Compare
|
@di, don't know how to do the "General alignment w/ error messages vs. what PyPI uses" step. |
So this was basically, packaging has one set of exception messages for when certain errors happen here, PyPI has a different, but similar set. Ideally we'd just be able to catch these errors and relay the message that originates from packaging, but they are not 1:1, so I was hoping to align them to simplify them. |
03ac2ae to
093a8c8
Compare
093a8c8 to
6625658
Compare
|
I like the idea of waiting till Variants are ready to add this, so someone using the new API is also aware of variants. #1148. |
Signed-off-by: Henry Schreiner <henryfs@princeton.edu> docs: cleanup Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Carry the validate_order parameter, InvalidTag handling, the non-empty project name checks, the tighter is_normalized_name regex, and the version notes from utils.py into filenames.py after the rebase. Assisted-by: ClaudeCode:claude-fable-5-1 ClaudeCode:claude-fable-5-1
6625658 to
2df2e53
Compare
The constructors now check the name, version, build tag, and variant label, so to_filename() can no longer produce a filename that does not parse. Non-strict from_filename still accepts legacy names. __replace__ (used by copy.replace) checks only the replaced parts. Assisted-by: ClaudeCode:claude-opus-5-5
The result was typed as NormalizedName but was not one. The filename code now replaces the hyphens itself. Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
The original strings were only partly kept, and equality already ignored them. Remove original_name and original_version, and accept a Version in the constructor and in __replace__. Assisted-by: ClaudeCode:claude-opus-5-5
The base class was only used to share code. The classmethod helpers are now module-level functions, and small helpers are inlined. Assisted-by: ClaudeCode:claude-opus-5-5
Use copy.copy in __replace__, simplify the wheel name regex, and return Self from from_filename. Merge overlapping tests into parametrized ones. Assisted-by: ClaudeCode:claude-opus-5-5
Replace the strict and validate_order parameters of from_filename with validate_wheel_filename, validate_sdist_filename, and validate_ordered_tags. Parsing is now always lenient. Assisted-by: ClaudeCode:claude-opus-5-5 ClaudeCode:claude-opus-5-5
Replace validate_ordered_tags with a distinct exception class per normalization check. validate_wheel_filename and validate_sdist_filename raise invalid filenames directly and collect normalization errors into an ExceptionGroup, so callers can select checks with except*. Assisted-by: ClaudeCode:claude-opus-5-5
Split wheel and sdist filenames once and build objects from the parts via a private classmethod. Move the no-variant policy into the splitter so parse_wheel_filename no longer reconstructs it. Share a _set helper between __init__ and __replace__, and reuse _check_name in the normalization check. Assisted-by: ClaudeCode:claude-fable-5-1
The tags argument is now required and comes before build_tag. An empty or non-compressible tag set is rejected at construction. Assisted-by: ClaudeCode:claude-opus-5-5
Assisted-by: ClaudeCode:claude-opus-5-5
Assisted-by: ClaudeCode:claude-opus-5-5
Assisted-by: ClaudeCode:claude-opus-5-5
Assisted-by: ClaudeCode:claude-opus-5-5
Assisted-by: ClaudeCode:claude-opus-5-5
Assisted-by: ClaudeCode:claude-opus-5-5
Assisted-by: ClaudeCode:claude-opus-5-5
Assisted-by: ClaudeCode:claude-opus-5-5
Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
Names that parsing accepts but that are not valid project names are now reported as NonNormalizedName in the ExceptionGroup, and not raised as InvalidFilename. Assisted-by: ClaudeCode:claude-opus-5-5
Parse, validate, and the constructor now share one build tag regex, so suffixes with spaces, newlines, or non-ASCII characters are rejected everywhere, and a non-int build number is rejected in the constructor. Add InvalidProjectName for names that parse but are not valid project names, so NonNormalizedName only covers valid names. Parse tags once in validate_wheel_filename, use type(self) in __setstate__, and document the six-part filename ambiguity rule. Assisted-by: ClaudeCode:claude-fable-5-1
copy.copy fell through to __getstate__ and __setstate__, so every __replace__ call serialized and re-parsed the filename. The instances are immutable, so __copy__ and __deepcopy__ return self, and __replace__ copies the slots directly. Assisted-by: ClaudeCode:claude-fable-5-1
Split wheel filenames with maxsplit instead of splitting and joining all parts, and return plain tuples instead of a NamedTuple. The classes wrap tuple-returning parsers, so the legacy parse_*_filename functions no longer build and unpack an object. The parsers live in utils, which removes the function-local import (about 200 ns per call) and the circular import. Assisted-by: ClaudeCode:claude-opus-5-5
…hods Parsed filenames keep the original string, so validate() checks it without parsing again. Instances that are constructed or made with __replace__ have no original filename and always pass. Pickles keep the original filename, so validation gives the same result after unpickling. Assisted-by: ClaudeCode:claude-opus-5-5
Also reuse the build_str and compressed_tags properties in validate. Assisted-by: ClaudeCode:claude-opus-5-5
Add an original_filename property to WheelFilename and SourceDistributionFilename. SourceDistributionFilename.validate() now reports a .zip extension as InvalidSdistFilename inside the ExceptionGroup, together with the other problems. Document all validate() exception classes. Assisted-by: ClaudeCode:claude-opus-5-5
Avoid the on_exit context manager, and check common normalized names, versions, and tags without re-normalizing them. Assisted-by: ClaudeCode:claude-opus-5-5
Also drop them from __match_args__. Assisted-by: ClaudeCode:claude-opus-5-5
|
This is getting stable again, dropping my design points while working on it:
Performance: 🤖 AI text below 🤖 Each value is in µs per filename, averaged over two runs.
|
This builds on #409 and #1009 to add a
packaging.filenamesmodule.Compared to #1009, this:
Filename.from_filenamemethod, which isn't usable without manual checking anyway due to not knowing which type is produced.strict=is a (required) argument of.from_filenamenow. Most logic is there.underscore=Trueoption added tocanonicalize_name, better perf than just replacing-with_afterwardsDecisions to be made or validated:
strict=True, maybe we should require sorted tag sets? (I think so, based on todo in [DRAFT] Addpackaging.filenamesAPI #1009).namethe traditionally canonical name, rather than the underscore one, I think that's best?strict=False) method checks for__for some reason. It's the only one it doesn't normalize. Maybe that should be pulled out to just the classic function?Todos:
strict=TrueCloses:
parse_sdist_filename#527parse_wheel_filenamepermits non-normalized versions. #873parse_wheel_filenamesaccepts wheel filenames with unsorted compressed tag sets #909packaging.filenamesAPI #1009AI usage disclaimer: I used copilot in VSCode to help fill out the coverage in 997c5a8, and some help with docs before that.