From f165e5694dee9de2759f359994d991260ab04250 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 22 Sep 2023 23:57:29 -0700 Subject: [PATCH] Avoid creating ref cycles By storing previously raised exceptions inside a local, this code created ref cycles that kept all locals in all calling stack frames alive. This is because exceptions hold references to their tracebacks, which hold references to the relevant frames, which holds a reference to the local errors dict that holds references to the exceptions. See https://peps.python.org/pep-0344/#open-issue-garbage-collection and https://peps.python.org/pep-3110/#rationale This breaks the cycle by deleting the local when we raise, so frames are destroyed by the normal reference counting mechanism. This fixes some resource exhaustion issues I encountered at work. --- src/typeguard/_checkers.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/typeguard/_checkers.py b/src/typeguard/_checkers.py index 34bff23..145312f 100644 --- a/src/typeguard/_checkers.py +++ b/src/typeguard/_checkers.py @@ -405,7 +405,10 @@ def check_union( formatted_errors = indent( "\n".join(f"{key}: {error}" for key, error in errors.items()), " " ) - raise TypeCheckError(f"did not match any element in the union:\n{formatted_errors}") + try: + raise TypeCheckError(f"did not match any element in the union:\n{formatted_errors}") + finally: + del errors def check_uniontype(