From a519f1e5c56987fd4b2907a3e893a469f0967dc6 Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Tue, 7 Nov 2023 13:14:26 +0800 Subject: [PATCH] fix: Significant slowdown on nested tables depending on syntax Fixes #193 Signed-off-by: Frost Ming --- tomlkit/container.py | 43 ++++++++++++++++++++++--------------------- tomlkit/items.py | 2 +- tomlkit/parser.py | 2 +- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/tomlkit/container.py b/tomlkit/container.py index 917a255..9251a98 100644 --- a/tomlkit/container.py +++ b/tomlkit/container.py @@ -147,7 +147,19 @@ def _get_last_index_before_table(self) -> int: last_index = i return last_index + 1 - def append(self, key: Key | str | None, item: Item) -> Container: + def _validate_out_of_order_table(self, key: SingleKey | None = None) -> None: + if key is None: + for k in self._map: + assert k is not None + self._validate_out_of_order_table(k) + return + if key not in self._map or not isinstance(self._map[key], tuple): + return + OutOfOrderTableProxy(self, self._map[key]) + + def append( + self, key: Key | str | None, item: Item, validate: bool = True + ) -> Container: """Similar to :meth:`add` but both key and value must be given.""" if not isinstance(key, Key) and key is not None: key = SingleKey(key) @@ -229,8 +241,8 @@ def append(self, key: Key | str | None, item: Item) -> Container: else: self._raw_append(key, item) - # Building a temporary proxy to check for errors - OutOfOrderTableProxy(self, self._map[key]) + if validate: + self._validate_out_of_order_table(key) return self @@ -288,7 +300,7 @@ def append(self, key: Key | str | None, item: Item) -> Container: self._raw_append(key, item) return self - def _raw_append(self, key: Key, item: Item) -> None: + def _raw_append(self, key: Key | None, item: Item) -> None: if key in self._map: current_idx = self._map[key] if not isinstance(current_idx, tuple): @@ -299,7 +311,7 @@ def _raw_append(self, key: Key, item: Item) -> None: raise KeyAlreadyPresent(key) self._map[key] = current_idx + (len(self._body),) - else: + elif key is not None: self._map[key] = len(self._body) self._body.append((key, item)) @@ -607,21 +619,8 @@ def __iter__(self) -> Iterator[str]: # Dictionary methods def __getitem__(self, key: Key | str) -> Item | Container: - if not isinstance(key, Key): - key = SingleKey(key) - - idx = self._map.get(key) - if idx is None: - raise NonExistentKey(key) - - if isinstance(idx, tuple): - # The item we are getting is an out of order table - # so we need a proxy to retrieve the proper objects - # from the parent container - return OutOfOrderTableProxy(self, idx) - - item = self._body[idx][1] - if item.is_boolean(): + item = self.item(key) + if isinstance(item, Item) and item.is_boolean(): return item.value return item @@ -800,11 +799,13 @@ def __init__(self, container: Container, indices: tuple[int]) -> None: self._tables.append(item) table_idx = len(self._tables) - 1 for k, v in item.value.body: - self._internal_container.append(k, v) + self._internal_container.append(k, v, validate=False) self._tables_map[k] = table_idx if k is not None: dict.__setitem__(self, k.key, v) + self._internal_container._validate_out_of_order_table() + def unwrap(self) -> str: return self._internal_container.unwrap() diff --git a/tomlkit/items.py b/tomlkit/items.py index af12ad5..c7396e5 100644 --- a/tomlkit/items.py +++ b/tomlkit/items.py @@ -1617,7 +1617,7 @@ def raw_append(self, key: Key | str | None, _item: Any) -> Table: if not isinstance(_item, Item): _item = item(_item) - self._value.append(key, _item) + self._value.append(key, _item, validate=False) if isinstance(key, Key): key = next(iter(key)).key diff --git a/tomlkit/parser.py b/tomlkit/parser.py index bdf0c4a..89ddae2 100644 --- a/tomlkit/parser.py +++ b/tomlkit/parser.py @@ -1030,7 +1030,7 @@ def _parse_table( InternalParserError, "_parse_item() returned None on a non-bracket character.", ) - + table.value._validate_out_of_order_table() if isinstance(result, Null): result = table