gh-144812: Add public argparse group typing protocols#150597
gh-144812: Add public argparse group typing protocols#150597gaborbernat wants to merge 1 commit into
Conversation
Documentation build overview
|
ArgumentParser.add_argument_group() and add_mutually_exclusive_group() return objects whose only public name was the private _ArgumentGroup and _MutuallyExclusiveGroup. Code that stores or passes these objects had no public type to annotate against, forcing callers to reference the underscore-prefixed implementation classes. Expose them as structural protocols rather than aliasing the concrete classes, so the implementation stays private and free to evolve while callers get a stable contract to annotate against. The protocols are built on first attribute access through the module __getattr__, keeping typing off the import path: argparse sits on the start-up path of most CLIs and does not otherwise import typing.
22dfcb6 to
6ab9015
Compare
|
Please, the issue has not reached a consensus so a PR is still premature. My concern is that once we expose the protocol publicly, we also can't change the implementation without deprecations (probably?) I think a 3rd party library would be better in that case. In the same idea as typing_extensions. Or alternatively, we should follow pathlib.types's idea though I'm not sure it's really the best for all modules in general. |
|
I'm a bit puzzled about the push back here. The only thing we are exposing here is the API that is already documented through the documentation. Should we decide to change that? The typing information through the protocols would be subject to the same deprecation policy as we have for our officially documented public interface on these classes. While the classes themselves are private, the methods to call on this object are documented as public. |
|
The stdlib has a history of not exposing lots of protocols, except with some exceptions in typing.py and collections.abc. Exposing a protocol is okay but as I said, it will create a friction with any changes we would do on the private interface and would also need to be synchronized with typeshed. OTOH, if we make argparse a package and add argparse.types as a module with unstable types, then it would be ok I think. Much like pathlib.types. That way, we could avoid friction with the main module and we would not need to have But again, this would still add a maintenance cost. So I would prefer that we discuss this on the issue first. I'm ok with the protocol but |
ArgumentParser.add_argument_group()andadd_mutually_exclusive_group()return objects whose only name is the private_ArgumentGroupand_MutuallyExclusiveGroup. Code that stores or passes these objects around has no public type to annotate against, so callers reach for the underscore-prefixed implementation classes. A GitHub code search turns up roughly 140 projects doing exactly that. 📦This exposes two public names,
argparse.ArgumentGroupandargparse.MutuallyExclusiveGroup, as structuraltyping.Protocols that guarantee theadd_argument()method callers rely on. Modelling them as protocols rather than aliasing the concrete classes keeps the implementation private and free to evolve, which addresses the concern raised on the issue that typing needs should not turn inner classes into frozen public API. ✨ The protocols are built on first attribute access through the module__getattr__, soimport argparsestays off thetypingimport path — relevant becauseargparsesits on the start-up path of most CLIs and does not otherwise importtyping.Type checkers read typeshed rather than
Lib/argparse.py, so these names take effect for end users oncestdlib/argparse.pyideclares the protocols and points the two return types at them. I confirmed that change makes mypy, pyright, ty, and pyrefly all resolve the public names; a matchingpython/typeshedPR will follow.