You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Make any callable compatible with (*args: Any, **kwargs: Any)? #5876
Perhaps a callable that accepts arbitrary arguments by having (*args: Any, **kwargs: Any) could be treated similarly to Callable[..., X] (with explicit ...), i.e. a callable with arbitrary arguments would be compatible with it.
The reason for this is that there's no easy way to annotate a function so that the type would be Callable[..., X], even though this seems to be a somewhat common need. Users sometimes assume that using (*args: Any, **kwargs: Any) will work for this purpose and are confused when it doesn't. python/typing#264 (comment) is a recent example.
Example:
fromtypingimportAnyclassA:
# A method that accepts unspecified arguments and returns intdeff(self, *args: Any, **kwargs: Any) ->int: passclassB(A):
deff(self, x: int) ->int: pass# Should be okay
We could perhaps extend this to handle decorators as well:
fromtypingimportAny, Callable, TypeVarT=TypeVar('T', bound=Callable[..., Any])
defdeco(f: T) ->T:
defwrapper(*args: Any, **kwargs: Any) ->Any:
print('called')
returnf(*args, **kwargs)
returnwrapper# Maybe this should be okay
Reacted by Ryan Morshead, aliaksandr-master, Nick Sweeting, Adrian Seyboldt, Leo Leppänen, Leandro Lima, nwalsh1995, Dale Sedivec, David Roeca, Mateusz Kurowski and 77 more
Reacted by misakar, Ailrk, Ryan Morshead, Aaron Gokaslan and Sylvan LE DEUNFF
Reacted by misakar, Ryan Morshead, Aaron Gokaslan, baterflyrity, Lucas Menicucci, Sylvan LE DEUNFF, Maddy Guthridge and Danny Nemer
Reacted by misakar, Gal Spiegler, Aaron Gokaslan, Julian Mehnle, lironzizva, baterflyrity and Sylvan LE DEUNFF
I actually like this idea, I have seen this confusion several times, and although it is a bit unsafe, most of the time when people write (*args, **kwargs) it means "don't care", rather than "should work for all calls".
Reacted by Anton Agestam, Max Kühn, Matthew Gamble and kevinphs
Just throwing this out here, but maybe a variant of Any could be useful for defining callback signatures:
...*args: AnyOrMissing, **kwargs: AnyOrMissing (sorry for the poor choice of name)
Or maybe the call could be decorated with something like @OptionalVarArgs to indicate that we really don't care about args/kwargs? Or the other way around, @PartialSignature to indicate that the method may include additional arguments, then the protocol signature only lists the required arguments (it doesn't have to include args/kwargs anymore).
Another similar (but different) use case would be to require the method to include a specific kwarg (or **kwargs), regardless of its position. I feel these will be hard to implement without the help of some decorators?
Reacted by Leah Einhorn, Leo Leppänen, Anton Agestam, Kevin L., Tim Savage, Steve Dignam, mo, Lucas Menicucci, Rafal Wojdyla and Matthew Gamble
Mypy recognizes a special form Callable[..., T] (with a literal ...) which can be used in less typical cases. It is compatible with arbitrary callable objects that return a type compatible with T, independent of the number, types or kinds of arguments. Mypy lets you call such callable values with arbitrary arguments, without any checking – in this respect they are treated similar to a (*args: Any, **kwargs: Any) function signature.
Does it mean this issue has been resolved? Correct me if I am wrong.
@ckyogg: no, the issue here is to be able to enforce partial signatures to improve duck typing possibilities. Your excerpt is asking for "any callback whatsoever" but i want to define "a callback with a quit flag as first argument and anything else after"
Reacted by Kevin L., Roni Choudhury, Lucas Menicucci, Rafal Wojdyla, Ricardo Madriz, Matthew Gamble and Max Kühn
Has anyone found a good solution for this? My usecase is exactly the same as @jonapich I want to enforce the first argument and allow anything afterwards.
Reacted by Kevin L., Conchylicultor, Dylan Modesitt, Hanusz Leszek, Roni Choudhury, Julian Mehnle, Danny Nemer, Achille Huet, Leah Einhorn, Ruben Di Battista and 3 more
+1. It seems like the only solution right now is to create a union type alias covering all cases, which is obviously something I'd like to avoid doing!
Another common real-world example is trying to create a type for a functional view in Django, where the first argument is always a Request and there might be more arguments for URL parameters.
Reacted by Ryan Morshead, Nick Sweeting, Kevin L., Iftah, autosquash, Dylan Modesitt, Alexander Carrasquillo, James Adams, Roni Choudhury, Michael Hadam and 10 more
I really just care about typing a function (in a prototype) with arbitrary arguments (except for perhaps self), and def foo(self, *args: Any, **kwargs: Any) -> Any: ... just doesn't work. What am I to do?
It's still an issue for subclasses, which is the main thing in the original post. This is one of mypy's most reacted-to open issues, so definitely some real usability issues here.
It's still an issue for subclasses, which is the main thing in the original post.
Are there good examples of when a base class would want to expose a member that accepts arbitrary parameters, but has a derived class that doesn't?
This is one of mypy's most reacted-to open issues, so definitely some real usability issues here.
Some of that reaction may have preceded the addition of ParamSpec, and may be focused on the decorator example, which is extremely important. There's no way to gauge how many people really want base methods with arbitrary parameters that are constrained in derived classes. To my eyes, that is a bad pattern that should be avoided.
Check out some of the issues that link to here. Yes, this is obviously unsafe if the parent actually accepts everything, which is why this hasn't been done yet. The change itself is trivial, see #11203
@hauntsaninja Yeah, I looked at a few. I think MyPy is correct. These are LSP violations and indicative of poor code. It would be better in my opinion to just expand the documentation to explain workarounds. For example, in #9174, the methods do_something on each class should simply have three different names. There was no reason to give them the same name since they can't be used polymorphically.
Reacted by Anton Agestam
Reacted by Nick Sweeting, Matthew Gamble and Andy Barcia Rodríguez
Since this has been merged and I got a question about it already, if your parent class actually accepts everything, use object instead of Any to ensure you get LSP errors (*args: object, **kwargs: object)
I've got the same issue here, currently writing an interface that accepts a callback that has at least one specific parameter, the others are optional and can be handed to a registering function beforehand. I thus defined my protocol as follows:
from typing import Any
from typing_extensions import Protocol
class Callback(Protocol):
def __call__(self, gain: bool, *args: Any, **kwargs: Any) -> None:
...
But now, every function passed with type Callback needs to explicitly have the structure
def func(param: bool, *args: Any, **kwargs: Any)
although a function
def func(param: bool)
is totally valid as long as no additional arguments were supplied to the registering function before. Is there any solution to this problem yet?
How did you end up resolving this issue? The PR appears to have only solved the case when the entirety of your function signature is args/kwargs, not when you have a partial signature as your example is.
Here is an example of code I am attempting to annotate, with no success. I was able to use ParamSpec/Concatenate to annotate this with pyright, but not with MyPy. But either one should be able to handle Protocol here:
For your problem, is it possible to use ParamSpec?
The pull request that closed this issue was really about providing some notation for Callable[..., Any]. (Personally, the purist in me wishes a decorator had been required in addition to the function accepting args/kwargs.)
In more general cases where you want to have a generic method that accepts some required parameters, it may be better to choose a design that doesn't have LSP errors, like:
And then you can simply derive from PreFuncExtra in your derived classes to add data members. Your derive classes should all accept PreFuncExtra, but assert that the correct subtype has been passed in.
Even if you were able to use the arbitrary args/kwargs solution that you want, you would have to assert on types, and you would end up asserting on many more types rather than a single instance of PreFuncExtra.
For your problem, is it possible to use ParamSpec?
It certainly is possible - as I noted above, using ParamSpec and Concatenate to do this works as expected in Pyright. It does not work in MyPy. Does MyPy have robust ParamSpec/Concatenate support? It was difficult to ascertain this from the various issues/PRs in this repo.
Making a separate sub-class annotation for every variant of arguments is not really a practical solution for me. This is a decorator that needs to be usable by anyone, anywhere, as long as their function accepts an int first argument and returns a sub-class of A. All other arguments to the decorated function are passed down verbatim. My actual code uses totally different types and has a real purpose behind it, the example I included above was just a minimal test case that paraphrased the issue.
Activity
ilevkivskyi commentedon Nov 8, 2018
I actually like this idea, I have seen this confusion several times, and although it is a bit unsafe, most of the time when people write
(*args, **kwargs)it means "don't care", rather than "should work for all calls".gvanrossum commentedon Nov 10, 2018
jonapich commentedon Jan 9, 2019
I just stumbled upon this trying to build a Protocol to require a callable that takes an Event as the first parameter:
Just throwing this out here, but maybe a variant of Any could be useful for defining callback signatures:
...*args: AnyOrMissing, **kwargs: AnyOrMissing(sorry for the poor choice of name)Or maybe the call could be decorated with something like
@OptionalVarArgsto indicate that we really don't care about args/kwargs? Or the other way around,@PartialSignatureto indicate that the method may include additional arguments, then the protocol signature only lists the required arguments (it doesn't have to include args/kwargs anymore).Another similar (but different) use case would be to require the method to include a specific kwarg (or **kwargs), regardless of its position. I feel these will be hard to implement without the help of some decorators?
ckyoog commentedon Feb 5, 2019
I do read the following words in mypy docs
Does it mean this issue has been resolved? Correct me if I am wrong.
jonapich commentedon Feb 7, 2019
@ckyogg: no, the issue here is to be able to enforce partial signatures to improve duck typing possibilities. Your excerpt is asking for "any callback whatsoever" but i want to define "a callback with a quit flag as first argument and anything else after"
jlaine commentedon May 25, 2019
Has anyone found a good solution for this? My usecase is exactly the same as @jonapich I want to enforce the first argument and allow anything afterwards.
chadrik commentedon Aug 12, 2019
+1. It seems like the only solution right now is to create a union type alias covering all cases, which is obviously something I'd like to avoid doing!
antonagestam commentedon Dec 6, 2019
Another common real-world example is trying to create a type for a functional view in Django, where the first argument is always a
Requestand there might be more arguments for URL parameters.rmorshea commentedon Jan 8, 2020
@antonagestam perhaps #8263 could help resolve this issue?
jmehnle commentedon Oct 9, 2020
I really just care about typing a function (in a prototype) with arbitrary arguments (except for perhaps
self), anddef foo(self, *args: Any, **kwargs: Any) -> Any: ...just doesn't work. What am I to do?JelleZijlstra commentedon Oct 9, 2020
@jmehnle does something like
foo: Callable[..., Any]work?rmorshea commentedon Oct 9, 2020
Tip for achieving this at present:
Try it: https://mypy-play.net/?mypy=latest&python=3.8&gist=ba254920ee62608c5696c29610b2a379
9 remaining items
NeilGirdhar commentedon Jun 16, 2022
Doesn't the recently-added
ParamSpeccover the decorator case in the original issue? Is there still a significant need for this?hauntsaninja commentedon Jun 16, 2022
It's still an issue for subclasses, which is the main thing in the original post. This is one of mypy's most reacted-to open issues, so definitely some real usability issues here.
NeilGirdhar commentedon Jun 16, 2022
Are there good examples of when a base class would want to expose a member that accepts arbitrary parameters, but has a derived class that doesn't?
Some of that reaction may have preceded the addition of
ParamSpec, and may be focused on the decorator example, which is extremely important. There's no way to gauge how many people really want base methods with arbitrary parameters that are constrained in derived classes. To my eyes, that is a bad pattern that should be avoided.hauntsaninja commentedon Jun 17, 2022
Check out some of the issues that link to here. Yes, this is obviously unsafe if the parent actually accepts everything, which is why this hasn't been done yet. The change itself is trivial, see #11203
NeilGirdhar commentedon Jun 17, 2022
@hauntsaninja Yeah, I looked at a few. I think MyPy is correct. These are LSP violations and indicative of poor code. It would be better in my opinion to just expand the documentation to explain workarounds. For example, in #9174, the methods
do_somethingon each class should simply have three different names. There was no reason to give them the same name since they can't be used polymorphically.Make any callable compatible with (*args: Any, **kwargs: Any) (#11203)
hauntsaninja commentedon Sep 22, 2022
Since this has been merged and I got a question about it already, if your parent class actually accepts everything, use
objectinstead ofAnyto ensure you get LSP errors (*args: object, **kwargs: object)gandhis1 commentedon Oct 6, 2022
How did you end up resolving this issue? The PR appears to have only solved the case when the entirety of your function signature is
args/kwargs, not when you have a partial signature as your example is.Here is an example of code I am attempting to annotate, with no success. I was able to use
ParamSpec/Concatenateto annotate this with pyright, but not with MyPy. But either one should be able to handleProtocolhere:NeilGirdhar commentedon Oct 6, 2022
For your problem, is it possible to use
ParamSpec?The pull request that closed this issue was really about providing some notation for
Callable[..., Any]. (Personally, the purist in me wishes a decorator had been required in addition to the function accepting args/kwargs.)In more general cases where you want to have a generic method that accepts some required parameters, it may be better to choose a design that doesn't have LSP errors, like:
And then you can simply derive from PreFuncExtra in your derived classes to add data members. Your derive classes should all accept
PreFuncExtra, but assert that the correct subtype has been passed in.Even if you were able to use the arbitrary args/kwargs solution that you want, you would have to assert on types, and you would end up asserting on many more types rather than a single instance of PreFuncExtra.
gandhis1 commentedon Oct 6, 2022
It certainly is possible - as I noted above, using
ParamSpecandConcatenateto do this works as expected in Pyright. It does not work in MyPy. Does MyPy have robustParamSpec/Concatenatesupport? It was difficult to ascertain this from the various issues/PRs in this repo.Making a separate sub-class annotation for every variant of arguments is not really a practical solution for me. This is a decorator that needs to be usable by anyone, anywhere, as long as their function accepts an
intfirst argument and returns a sub-class ofA. All other arguments to the decorated function are passed down verbatim. My actual code uses totally different types and has a real purpose behind it, the example I included above was just a minimal test case that paraphrased the issue.NeilGirdhar commentedon Oct 6, 2022
In the case of annotating a decorator, you should use
ParamSpec. I think MyPy support is advancing quickly, but it might not be complete yet. You can track its progress using its topic: https://github.com/python/mypy/issues?q=is%3Aissue+is%3Aopen+label%3Atopic-paramspec+Using
PreFuncExtrais just a design pattern to avoid LSP errors and the accompanying design problems in your own class hierarchy.Rework typing of the 'Step' __call__ method