Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- Speed up parsing by removing the internal `TOMLChar` wrapper: the parser now reads plain `str` characters from `Source` and detects end-of-input positionally, avoiding a per-character object construction and method dispatch. ([#492](https://github.com/python-poetry/tomlkit/pull/492))
- Speed up parsing by comparing `StringType` members by identity (`is`) instead of building a set on every `is_basic`/`is_literal`/`is_singleline`/`is_multiline` call, avoiding millions of enum hashes while parsing. ([#502](https://github.com/python-poetry/tomlkit/pull/502))
- Speed up merging super tables by merging in place instead of deep-copying the growing target on every merge, turning the parse of documents with many subtables under a shared super table (e.g. consecutive `[a.b.c]` / `[a.b.d]` headers) from O(n²) into O(n). ([#503](https://github.com/python-poetry/tomlkit/pull/503))
- Speed up membership tests (`key in ...`) on out-of-order tables with a native `OutOfOrderTableProxy.__contains__`, completing [#483](https://github.com/python-poetry/tomlkit/issues/483) for the last mapping type that still inherited the slow `MutableMapping` mixin (which resolves the value and builds an exception on every absent key). ([#515](https://github.com/python-poetry/tomlkit/pull/515))

### Fixed

Expand Down
22 changes: 22 additions & 0 deletions tests/test_items.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from tests.util import elementary_test
from tomlkit import api
from tomlkit import parse
from tomlkit.container import OutOfOrderTableProxy
from tomlkit.exceptions import NonExistentKey
from tomlkit.items import Array
from tomlkit.items import Bool
Expand Down Expand Up @@ -1345,3 +1346,24 @@ def test_out_of_order_table_membership() -> None:
assert "b" in table
assert "missing" not in table
assert doc.as_string() == content


def test_out_of_order_table_proxy_membership() -> None:
# A top-level table split by another table (``a`` interrupted by ``foo``)
# resolves to an ``OutOfOrderTableProxy``; exercise its native
# ``__contains__`` directly (the test above goes through ``Table``).
content = "[a.x]\np = 1\n[foo]\nbar = 2\n[a.y]\nq = 3\n"
doc = parse(content)
table = doc["a"]
assert isinstance(table, OutOfOrderTableProxy)

assert "x" in table
assert "y" in table
assert "missing" not in table
# a Key is accepted just like __getitem__ does
assert Key("x") in table
# a non-str/non-Key argument is rejected like the other mapping types
with pytest.raises(TypeError):
_ = 123 in table
# membership must not resolve values or mutate the document
assert doc.as_string() == content
9 changes: 9 additions & 0 deletions tomlkit/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -954,6 +954,15 @@ def unwrap(self) -> dict[str, Any]:
def value(self) -> dict[str, Any]:
return self._internal_container.value

def __contains__(self, key: object) -> bool:
# Native membership test. The inherited ``MutableMapping.__contains__``
# resolves the value via ``__getitem__`` (and builds a ``NonExistentKey``
# on every absent key) only to discard it. Probe the internal container
# directly -- the same predicate ``__getitem__`` already uses -- which is
# itself a native ``_map`` lookup that still rebuilds the proxy for an
# out-of-order key so its validation runs exactly as before.
return key in self._internal_container

def __getitem__(self, key: Key | str) -> Any:
if key not in self._internal_container:
raise NonExistentKey(key)
Expand Down
Loading