Add native __contains__ to Container, Table and InlineTable#487
Conversation
Container inherits __contains__ from MutableMapping, so `key in container` goes through __getitem__ -> item(), which resolves and returns the value (and constructs a NonExistentKey on every absent key) only to discard it. Resolve the key exactly as item() does (str -> SingleKey; a non-str/non-Key argument still raises TypeError) and probe _map directly. For an out-of-order table the OutOfOrderTableProxy is still built so its validation runs as before. This also speeds up __setitem__, which does `if key in self` on every assignment. Behaviour-identical: 969 tests pass (incl. toml-test); a differential `in` check over present/absent/out-of-order/dotted/non-str keys matches master exactly. ~1.17x faster on membership (drift-immune A/B). Part of python-poetry#483. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
LGTM, do you also look at |
Table and InlineTable inherit __contains__ from the MutableMapping mixin, which answers `key in table` by calling self[key] -- resolving the value through the inner container and building a NonExistentKey on every absent key -- only to discard it. Add a native AbstractTable.__contains__ (covering both Table and InlineTable) that delegates straight to the inner container, so membership is answered from the now-native Container.__contains__ while still building the OutOfOrderTableProxy for an out-of-order entry (validation unchanged).
|
Good call — yes. I've pushed it to this PR — a one-liner on def __contains__(self, key: object) -> bool:
return key in self._valueDelegating to the inner container picks up the fast
|
Summary
First of the small, self-contained, conformance-preserving performance changes discussed in #483 — now also covering
Table/InlineTableper @frostming's review question.Both
Containerand the table types (Table,InlineTable, viaAbstractTable) inherit__contains__fromMutableMapping, sokey in xgoes throughself[key]→__getitem__→item(), which resolves and returns the value — and constructs aNonExistentKeyexception on every absent key — only to discard it.Containergets a native__contains__that resolves the key exactly asitem()does (str→SingleKey; a non-str/non-Keyargument still raisesTypeError) and probesself._mapdirectly. For an out-of-order table (a tuple index) it still builds theOutOfOrderTableProxy, so its validation runs exactly as before. This also speeds upContainer.__setitem__, which doesif key in selfon every assignment.Table/InlineTableget a nativeAbstractTable.__contains__that delegates to the inner container (return key in self._value). The inherited mixin never reached the nativeContainer.__contains__(it went through__getitem__), so the table types didn't benefit transitively; delegating fixes that while still building theOutOfOrderTableProxyfor an out-of-order entry, so validation is unchanged.Behaviour — no change
Beyond the existing suite, I ran a differential
incheck (present / absent / out-of-order / dotted-string /Key-vs-str/ non-strkeys) comparing this branch againstmaster, for bothContainerand the table types: identical results, including theTypeErroron a non-string key and the out-of-order proxy path. A regression test on an out-of-order super-table is included.Benchmark
Drift-immune A/B in a single process (native
__contains__vs the inherited__getitem__-based path, interleaved so thermal drift cancels), CPython 3.11:Verification
pytest tests/(incl. thetoml-testconformance submodule): 972 passed (969 onmaster+ 3 new membership tests), no regressions.ruff check+ruff format --check: clean.mypy(1.19.1): no new errors. The single# type: ignore[arg-type](inContainer.__contains__) mirrors the existingSingleKey(key)call initem().__contains__is killed, and an equivalent mutant correctly survives.Context: this is harness-assisted work — every change is individually benchmarked and gated (full test suite incl. conformance, a differential guard, ruff + mypy) before it can land, and I review and stand behind each PR personally. Happy to slice the rest of #483 into further small PRs — e.g. the same native
__contains__forOutOfOrderTableProxy(a separate base class), or the bulk char-scanning / header-cache work.