recursive_table_toml = nest_count * "key = {" + nest_count * "}"
tomllib.loads(recursive_table_toml)
+ def test_key_recursion_limit(self):
+ nest_count = tomllib._parser.MAX_KEY_PARTS - 2
+ nested_key_toml = "a." * nest_count + "a = 1"
+ tomllib.loads(nested_key_toml)
+
+ nest_count = tomllib._parser.MAX_KEY_PARTS + 2
+ nested_key_toml = "a." * nest_count + "a = 1"
+ with self.assertRaisesRegex(
+ RecursionError,
+ r"TOML key has more than the allowed [0-9]+ parts",
+ ):
+ tomllib.loads(nested_key_toml)
+
def test_types_import(self):
"""Test that `_types` module runs.
from ._types import Key, ParseFloat, Pos
+# Pathologically excessive number of parts in a key runs into quadratic
+# behavior (e.g. in Flags.is_).
+# Even if keys aren't currently parsed using recursion, they name a
+# recursive structure, so it makes sense to limit it using getrecursionlimit()
+# and RecursionError.
+MAX_KEY_PARTS: Final = sys.getrecursionlimit()
+
ASCII_CTRL: Final = frozenset(chr(i) for i in range(32)) | frozenset(chr(127))
# Neither of these sets include quotation mark or backslash. They are
pos = skip_chars(src, pos, TOML_WS)
pos, key_part = parse_key_part(src, pos)
key += (key_part,)
+ if len(key) > MAX_KEY_PARTS:
+ raise RecursionError(
+ f"TOML key has more than the allowed {MAX_KEY_PARTS} parts"
+ )
pos = skip_chars(src, pos, TOML_WS)