From d435a9443e8583315d10eff584c2cce0bde4b20a Mon Sep 17 00:00:00 2001 From: Ulvin Date: Sat, 1 Aug 2026 23:39:47 +0300 Subject: [PATCH] Catch RecursionError in the regex format checker re.compile raises RecursionError, not re.error, when a pattern nests deeply enough to blow the C stack, so the exception escaped the format checker instead of being turned into a FormatError. --- jsonschema/_format.py | 2 +- jsonschema/tests/test_format.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/jsonschema/_format.py b/jsonschema/_format.py index 62c0e4ee3..dd54a78af 100644 --- a/jsonschema/_format.py +++ b/jsonschema/_format.py @@ -413,7 +413,7 @@ def is_time(instance: object) -> bool: return is_datetime("1970-01-01T" + instance) -@_checks_drafts(name="regex", raises=re.error) +@_checks_drafts(name="regex", raises=(re.error, RecursionError)) def is_regex(instance: object) -> bool: if not isinstance(instance, str): return True diff --git a/jsonschema/tests/test_format.py b/jsonschema/tests/test_format.py index d829f9848..d70f3ea5b 100644 --- a/jsonschema/tests/test_format.py +++ b/jsonschema/tests/test_format.py @@ -80,6 +80,11 @@ def test_format_checkers_come_with_defaults(self): with self.assertRaises(FormatError): checker.check(instance="not-an-ipv4", format="ipv4") + def test_regex_recursion_error_is_caught(self): + checker = FormatChecker() + with self.assertRaises(FormatError): + checker.check(instance="(" * 500, format="regex") + def test_repr(self): checker = FormatChecker(formats=()) checker.checks("foo")(lambda thing: True) # pragma: no cover