How do I annotate the type of __add__ (double underscore) method on a class? - python-3.x

I'm trying to annotate the __add__ method of a class so that I can add instances of it together. However, I'm unable to specify that this method takes another instance of the same type:
class Foo:
def __init__(self, v: int) -> None:
self.v = v
def __add__(self, other: Foo) -> Foo:
return Foo(self.v + other.v)
However I seem to get an error regarding Foo not yet being defined, since it is referencing Foo within one of it's own method definitions
def __add__(self, other: Foo) -> Foo:
NameError: name 'Foo' is not defined
How can I annotate this correctly so that mypy warns me whenever there is code attempting to add instances of this class to instances of other classes?

Related

Narrowing down a type from function return types [duplicate]

This question already has an answer here:
Python typing: Narrowing type from function that returns a Union
(1 answer)
Closed 9 months ago.
I have a function which might return values of two different types, e.g. Union[str, int]. The type of the returned value can be determined from the function argument. So on each call, I know which type should be returned. However, mypy complains that Union[str, int] type cannot be assigned to a stricter type, e.g. str. Is there a way to allow stricter type assignment, without completely turning type checking off (e.g. with # type: ignore comment)?
For example, if I have this function:
from typing import Union
def change_value(action: str, value: int) -> Union[str, int]
if action == "stringify":
return str(value)
return value
stringified_value: str
stringified_value = change_value("stringify", 10)
Mypy complains with this error:
Expression of type "str | int" cannot be assigned to declared type "str"
I would like to remove mypy error by somehow telling mypy, that in this case type narrowing down is always correct.
It can be done using overloads:
from typing import Literal, Union, overload
#overload
def change_value(action: Literal['stringify'], value: int) -> str: ...
#overload
def change_value(action: Literal['whatever_else'], value: int) -> int: ...
def change_value(action: str, value: int) -> Union[str, int]:
if action == "stringify":
return str(value)
return value
stringified_value: str = change_value("stringify", 10)
playground
This approach has an additional benefit: you explicitly tell that stringify and whatever_else are two only possible actions, and call like change_value('stingify', 2) will be denied by type checker (noticed the typo? If not, it would be hard to debug otherwise).

Python & MyPy - Passing On Kwargs To Complex Functions

Consider the following attempt at add adding type hints to the functions parent and child:
def parent(*, a: Type1, b: Type2):
...
def child(*, c: Type3, d: Type4, **kwargs):
parent(**kwargs)
...
MyPy complains that kwargs has the type Dict[str, Any] but that the arguments a and b require Type1 and Type2 respectively.
I understand that the solution to this error is to rewrite child in the following way:
def child(*, a: Type1, b: Type2, c: Type3, d: Type4, **kwargs):
parent(a=a, b=b)
...
However what if the argument list of parent is much longer, or there is functiongrandchild which has its own argument list and must call child. Are you required to enumerate the arguments and their types from all the downstream functions? Or is there a graceful way to handle the "per-key" typing of **kwargs?

Variadic generic type alias

I'm writing a python typing stub for use with mypy. There are a lot of functions that take callback parameters of the form Callable[[*foo], Any], where *foo represents zero or more types. I would like to be able to use a generic type alias to reduce repetition.
Generic type aliases are documented here, but I don't see how it would be possible to have a list of types as a parameter.
I know that this can be done with a concrete number of arguments:
T = TypeVar('T')
Callback0 = Callable[[], Any]
Callback1 = Callable[[T], Any]
def foo(f: Callback0): ...
def bar(f: Callback1[str]): ...
What I'd like to declare instead is something like:
def foo(f: Callback[]): ...
def bar(f: Callback[str]): ...
If it matters, the code is for Python 3.3, and I'm running mypy with Python 3.7.
What about protocol?
T = TypeVar("T")
class MyAwesomeProtocol(Protocol[T]):
def __call__(self, a: T) -> Any:
pass
def foo(f: MyAwesomeProtocol):
...
def bar(f: MyAwesomeProtocol[str]):
...

python typing: How to inherit self type (with static typing)

I want a child class to inherit its parents' methods which return self. While doing so, the type-checker (mypy) by default keeps the return type the parent class. I want it to automatically infer the child class as the return type. For simple cases I found the following code to work:
import typing
Self = typing.TypeVar("Self", bound="Foo")
class Foo:
def foo(self) -> "Foo":
return self
def bar(self: Self) -> Self:
return self
class Bar(Foo):
...
# The naive implementation reports the type as "Foo".
# This is not what I want
reveal_type(Bar().foo())
# Using a generic `Self` type seems to work for simple cases
# Revealed type is 'Bar*'
reveal_type(Bar().bar())
This "solution" breaks down if I try to use a context-manager:
import contextlib
import typing
Self = typing.TypeVar("Self")
class Foo(typing.ContextManager):
def __enter__(self: Self) -> Self:
return self
class Bar(Foo):
...
with Bar() as f:
# Revealed type is 'Bar*'
reveal_type(f)
with contextlib.ExitStack() as cx:
f2 = cx.enter_context(Bar())
# Revealed type is 'Any'
reveal_type(f2)
It works in the first case but not in the second. I think this is because I
did not specify the type parameter of typing.ContextManager. If I do though, mypy reveals both types as Any:
class Foo(typing.ContextManager[Self]):
def __enter__(self: Self) -> Self:
return self
As far as I understand, this happens because Self isn't bound to any concrete type at this point. I am kind of lost right now, I did not find any way to make it work ... Is this even possible?

MyPy doesn't allow constrained TypeVar's to be covariant? Defining a generic dict with constrained but covariant key-val types

I'm trying to define a custom generic dict, whose keys are of type T_key and values are of type T_val.
I also want to put constraints on T_key and T_val, such that T_key can only be of type A or B or their subclass.
How do I accomplish this?
from typing import TypeVar, Generic
class A: ...
class B: ...
class Asub(A): ...
class Bsub(B): ...
T_key = TypeVar('T_key', A, B, covariant=True)
T_val = TypeVar('T_val', A, B, covariant=True)
class MyDict(Generic[T_key, T_val]): ...
w: MyDict[ A, B]
x: MyDict[ A, Bsub]
y: MyDict[Asub, B]
z: MyDict[Asub, Bsub]
When I try to check this, mypy gives errors on annotations of x, y and z. Only the annotation for w works as expected.
generic.py:17: error: Value of type variable "T_val" of "MyDict" cannot be "Bsub"
generic.py:18: error: Value of type variable "T_key" of "MyDict" cannot be "Asub"
generic.py:19: error: Value of type variable "T_key" of "MyDict" cannot be "Asub"
generic.py:19: error: Value of type variable "T_val" of "MyDict" cannot be "Bsub"
I don't understand why Asub is not a valid type for T_key even with covariant=True specified.
What am I missing here?
mypy version: 0.630
That's not what covariance means. With a covariant type variable T and a generic class Foo[T], instances of Foo[Subclass] are also considered instances of Foo[Superclass]. Covariance has no effect on what types may be substituted for T.
If your B were defined as
class B(A): ...
instead of
class B: ...
, then a value of type MyDict[B, B] would be considered a valid value of type MyDict[A, A] by static type checkers due to covariance. You would still not be able to create a value of type MyDict[ASub, BSub], because the only valid values of your type variables are A and B.
The concept you're looking for is bounded type variables, using the bound keyword argument, not constrained type variables. It looks like you can specify a union as the bound, which comes as quite a surprise to me, so declaring the type variables as
T_key = TypeVar('T_key', bound=Union[A, B])
T_val = TypeVar('T_val', bound=Union[A, B])
should work.
solution:
Turns out bound can accept Unions.
from typing import TypeVar, Generic, Union
class A: ...
class B: ...
class Asub(A): ...
class Bsub(B): ...
T_key = TypeVar('T_key', bound=Union[A, B])
T_val = TypeVar('T_val', bound=Union[A, B])
class MyDict(Generic[T_key, T_val]): ...
w: MyDict[ A, B] # passes
x: MyDict[ A, Bsub] # passes
y: MyDict[Asub, B] # passes
z: MyDict[Asub, Bsub] # passes
bad: MyDict[int, int] # Type argument "builtins.int" of "MyDict" must be a subtype of "Union[generic.A, generic.B]"

Resources