From c5fd3945e801a466f8f0339ac58983a4ca0e443e Mon Sep 17 00:00:00 2001 From: netliomax25-code Date: Sun, 14 Jun 2026 12:54:45 +0530 Subject: [PATCH] reject a table redefined after its parent table header --- tests/test_parser.py | 15 +++++++++++++++ tomlkit/container.py | 8 ++++++++ 2 files changed, 23 insertions(+) diff --git a/tests/test_parser.py b/tests/test_parser.py index 88232e8..15eeafe 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -112,3 +112,18 @@ def test_parser_rejects_tab_in_bare_key(content: str) -> None: parser = Parser(content) with pytest.raises(ParseError): parser.parse() + + +@pytest.mark.parametrize( + "content", + [ + "[a.b]\n[a]\n[a.b]", + "[a.b]\nx = 1\n[a]\n[a.b]\ny = 2", + "[a.b.c]\n[a]\n[a.b.c]", + "[a.b]\n[a.c]\n[a]\n[a.b]", + ], +) +def test_parser_rejects_table_redefined_after_parent(content: str) -> None: + parser = Parser(content) + with pytest.raises(ParseError): + parser.parse() diff --git a/tomlkit/container.py b/tomlkit/container.py index 3f8363d..6113a3b 100644 --- a/tomlkit/container.py +++ b/tomlkit/container.py @@ -339,6 +339,14 @@ def _validate_table_candidate(self, current: Table, candidate: Table) -> None: raise KeyAlreadyPresent(k) if k.is_dotted(): raise TOMLKitError("Redefinition of an existing table") + if isinstance(existing, Table) and isinstance(v, Table): + if not existing.is_super_table() and not v.is_super_table(): + # Both sides are concrete `[table]` definitions of the + # same name; the table is declared twice. + raise KeyAlreadyPresent(k) + # One side is still an implicit/super table, so a duplicate + # (if any) is nested deeper - keep checking the subtree. + self._validate_table_candidate(existing, v) continue if not k.is_dotted():