diff --git a/mypyc/irbuild/statement.py b/mypyc/irbuild/statement.py index 8f97fc4d0303..d60de0d90e1c 100644 --- a/mypyc/irbuild/statement.py +++ b/mypyc/irbuild/statement.py @@ -1137,7 +1137,13 @@ def transform_with( if not is_async and mgr_v.type == lock_rprimitive: transform_with_lock(builder, mgr_v, target, body, line) return - is_native = isinstance(mgr_v.type, RInstance) + # The methods may be inherited from a non-native class, and then they can't be + # called natively. + is_native = ( + isinstance(mgr_v.type, RInstance) + and mgr_v.type.class_ir.has_method(f"__{al}enter__") + and mgr_v.type.class_ir.has_method(f"__{al}exit__") + ) if is_native: value = builder.add(MethodCall(mgr_v, f"__{al}enter__", args=[], line=line)) exit_ = None diff --git a/mypyc/test-data/run-misc.test b/mypyc/test-data/run-misc.test index 6ee16e6a9e94..3f2c2457ed17 100644 --- a/mypyc/test-data/run-misc.test +++ b/mypyc/test-data/run-misc.test @@ -1148,6 +1148,8 @@ def test_redefinition() -> None: assert i == b"foo" [case testWithNative] +from base import Base, EnterBase + class DummyContext: def __init__(self): self.c = 0 @@ -1162,6 +1164,50 @@ def test_dummy_context() -> None: assert c.c == 1 assert c.c == 0 +class InheritsEnter(EnterBase): + def __init__(self) -> None: + self.log: list[str] = [] + + def __exit__(self, exc_type: object, exc_value: object, traceback: object) -> None: + self.log.append("exit") + +class InheritsExit(Base): + def __enter__(self) -> str: + return "enter" + +class InheritsBoth(Base): + pass + +def test_inherited_enter() -> None: + c = InheritsEnter() + with c as x: + assert x is c + c.log.append("body") + assert c.log == ["body", "exit"] + +def test_inherited_exit() -> None: + with InheritsExit() as x: + assert x == "enter" + with InheritsExit(): + # Base.__exit__ suppresses the exception + raise ValueError() + +def test_inherited_both() -> None: + with InheritsBoth() as x: + assert x == "base" + +[file base.py] +class EnterBase: + def __enter__(self) -> "EnterBase": + return self + +class Base: + def __enter__(self) -> str: + return "base" + + def __exit__(self, exc_type: object, exc_value: object, traceback: object) -> bool: + return True + [case testWithNativeVarArgs] class DummyContext: def __init__(self):