-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Infer type of generic class from return type of (optionally awaitable) callable passed to constructor #19143
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
Comments
As you note this is ambiguous. As a workaround overloads work. I'm not sure if there's any principled way around this. I have been thinking about a "strict coloring" mode which treats async functions as different types than non-async ones, but that would be stricter than you would like and also not everyone will enable it. Maybe mypy should special case specifically |
@A5rocks I appreciate the quick response. Naively, I'd imagine a general rule like "in case of multiple possible matches, pick the most specific one", which may be what pyright is doing (note that I haven't looked at the implementation). Special casing from typing import Sequence, TypeVar, assert_type
T = TypeVar("T")
def ensure_sequence[T](x: T | Sequence[T]) -> Sequence[T]:
if isinstance(x, Sequence):
return x
return [x]
assert_type(ensure_sequence(1), Sequence[int])
assert_type(ensure_sequence([1, 2, 3]), Sequence[int]) mypy currently says this:
Note that pyright doesn't like this either, so maybe it is special casing
|
I imagine this wouldn't do well if there's multiple possible matches with same specificity, or even something like: class A(Protocol[T]):
a: T
def f(x: T | A[T]) -> T: ... (You could imagine However it is an improvement in some cases so if we can isolate those that sounds fine. But also if we're adding special cases I would rather being specific eg only special casing Maybe a better method is tracking the number of levels above the typevar and choosing the highest one? Or maybe discarding conflicting constraints in order of the union? Neither sound very performant of course. |
@A5rocks Good point, a special case sounds reasonable then. Thanks for considering this! |
And BTW I saw you misinterpreted me in the comment for the PR: |
@A5rocks As don't think that'd work with the real T_co = TypeVar('T_co', covariant=True)
# output_type=Type or output_type=function or output_type=object.method
SimpleOutputType = TypeAliasType(
'SimpleOutputType', Union[type[T_co], Callable[..., T_co], Callable[..., Awaitable[T_co]]], type_params=(T_co,)
)
# output_type=ToolOutput(<see above>) or <see above>
SimpleOutputTypeOrMarker = TypeAliasType(
'SimpleOutputTypeOrMarker', Union[SimpleOutputType[T_co], ToolOutput[T_co]], type_params=(T_co,)
)
# output_type=<see above> or [<see above>, ...]
OutputType = TypeAliasType(
'OutputType', Union[SimpleOutputTypeOrMarker[T_co], Sequence[SimpleOutputTypeOrMarker[T_co]]], type_params=(T_co,)
) That means def func(x: str, y: int) -> str:
return f'{x} {y}'
async def coro(x: int, y: int) -> int:
return x * y
complex_output_agent = Agent(output_type=[Foo, Bar, func, coro])
assert_type(complex_output_agent, Agent[None, Foo | Bar | str | int]) Note that the use of To use overloads, I think I'd need to define some new marker class like Is there another option I'm missing? |
I've confirmed the following works in pyright and pyrefly - but not in mypy:
I want
T
to be inferred as the ultimate return type of the awaitable if an async function is passed rather than a regular one, but I suppose it's ambiguous which side of the union is the best match.It would be great to see this work in mypy, but I'm also open to suggestions to do this in a less ambiguous way!
The text was updated successfully, but these errors were encountered: