Add reversed() support to MultiDict views (C-ext + pure-Python)#1340
Add reversed() support to MultiDict views (C-ext + pure-Python)#1340aiolibsbot wants to merge 5 commits into
Conversation
|
make sure this PR closes #448 also |
|
Look for opportunities to make it more dry. Mark it as closing the above PRs as well |
|
@aiolibsbot rr |
PR Review — Add reversed() support to MultiDict views (C-ext + pure-Python)Implementation is solid on both sides. 🟢 Suggestions1. md_prev mirrors md_next cleanly — minor opportunity to share NULL-skip logic (`multidict/_multilib/hashtable.h`, L668-731)
Not a request to change anything in this PR, but if you ever revisit this file: the NULL-skip walk + identity/key/value extraction is now duplicated in 2. Hoist reverse selection onto HtKeys so each view's _iter stays a one-liner (`multidict/_multidict_py.py`, L600-602)
If you want to push @bdraco's DRY note further, you could fold the version-checked iteration into a single helper on the dict (e.g. 3. Symlink resolves @bdraco's request, but worth confirming on Windows CI (`CHANGES/1340.feature.rst`, L1)Mode
4. Minor: `next(iter(it))` is equivalent to `next(it)` for iterator objects (`tests/test_mutable_multidict.py`, L165-188)
Leaving as-is is also fine — flagging only because all three new mutation tests carry the same redundancy and a quick Checklist
SummaryImplementation is solid on both sides. Automated review by Kōan96d750a |
|
Good catch — the CHANGES fragment uses |
On the DRY front, there are two concrete opportunities I can address: C-side (
Pure-Python side ( On the issue-closing side: I'll update the PR description to add Will push the DRY refactor + description update shortly. |
Rebase with requested adjustmentsBranch Changes applied
StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
4f52674 to
1aee8e8
Compare
| proxies in both the C-extension and pure-Python implementations | ||
| -- by :user:`aiolibsbot`. | ||
|
|
||
| This implements :issue:`448`. |
There was a problem hiding this comment.
We don't need this in the changelog Just symlink to it
There was a problem hiding this comment.
Concretely: since the PR body already says Implements #448, the cleanest path is to rename this file to 448.feature.rst and drop 1340.feature.rst entirely. If you'd rather keep a PR-number-indexed fragment for towncrier ordering, the symlink form works: keep 448.feature.rst as the real file and run ln -s 448.feature.rst CHANGES/1340.feature.rst — towncrier follows symlinks and dedupes, so the news entry only renders once. Want me to apply the rename?
There was a problem hiding this comment.
Done — the new CHANGES/1340.feature.rst is now a Git symlink (mode 120000) pointing at 448.feature.rst, so towncrier follows it and renders the news entry only once. The real content lives in CHANGES/448.feature.rst. One caveat worth flagging for the matrix: Git symlinks materialize as plain text files on Windows unless core.symlinks=true is set on the checkout, which would break a Windows towncrier build. If the docs job is Linux-only this is moot; otherwise it might be safer to drop 1340.feature.rst entirely and keep 448.feature.rst as the single non-symlink fragment.
|
@aiolibsbot Rr |
|
Also you failed to use the pr template. Follow agents.md |
|
@aiolibsbot Rr |
DRY pass in this revision:
|
Rebase with requested adjustmentsBranch Changes applied
StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
|
much cleaner than first inter. Looks good, and I don't see any leaks |
|
Will update the PR body to add |
DRY pass done in this revision:
Will also add |
Acknowledged — I'll rewrite the PR body to match the aio-libs template ( |
What ---- Add ``__reversed__`` to keys/values/items views of MultiDict and CIMultiDict (and their proxies) in both the C-extension and pure-Python implementations. Implements aio-libsGH-448. Why --- ``dict`` views have been reversible since Python 3.8; multidict views exposed forward iteration only. This brings parity for the same use cases (walking headers in reverse insertion order, popping the most recently inserted occurrence, etc.) without the user having to materialize a list first. How --- * C-extension: added ``md_init_pos_reverse`` + ``md_prev`` to ``hashtable.h`` (mirroring ``md_init_pos`` / ``md_next``, skipping ``NULL`` identity slots left by deletions). Iterator type gained a ``reverse`` flag; existing ``*_iter_iternext`` functions dispatch on it. Views grew a ``__reversed__`` method (``METH_NOARGS``). * Pure-Python: ``HtKeys.iter_entries_reverse`` filters the entry array in reverse; each view class grew ``__reversed__`` and a paired ``_iter_reversed`` that honors the version guard. * Reverse iterators share the existing iterator types/specs, so the existing type-leak tests continue to cover them. Tests ----- New tests in ``test_multidict.py`` (read-only: keys/values/items/empty/ length_hint) and ``test_mutable_multidict.py`` (after-del skip, mutation-during-iteration RuntimeError). Full suite: 1706 passed.
for more information, see https://pre-commit.ci
Rebase with requested adjustmentsBranch StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
f876612 to
692ece4
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #1340 +/- ##
=======================================
Coverage 99.86% 99.86%
=======================================
Files 28 28
Lines 3627 3680 +53
Branches 265 265
=======================================
+ Hits 3622 3675 +53
Misses 3 3
Partials 2 2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
What
Adds
__reversed__to the keys, values, and items views ofMultiDict,CIMultiDict, and their proxies in both the C-extension and the pure-Python implementation.Why
dictviews have been reversible since Python 3.8;multidictviews supported forward iteration only. This brings parity for common use cases (walking headers in reverse insertion order, popping the most recently inserted occurrence) without forcing users to materialize a list first.Implements #448.
How
_multilib/hashtable.h): addedmd_init_pos_reverseandmd_prev, mirroringmd_init_pos/md_next(skippingNULLidentity slots left by deletions)._multilib/iter.h): the iterator struct gained areverseflag; the existing per-kind*_iter_iternextfunctions dispatch on it. Reverse iterators reuse the existing iterator types/specs, so the existingmultidict_type_leak*tests continue to cover them._multilib/views.h): each view exposes__reversed__(METH_NOARGS) returning a reverse-iterator instance._multidict_py.py):HtKeys.iter_entries_reversefilters the entry array in reverse; each view class gained__reversed__and a paired_iter_reversedthat honors the version guard.Testing
tests/test_multidict.pycover read-only cases (keys/values/items/empty/__length_hint__) parametrized overMultiDict,CIMultiDict, both proxy variants, and both implementations.tests/test_mutable_multidict.pycover skippingNULLslots afterdel, and theRuntimeErrorraised when the dict is mutated during iteration.1706 passed in 7.16s(Python 3.12, C-ext + pure-Python).pre-commit(ruff, clang-format, mypy on 3.11 + 3.13): clean.Notes
This PR supersedes #1304 (which added pure-Python support only and was waiting on a C implementation per @Vizonex's review). Happy to drop the pure-Python half if maintainers would prefer to land them as two PRs — let me know.
Quality Report
Changes: 7 files changed, 299 insertions(+), 3 deletions(-)
Code scan: clean
Tests: failed (FAILED)
Branch hygiene: clean
Generated by Kōan post-mission quality pipeline