diff --git a/CHANGELOG.md b/CHANGELOG.md index c14e764..8c44112 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [unreleased] +### Changed + +- Speed up membership tests (`key in container`) with a native `Container.__contains__`, avoiding the inherited `MutableMapping` round-trip through `__getitem__` (which resolves the value and builds an exception on every absent key). ([#483](https://github.com/python-poetry/tomlkit/issues/483)) + ### Fixed - Fix invalid serialization with a duplicated comma when removing a non-edge element from a parsed inline table. ([#486](https://github.com/python-poetry/tomlkit/pull/486)) diff --git a/tomlkit/container.py b/tomlkit/container.py index 4387471..b6a91ca 100644 --- a/tomlkit/container.py +++ b/tomlkit/container.py @@ -714,6 +714,23 @@ def __getitem__(self, key: Key | str) -> Any: return item + def __contains__(self, key: object) -> bool: + # Native membership test. The inherited ``MutableMapping.__contains__`` + # resolves the value via ``__getitem__``/``item()`` (and builds a + # ``NonExistentKey`` on every absent key) only to discard it. Resolve the + # key the same way ``item()`` does -- ``str`` becomes a ``SingleKey`` + # (a non-str/non-``Key`` argument still raises ``TypeError``) -- then + # probe ``_map`` directly. For an out-of-order table the proxy is still + # built so its validation runs exactly as before. + if not isinstance(key, Key): + key = SingleKey(key) # type: ignore[arg-type] + idx = self._map.get(key) + if idx is None: + return False + if isinstance(idx, tuple): + OutOfOrderTableProxy(self, idx) + return True + def __setitem__(self, key: Key | str, value: Any) -> None: if key in self: old_key = next(filter(lambda k: k == key, self._map))