From d0d9fe6df2bf5a2352de5556ba6b2188875f8ee1 Mon Sep 17 00:00:00 2001 From: Drew O'Meara <35852900+drew-512@users.noreply.github.com> Date: Sun, 8 Nov 2026 16:43:27 -0600 Subject: [PATCH] vm: use truthiness of the __exit__ result in WITH_CLEANUP WITH_CLEANUP only silenced the exception when __exit__ returned the True singleton, so a true non-bool result such as 1 let the exception propagate. Test the result with ObjectIsTrue, as CPython 3.4 does with PyObject_IsTrue, and return any error raised while doing so. --- vm/eval.go | 5 ++++- vm/tests/with.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/vm/eval.go b/vm/eval.go index 9db0fae9..912f03a9 100644 --- a/vm/eval.go +++ b/vm/eval.go @@ -872,7 +872,10 @@ func do_WITH_CLEANUP(vm *Vm, arg int32) error { wasErr := false if exc != py.None { - wasErr = res == py.True + wasErr, err = py.ObjectIsTrue(res) + if err != nil { + return err + } } if wasErr { /* There was an exception and a True return */ diff --git a/vm/tests/with.py b/vm/tests/with.py index a2ff7765..9efe087a 100644 --- a/vm/tests/with.py +++ b/vm/tests/with.py @@ -75,6 +75,50 @@ def __exit__(self, type, value, traceback): assert c.value is None assert c.traceback is None +class ExitResult(): + def __init__(self, result): + self.result = result + def __enter__(self): + pass + def __exit__(self, type, value, traceback): + return self.result + +class Falsy(): + def __bool__(self): + return False + +class Empty(): + def __len__(self): + return 0 + +class BadBool(): + def __bool__(self): + raise ZeroDivisionError("bad bool") + +doc="with silenced error: true __exit__ result" +for result in (1, "potato", [0], 0.5, object()): + with ExitResult(result): + raise ValueError("potato") + +doc="with error: false __exit__ result" +for result in (None, False, 0, "", [], 0.0, Falsy(), Empty()): + ok = False + try: + with ExitResult(result): + raise ValueError("potato") + except ValueError: + ok = True + assert ok, "ValueError not raised" + +doc="with error: __bool__ of __exit__ result raises" +ok = False +try: + with ExitResult(BadBool()): + raise ValueError("potato") +except ZeroDivisionError: + ok = True +assert ok, "ZeroDivisionError not raised" + doc="with in loop: break" c = Context() assert c.state == "__init__"