How to annotate a function that returns a callable that should follow a generic Protocol
?
#1753
-
Say I have a generic protocol What should be Python 3.12, mypy 1.10.0: from enum import IntEnum, auto
from typing import Any, NamedTuple, Protocol, TypeVar
T_contra = TypeVar("T_contra", int, str, contravariant=True)
class Fn(Protocol[T_contra]):
def __call__(self, x: T_contra) -> bool: ...
def fn_int(x: int) -> bool:
return True
def fn_str(x: str) -> bool:
return True
def fn_none(x: None) -> bool:
return True
class FnOption(IntEnum):
INT = auto()
STR = auto()
class Config(NamedTuple):
fn_option: FnOption
def get_fn(config: Config) -> Fn[Any]: # How to type this properly and concisely?
match config.fn_option:
case FnOption.INT:
return fn_int
case FnOption.STR:
return fn_none # Should fail (fn_str would be correct)
case _:
raise AssertionError("Unsupported.")
x = "hi"
fn = get_fn(config=Config(fn_option=FnOption.STR))
y = fn(x=x) # Should fail the argument's check If I replace
Which indicates I should probably use Finally, I'd like to avoid repeating all possible types from |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 1 reply
-
P.s. GPT-4 doesn't know either |
Beta Was this translation helpful? Give feedback.
-
Generics aren't capable of following all the internal logic of This more general mapping from argument types to return type is exactly what overloads are for, so you could use overloads here if there were an argument of type Unfortunately you introduce a second level of indirection by nesting the If you're free to adjust the runtime code, you have more options, but it's hard to recommend a specific one without knowing more about your constraints. If you could make |
Beta Was this translation helpful? Give feedback.
Generics aren't capable of following all the internal logic of
get_fn
, they just match types in the signature. So in order to do this with generics, you would needT_contra
itself to appear directly as the type (or part of the type) of an argument toget_fn
. You won't be able to use generics to map from anFnOption
value to the correspondingT_contra
type argument.This more general mapping from argument types to return type is exactly what overloads are for, so you could use overloads here if there were an argument of type
FnOption
toget_fn
(and if all call-sites calledget_fn
with literal values ofFnOption
)Unfortunately you introduce a second level of indirection by nesting the
FnOption