Reputation: 7576
I'm trying to write a Python function that constructs a list with intercepted methods that's reasonably type safe. It intercepts the methods by subclassing the list that's passed.
from typing import Type, TypeVar, List
V = TypeVar("V")
T = TypeVar("T", bound=List[V])
def build_interceptor(list_cls: Type[T]) -> T:
class LImpl(list_cls):
def append(self, v: V) -> None:
print(v)
super().append(v)
return LImpl()
l: List[int] = build_interceptor(List[int])
l.append(10)
MyPy isn't happy with this, but the code does work.
main.py:4: error: Type variable "__main__.V" is unbound
main.py:4: note: (Hint: Use "Generic[V]" or "Protocol[V]" base class to bind "V" inside a class)
main.py:4: note: (Hint: Use "V" in function signature to bind "V" inside a function)
main.py:8: error: Variable "list_cls" is not valid as a type
main.py:8: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
main.py:8: error: Invalid base class "list_cls"
I'm not sure what the fixes are. Yes, V
is unbound, but I don't really care what it is beyond getting the right return type. I also think there's an issue with making both the list and its contents generic, but I'm not sure how to express that.
Upvotes: 0
Views: 2500
Reputation: 180
I think the problem with 'V' is that it cant be used in the context of a TypeVar, but when defining your new class:
from typing import List, Type, TypeVar
T = TypeVar("T", bound="List")
V = TypeVar("V")
def build_interceptor(list_cls: Type[T]) -> T:
class LImpl(list_cls[V]): # type: ignore
def append(self, v: V) -> None:
print(v)
super().append(v)
return LImpl()
l: List[int] = build_interceptor(List[int])
l.append(10)
This still produces 'Variable "list_cls" is not valid as a type' which is likely related to mypy. It seems to work after adding a type ignore comment.
Upvotes: 1