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
Mypy doesn't support a function that tries to return a generic callable. I wonder if mypy could make this code work properly:
from typing import TypeVar, Callable, Any
T = TypeVar('T', bound=Callable[..., Any])
def deco() -> Callable[[T], T]:
def inner(x):
return x
return inner
@deco()
def g(x: str) -> int: ...
g('') # should be fine? currently "Argument 1 to "g" has incompatible type "str"; expected None"
g(1) # should be an error
As Jukka and I discussed at PyCon, this situation where a type variable appears only in the result type of a generic callable could be handled by "floating in" the type variable to the result type: instead of
At Zulip, we're using such decorator-returning functions a lot. One of these decorator-returning functions, named cache_with_keys, is applied to dozens of functions, many of which are called hundreds of times (see zulip/zulip#1348). It'll be great to see this issue fixed since a lot of our code is not properly type checked because of this.
Reacted by Eugene Ivanchenko, Bailey Parker, Maksim Kurnikov, Mira Hedl, William Harvey, Timm Wagener, Hiroki Konishi, Vu Anh Duc, k7paul, Anton and 11 more
Reacted by Mike McDermott and Moritz Oliveira Makowski
Oooh, very clever! I can't say I personally understand how it works, and we
should still fix the original error, but this looks like a useful
workaround. Thanks!
On Sat, Oct 15, 2016 at 4:28 AM, Yegor Roganov [email protected]
wrote:
As a workaround, rewriting nested functions to a class seem to work:
from functools import wrapsfrom typing import TypeVar, Callable, Any, cast
I still have this issue (and it is fixed by the class hack) on mypy 1.14.1.
For context, numpy "ufuncs" can be applied to anything that is compatible with a certain protocol, but the type hints don't work. The xarray types DataArray and Dataset support this protocol, and e.g. np.sqrt(some_dataset) returns a Dataset, but the type hints say the result is a numpy array.
I can fix this for an individual numpy ufunc, but my attempts to make a function returning a generic function with the correct type hints lead me here.
from collections.abc import Callable
from typing import cast, TypeVar
import numpy as np
import xarray as xr
XrData = TypeVar("XrData", xr.DataArray, xr.Dataset)
XrOpType = Callable[[XrData], XrData]
# this gives the correct type hints
def xr_sqrt(data: XrData) -> XrData:
result = np.sqrt(data)
if isinstance(data, xr.DataArray):
return cast(xr.DataArray, result)
if isinstance(data, xr.Dataset):
return cast(xr.Dataset, result)
# this doesn't work
def xr_types(ufunc: np.ufunc) -> XrOpType:
def xr_typed_func(data: XrData) -> XrData:
result = ufunc(data)
if isinstance(data, xr.DataArray):
return cast(xr.DataArray, result)
if isinstance(data, xr.Dataset):
return cast(xr.Dataset, result)
return xr_type_func # reveal_type says this maps XrData to XrData
bad_xr_sqrt = xr_types(np.sqrt) # reveal_type says this maps Any to Any
# class hack works
class XrTypesHack:
def __init__(self, ufunc: np.ufunc) -> None:
self.ufunc = ufunc
def __call__(self, data: XrData) -> XrData:
result = self.ufunc(data)
if isinstance(data, xr.DataArray):
return cast(xr.DataArray, result)
if isinstance(data, xr.Dataset):
return cast(xr.Dataset, result)
xr_sqrt_hack = XrTypesHack(np.sqrt) # reveal_type(xr_sqrt_hack.__call__) says this maps XrData to XrData
Activity
rwbarton commentedon May 18, 2016
This is related to my comments at #1317, but maybe worth keeping open as a separate issue.
rwbarton commentedon Jun 13, 2016
As Jukka and I discussed at PyCon, this situation where a type variable appears only in the result type of a generic callable could be handled by "floating in" the type variable to the result type: instead of
give
decothe typeI wonder how often this arises in practice?
JukkaL commentedon Jun 13, 2016
The only case where I remember having seen this is in a decorator that is used like this:
The decorator, when called, returns a generic function
T->Tthat preserves the signature of the decorated function.sharmaeklavya2 commentedon Jul 22, 2016
At Zulip, we're using such decorator-returning functions a lot. One of these decorator-returning functions, named
cache_with_keys, is applied to dozens of functions, many of which are called hundreds of times (see zulip/zulip#1348). It'll be great to see this issue fixed since a lot of our code is not properly type checked because of this.jstasiak commentedon Aug 10, 2016
I have another example from our codebase:
Both of the decorators returned by
app.route()andrate_limit()at lest formally preserve the signature of the callable being decorated.Sadly I don't have enough knowledge to help with this myself.
gnprice commentedon Aug 11, 2016
This seems like something we'll really want to fix, but it's not immediately clear yet what the fix looks like.
roganov commentedon Oct 15, 2016
As a workaround, rewriting nested functions to a class seem to work:
gvanrossum commentedon Oct 15, 2016
Oooh, very clever! I can't say I personally understand how it works, and we
should still fix the original error, but this looks like a useful
workaround. Thanks!
On Sat, Oct 15, 2016 at 4:28 AM, Yegor Roganov [email protected]
wrote:
--Guido van Rossum (python.org/~guido)
Exclude boto from tests due to broken Liskov Substitution Principle
Revert "Make all single-constraint TypeVars to use bounds" (#806)
sixolet commentedon Mar 24, 2017
This looks fun and important. It'll be a good warm-up to all the rest of the features we need to get decorators working right. claim
Make all single-constraint TypeVars use bound= insteads (plus hack) (#…
Make TypeQuery more general, handling nonboolean queries.
Make TypeQuery more general, handling nonboolean queries.
Make TypeQuery more general, handling nonboolean queries.
Make TypeQuery more general, handling nonboolean queries.
sixolet commentedon Apr 3, 2017
I just opened a pull request #3113 that fixes this. It's still WIP, but I should link this issue in.
Make TypeQuery more general, handling nonboolean queries. (#3084)
gvanrossum commentedon Apr 21, 2017
@sixolet, Is this fixed by your #3113?
sixolet commentedon Apr 22, 2017
This is the email I meant to reply to saying yes I think it's fixed. Sorry, I was getting off a plane on 4h sleep.
gvanrossum commentedon Apr 22, 2017
NP! We've had quite a busy week. I'll tell you the stories some time.
brendan-m-murphy commentedon Feb 27, 2025
I still have this issue (and it is fixed by the class hack) on mypy 1.14.1.
For context, numpy "ufuncs" can be applied to anything that is compatible with a certain protocol, but the type hints don't work. The xarray types
DataArrayandDatasetsupport this protocol, and e.g.np.sqrt(some_dataset)returns aDataset, but the type hints say the result is a numpy array.I can fix this for an individual numpy ufunc, but my attempts to make a function returning a generic function with the correct type hints lead me here.