Skip to content

Commit 1d8ddd5

Browse files
[stubtest] Fix crash on instances with redefined __class__ (#20926)
Closes #20919 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 0177c0d commit 1d8ddd5

File tree

2 files changed

+47
-0
lines changed

2 files changed

+47
-0
lines changed

mypy/stubtest.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2049,6 +2049,10 @@ def _named_type(name: str) -> mypy.types.Instance:
20492049
return mypy.types.TupleType(items, fallback)
20502050

20512051
fallback = mypy.types.Instance(type_info, [anytype() for _ in type_info.type_vars])
2052+
if type(runtime) != runtime.__class__:
2053+
# Since `__class__` is redefined for an instance, we can't trust
2054+
# its `isinstance` checks, it can be dynamic. See #20919
2055+
return fallback
20522056

20532057
value: bool | int | str
20542058
if isinstance(runtime, enum.Enum) and isinstance(runtime.name, str):

mypy/test/teststubtest.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1564,6 +1564,49 @@ def f(): return 3
15641564
error=None,
15651565
)
15661566

1567+
@collect_cases
1568+
def test_proxy_object(self) -> Iterator[Case]:
1569+
yield Case(
1570+
stub="""
1571+
class LazyObject:
1572+
def __init__(self, func: object) -> None: ...
1573+
def __bool__(self) -> bool: ...
1574+
""",
1575+
runtime="""
1576+
class LazyObject:
1577+
def __init__(self, func):
1578+
self.__dict__["_wrapped"] = None
1579+
self.__dict__["_setupfunc"] = func
1580+
def _setup(self):
1581+
self.__dict__["_wrapped"] = self._setupfunc()
1582+
@property
1583+
def __class__(self):
1584+
if self._wrapped is None:
1585+
self._setup()
1586+
return type(self._wrapped)
1587+
def __bool__(self):
1588+
if self._wrapped is None:
1589+
self._setup()
1590+
return bool(self._wrapped)
1591+
""",
1592+
error="test_module.LazyObject.__class__",
1593+
)
1594+
yield Case(
1595+
stub="""
1596+
def default_value() -> bool: ...
1597+
1598+
DEFAULT_VALUE: bool
1599+
""",
1600+
runtime="""
1601+
def default_value():
1602+
return True
1603+
1604+
DEFAULT_VALUE = LazyObject(default_value)
1605+
bool(DEFAULT_VALUE) # evaluate the lazy object
1606+
""",
1607+
error="test_module.DEFAULT_VALUE",
1608+
)
1609+
15671610
@collect_cases
15681611
def test_all_at_runtime_not_stub(self) -> Iterator[Case]:
15691612
yield Case(

0 commit comments

Comments
 (0)