`decorators.pyi` interface file?
Background
In rich-click 1.9.0.dev1, I added a decorators.pyi (link) that enables typed args and-- critically for the developer experience-- tab completion for IDEs.
So in rich-click, @click.option() (and other decorators) still take in arbitrary **kwargs, but as far as type-checking and the overall developer experience in a modern IDE is concerned, the experience is vastly improved:
context_settings=... is an especially opaque and benefits tremendously from a TypedDict:
The ask
I've found this to be an incredibly useful change that vastly improves the developer experience, but I also feel like it should be something in base Click? It feels odd to add this additional functionality that's not in base click and is orthogonal to the purpose of rich-click, and yet it still feels like a necessity to have for modern development.
I'm posting this here because I am wondering if you are interested in a contribution to this effect. I don't see any open issues relating to this. I would be willing to contribute this, especially since I've done a lot of this work already.
Additional considerations
- I think it is both a rare use case, and a violation of the Liskov substitution principle, to create a subclass of
Command,Option, etc. that does not accept all the parent class's args/kwargs. Additionally, if a user does this, then worst case scenario is they can just ignore their IDE's auto-completion, and the static type-checker also shouldn't get upset by it either. context_settingstakes in an arbitrary dict, but in rich-click'sdecorators.pyi, we use aTypedDict. I don't think it's common for developers to subclassContext, but it's certainly possible, andContextwill often take in additional kwargs. Thus, if not handled properly, aTypedDictcould cause a static type checker to fail when it shouldn't. Additionally, PEP 728 is a long way's away.- That said, there is a simple solution:
@overloadthe command and group decorators'context_settingsto take in both aTypedDictand adict[str, Any]; by placing theTypedDictas the first overload, PyCharm autocompletes correctly (this is untested with VSCode). as long ascls=Commandorcls=Nonethen the TypedDict is appropriate; otherwise for any arbitrary subclass ofCommandthen an arbitrary dict[str, Any] is the appropriate type.
- That said, there is a simple solution:
core.pywould also strongly benefit from its owncore.pyibecause of theGroup.commandandGroup.groupdecorators.
I don't want to add pyi files, from experience in Werkzeug they are too hard to maintain and keep in sync.
I do want to stop using kwargs and start typing them all out even though it will be verbose. I don't necessarily want to do that right this instant though, as there are other things going on with signatures right now that need to settle.
For subclassing and type dicts, ultimately we should be using generics that specify their component classes. But that's another thing that will have to happen a little later than now. And it's really complicated, I've tried to do it a few times in Werkzeug and Flask and realized how pervasive it becomes.
I'm pretty sure there have been requests to the typing spec for a general way to copy signatures, but they haven't gone forward. That would be a good place to work to fully solve this problem.