-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Can't resolve generic type from return type of callable passed as (default) argument? #10854
Comments
I think that your first sample ( For your second sample ( def __call__(self, n: int, /) -> _T_co: |
Thanks @erictraut! I can verify that your suggestion fixed my simple test case. Here's one closer to the real thing (note the generic in the more complex return type): from fractions import Fraction
from typing import List, Protocol, Tuple, TypeVar
_T = TypeVar("_T")
_T_co = TypeVar("_T_co", covariant=True)
class _RationalP(Protocol[_T_co]):
def __call__(self, numerator: int, denominator: int, /) -> _T_co:
...
def generate3(n: int, constructor: _RationalP[_T] = Fraction) -> List[Tuple[float, _T]]:
return [(float(i), constructor(i, 2)) for i in range(n)] # ^^^^^^^^^^^^^^^^^^**^^
# error: Need type annotation for "res3"
res3 = generate3(5) # res3 should be of type List[Tuple[float, Fraction]]
res3_list = list(res3)
print(res3_list)
assert res3_list == [
(0.0, Fraction(0, 1)),
(1.0, Fraction(1, 2)),
(2.0, Fraction(1, 1)),
(3.0, Fraction(3, 2)),
(4.0, Fraction(2, 1)),
] Runtime:
This doesn't work (at least with MyPy): # …
_PairT = Tuple[float, _T]
# …
def generate3(n: int, constructor: _RationalP[_T] = Fraction) -> List[_PairT[_T]]:
...
# … This gives a new error: # …
class _PairT(Tuple[float, _T]):
...
# …
# error: Generic tuple types not supported
def generate3(n: int, constructor: _RationalP[_T] = Fraction) -> List[_PairT[_T]]:
...
# … |
That should type check without errors. Pyright is fine with it. If you explicitly pass |
Whoo boy. from fractions import Fraction
from typing import List, Protocol, Tuple, TypeVar, overload
_T = TypeVar("_T")
_T_co = TypeVar("_T_co", covariant=True)
class _RationalP(Protocol[_T_co]):
def __call__(self, numerator: int, denominator: int, /) -> _T_co:
...
@overload
def generate4(n: int) -> List[Tuple[float, Fraction]]:
...
@overload
def generate4(n: int, constructor: _RationalP[_T]) -> List[Tuple[float, _T]]:
...
def generate4(n: int, constructor: _RationalP[_T] = Fraction) -> List[Tuple[float, _T]]:
return [(float(i), constructor(i, 2)) for i in range(n)]
res4 = generate4(5) # res4 should be of type Iterator[Tuple[float, Fraction]]
res4_list = list(res4)
print(res4_list)
assert res4_list == [
(0.0, Fraction(0, 1)),
(1.0, Fraction(1, 2)),
(2.0, Fraction(1, 1)),
(3.0, Fraction(3, 2)),
(4.0, Fraction(2, 1)),
] ☝️ That is some work-around! Thanks for all your help narrowing this down! |
Linking #3737 |
Thanks @hauntsaninja! It does look like this has overlap with #3737, and is probably even a dup, since removing the default argument works, even for the non-protocol version of my simple test: from typing import Callable, List, TypeVar
_T = TypeVar("_T")
def generate(n: int, constructor: Callable[[int], _T]) -> List[_T]:
return [constructor(i) for i in range(n)]
res = generate(5, str)
assert res == ["0", "1", "2", "3", "4"]
assert generate(5, float) == [0.0, 1.0, 2.0, 3.0, 4.0] ☝️ No errors with that one. In any event, this definitely feels like a dupe of #10504, which I just now found (via #3737). |
Somewhere between 0.993 and 1.4.1 (yes, I know that's a big range), the work-around identified in #10854 (comment) no longer works. 😞 It results in:
|
New work-around (using from fractions import Fraction
from typing import Callable, TypeVar, overload
_T = TypeVar("_T")
@overload
def generate(
n: int,
) -> list[tuple[float, Fraction]]:
pass
@overload
def generate(
n: int,
constructor: Callable[[int, int], _T],
) -> list[tuple[float, _T]]:
pass
def generate(
n: int,
constructor: Callable[[int, int], _T] | None = None,
) -> list[tuple[float, _T]]:
if constructor is None:
# This is a persistent error, but it's limited to the implementation. The interface remains usable.
constructor = Fraction # type: ignore
assert constructor is not None
return [(float(i), constructor(i, 2)) for i in range(n)]
res1 = generate(5, Fraction)
reveal_type(res1) # list[tuple[float, Fraction]]
res2 = generate(5, lambda n, d: n / d)
reveal_type(res2) # list[tuple[float, float]]
res3 = generate(5)
reveal_type(res3) # list[tuple[float, Fraction]] |
UPDATE: Probably a dup of #3737 and #10504.
What I want to do is define a function that takes a callable that returns a generic as an argument where that generic influences the return type of the function. Something like this:
I can't seem to figure out a way to get that done, though, especially when I am supplying a default argument for the callable. Here are my test cases:
Runtime:
I tried searching for a similar issue, but was unable to find what I was looking for.
The text was updated successfully, but these errors were encountered: