The new version of this PEP leaves some important pushback from @Jelle and @carljm incompletely answered.
The problem is that only actual function objects support subscription, and a decorator whose signature is
def deco(func: Callable[[int], int]) -> Callable[[int], int]: ...
does not say whether it returns a function object – only that it returns a callable object. And not all callables are subscriptable – in particular, objects with a __call__ method aren’t, unless their author also added a __getitem__ method (and if they did so before this PEP was accepted, it would surely do something unrelated).
The new version answers this by saying “yeah, we know, it’ll just be a runtime error, no big deal”. But Jelle and Carl don’t like this answer much.
A partial solution would be to make your decorator generic, like this:
def deco[F: Callable](func: F) -> F: ...
This allows the type checker to realize that if the argument is a types.FunctionType instance (which all objects created by def and lambda are), the returned object is also such an instance, and peace returns to the kingdom.
But if the return type preserves the “function-ness” but not the exact signature, or if it takes any callable and wraps it with an actual function (def), this hack won’t work.
Jelle’s proposal is to make a change to typeshed, where the actual specification for FunctionType lives, to make it generic, so you can write things like this:
def deco[**P, R](func: Callable[P, R]) -> FunctionType[P, R]:
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
print("Calling", func.__name__)
return func(*args, **kwargs)
return wrapper
(A small change to type checkers will be needed for this to work.)
We can then see FunctionType as a subtype of Callable that adds a bunch of additional dunder attributes, rather than a stand-alone object definition that happens to define __call__ (and adding __getitem__ per the new PEP version).
Obviously the same treatment should be applied to all function types supported by the PEP (FunctionType, MethodType, BuiltinFunctionType, BuiltinMethodType, and MethodWrapperType, all in types).
Thoughts? @Jelle, did I capture your idea correctly?