-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Description
Describe the bug
This is easiest illustrated with the repro-case below. In short, if you decorate an invariant generic method, the type-var unification doesn't seem to propagate across the LRU wrapped method. In the following code, the store_of method works fine but cached_store_of's type is still captured as the "T@cached_store_of" fresh Ty var instead of being unified with the Account type.
Note that in this case, the type var T must be invariant but that's incidental to this example. Custom decorators seem to work properly. I also believe I've confidently rule out that this is unrelated to TypeForm and any of the improvements that might bring to the table.
Code
import typing
from functools import lru_cache
from typing import Any, Generic, TypeVar
T = TypeVar("T")
class Store(Generic[T]):
def __init__(self, entity_cls: type[T], db_session: Any):
super().__init__()
self._entity_cls = entity_cls
self._db_session = db_session
def get(self, pkey: str) -> T:
...
def create(self, value: T) -> T:
...
class DB:
def __init__(self, db_session: Any):
super().__init__()
self._db_session = db_session
def store_of(self, entity_cls: type[T]) -> Store[T]:
return Store(entity_cls=entity_cls, db_session=self._db_session)
@my_decorator
def my_decorated_store_of(self, entity_cls: type[T]) -> Store[T]:
return Store(entity_cls=entity_cls, db_session=self._db_session)
@lru_cache
def cached_store_of(self, entity_cls: type[T]) -> Store[T]:
return Store(entity_cls=entity_cls, db_session=self._db_session)
class Account:
...
typing.reveal_type(DB(db_session="connection").store_of(entity_cls=Account))
account_store_1: Store[Account] = DB(db_session="connection").store_of(
entity_cls=Account,
)
typing.reveal_type(DB(db_session="connection").my_decorated_store_of(entity_cls=Account))
account_store_2: Store[Account] = DB(db_session="connection").my_decorated_store_of(
entity_cls=Account,
)
typing.reveal_type(DB(db_session="connection").cached_store_of(entity_cls=Account))
account_store_3: Store[Account] = DB(db_session="connection").cached_store_of(
entity_cls=Account,
)Version
❯ pyright --version
pyright 1.1.356Output
❯ pyright generic_bug.py
/Users/shawn/Code/instance-bio/instance/services/web/generic_bug.py
/Users/shawn/Code/instance-bio/instance/services/web/generic_bug.py:51:20 - information: Type of "DB(db_session="connection").store_of(entity_cls=Account)" is "Store[Account]"
/Users/shawn/Code/instance-bio/instance/services/web/generic_bug.py:54:20 - information: Type of "DB(db_session="connection").my_decorated_store_of(entity_cls=Account)" is "Store[Account]"
/Users/shawn/Code/instance-bio/instance/services/web/generic_bug.py:59:20 - information: Type of "DB(db_session="connection").cached_store_of(entity_cls=Account)" is "Store[T@cached_store_of]"
/Users/shawn/Code/instance-bio/instance/services/web/generic_bug.py:60:35 - error: Expression of type "Store[T@cached_store_of]" cannot be assigned to declared type "Store[Account]"
"Store[T@cached_store_of]" is incompatible with "Store[Account]"
Type parameter "T@Store" is invariant, but "T@cached_store_of" is not the same as "Account" (reportAssignmentType)
1 error, 0 warnings, 3 informations