Bug fixes
-
A segmentation fault that could be triggered when getting an item is now fixed
-- by :user:Vizonex.Related issues and pull requests on GitHub:
#1310. -
Fixed reference leak in iterators, views and
istr
-- by :user:Vizonex.Related issues and pull requests on GitHub:
#1311. -
Fixed the pure-Python :class:
~multidict.MultiDictconstructor and
:py:meth:~multidict.MultiDict.extend,
:py:meth:~multidict.MultiDict.update, and
:py:meth:~multidict.MultiDict.mergemethods over-allocating their
internal hash table when called with both a positional argument and
keyword arguments, because keyword arguments were counted twice in the
size estimate -- by :user:aiolibsbot.Related issues and pull requests on GitHub:
#1338. -
Fixed
__repr__of :class:~multidict.MultiDict,
:class:~multidict.CIMultiDict, their proxies, and the keys/items views
producing invalid output when keys contained quote characters --
keys are now formatted with :func:reprso the result is a valid Python
string literal -- by :user:aiolibsbot.Related issues and pull requests on GitHub:
#1342. -
Fixed a segfault when calling :py:meth:
~multidict.MultiDict.addwith only one of its two required arguments supplied by keyword, e.g.d.add(key="k"). Extra keyword arguments passed to the lookup and removal methods are also now rejected with :exc:TypeErrorinstead of being silently ignored.-- by :user:
devdanzinRelated issues and pull requests on GitHub:
#1376. -
Fixed a segfault when constructing a multidict iterator type directly, e.g.
type(iter(md.keys())).__new__(...). Such an iterator had a NULL internal pointer thatnext()dereferenced. The iterator types now forbid direct instantiation, the same way the view types were fixed in #1163.-- by :user:
devdanzinRelated issues and pull requests on GitHub:
#1377. -
Fixed a segfault when using a :py:class:
~multidict.MultiDictor :py:class:~multidict.CIMultiDictcreated via__new__without calling__init__(for example a subclass that does not callsuper().__init__()). The internal state was left as NULL pointers that the first method call dereferenced.tp_newnow initializes the object to a valid empty mapping.-- by :user:
devdanzinRelated issues and pull requests on GitHub:
#1378. -
Fixed two crashes in the C extension caused by holding a raw pointer into a hash table across an operation that could reshape it. Updating a multidict from itself (e.g.
d.extend(d)) freed the very table being iterated -- a use-after-free;extend(self)now doubles the contents andupdate(self)/merge(self)are no-ops. Building a multidict from a list of pairs whose case-insensitive key.lower()shrinks that list read past the end of the list; the length is now re-checked on every iteration.-- by :user:
devdanzinRelated issues and pull requests on GitHub:
#1379. -
Fixed three reference/resource leaks on error paths in the C extension: the items-view
__contains__leaked the first element of a candidate pair when reading the second one raised;__repr__leaked itsPyUnicodeWriterwhen the multidict was mutated during iteration; and the internal iteration helper leaked the identity reference when key materialization failed under low memory.-- by :user:
devdanzinRelated issues and pull requests on GitHub:
#1381. -
Stopped several feature-detection fallbacks in the C extension from swallowing every exception. When probing an argument (
arg.items()/arg.keys()) or measuring it (len(other)) fails, the code now clears only the expected :exc:TypeError/:exc:AttributeErrorand lets everything else -- notably :exc:MemoryErrorand :exc:KeyboardInterrupt-- propagate, matching the already-correct sites elsewhere in the module.-- by :user:
devdanzinRelated issues and pull requests on GitHub:
#1382. -
Fixed a segmentation fault when extending a multidict with itself
-- by :user:cananooand :user:asvetlov.Related issues and pull requests on GitHub:
#1398. -
Fixed a memory leak when
MultiDictandCIMultiDictinstances are
initialized more than once -- by :user:asvetlov.Related issues and pull requests on GitHub:
#1412. -
Fixed a reference leak in items-view set operations
-- by :user:asvetlov.Related issues and pull requests on GitHub:
#1413. -
Fixed a memory leak when destroying C-extension multidict instances
and proxies -- by :user:asvetlov.Related issues and pull requests on GitHub:
#1415. -
Fixed a reference leak from repeated
__init__calls on
MultiDictProxyandCIMultiDictProxyinstances -- by :user:asvetlov.Related issues and pull requests on GitHub:
#1419. -
Fixed missing decref for :data:
Noneon error in :meth:~multidict.MultiDict.setdefault
-- by :user:asvetlovRelated issues and pull requests on GitHub:
#1421.
Features
-
Added :py:func:
reversedsupport to the keys, values, and items views of
:class:~multidict.MultiDict, :class:~multidict.CIMultiDict, and their
proxies in both the C-extension and pure-Python implementations
-- by :user:aiolibsbot.Related issues and pull requests on GitHub:
#448. -
Optimized key identity comparison for C Extension.
Now it uses fast path that is equal to :c:func:
PyUnicode_Equalfrom
Python 3.14+ but without redundant type checks. It gives ~15% speed-up on benchmarks with many key comparisons.-- by :user:
asvetlovRelated issues and pull requests on GitHub:
#1406. -
Changed both the pure-Python and C implementations to mark a temporarily
removed hash table entry by setting the high bit of its hash instead of
overwriting it with a sentinel value, so restoring the entry no longer
recomputes the hash; :meth:~multidict.MultiDict.getallis faster ~10% now in C version
-- by :user:asvetlovRelated issues and pull requests on GitHub:
#1426.
Removals and backward incompatible breaking changes
-
Dropped support for Python 3.9 as it has reached end of life.
Related issues and pull requests on GitHub:
#1316. -
Dropped support for free-threaded Python 3.13 -- by :user:
ngoldbaum.Related issues and pull requests on GitHub:
#1326.
Improved documentation
-
Fixed
CIMultiDictProxydocumentation to state it inherits from
MultiDictProxy(notMultiDict), and fixed missing word in
istrsection -- by :user:veeceey.Related issues and pull requests on GitHub:
#1298. -
Clarified that
istrpreserves the original casing and
compares as a regularstr; case-insensitive matching is
handled byCIMultiDict-- by :user:gyanu2507.Related issues and pull requests on GitHub:
#1397. -
Fixed broken RST markup in
items()docstrings
-- by :user:veeceey.Related issues and pull requests on GitHub:
#1299.
Packaging updates and notes for downstreams
-
Dropped support for free-threaded Python 3.13 -- by :user:
ngoldbaum.Related issues and pull requests on GitHub:
#1326. -
Wheels for iOS and Android (CPython 3.13+) are now published on
release-tag builds, so downstreams on those platforms no longer need a
local compiler to installmultidict; these wheels are build-verified
only, since running the test suite on mobile needs a simulator or an
emulator -- by :user:aiolibsbot.Related issues and pull requests on GitHub:
#1337. -
Added support for building and shipping riscv64 wheels
-- by :user:justeph.Related issues and pull requests on GitHub:
#1293. -
The
setuptoolsbuild dependency lower bound has been restored to be
>= 47after an incorrect automated increase in :pr:1315
-- by :user:aiolibsbot.Related issues and pull requests on GitHub:
#1400.
Contributor-facing changes
-
Added support for collecting code coverage of isolated multidict tests
-- by :user:Vizonex.Related issues and pull requests on GitHub:
#1314. -
Switched to
mirrors-clang-formatand enabled clang-format inpre-commit.ci
-- by :user:bdraco.Related issues and pull requests on GitHub:
#1318. -
Added a release-tag-gated CI job that cross-compiles the C extension
for iOS and Android throughcibuildwheel-- by :user:aiolibsbot.Related issues and pull requests on GitHub:
#1337. -
Added an
AGENTS.mdorientation file at the repository root, covering the
pull request template, theCHANGES/news fragment conventions, the
draft-PR / human-review workflow, and the dual pure-Python and C-extension
parity rule, so LLM contributors land changes that match project style
-- by :user:bdraco.Related issues and pull requests on GitHub:
#1339. -
Added
init,installable,instantiation,parametrization,
parametrized,postfix, andunparseableto the docs spelling list
so news fragments and docs can use these words without failing the spell
check build -- by :user:pctablet505.Related issues and pull requests on GitHub:
#1343. -
Documented three common agent failure modes in
:file:AGENTS.md: the docs spell check
(make doc-spelling) catches unknown words in news fragments
before CI does; coverage runs over the test tree too, so
unreachable defensiveraiseguards and one-sided cleanup
branches in tests will surface as uncovered on the codecov
patch report; and branch creation is restricted on
aio-libs/multidict, so PRs must be pushed from a fork
-- by :user:bdraco. -
Trimmed the CI test matrix to drop redundant
Py_DEBUGjobs on
macOS and Windows and to gate thewindows-11-armwheel build to
release-time only, cutting CI wall-clock by roughly a third without
reducing code coverage -- by :user:aiolibsbot.Related issues and pull requests on GitHub:
#1346. -
Added a
CLAUDE.mdat the repository root that imports
:file:AGENTS.mdvia Claude Code's@-syntax, so the
project's LLM contributor rules load automatically when
working in Claude Code
-- by :user:aiolibsbot.Related issues and pull requests on GitHub:
#1347. -
Enabled the
I(import sorting) rule in
[tool.ruff.lint]soruff checkcovers import order on top
of the existingUP(pyupgrade) group, and updated the
ruff-checkpre-commit hook to run with
--fix --exit-non-zero-on-fix --show-fixesso import-order
fixes apply on commit. Sorted imports in fourtests/files
to match the new rule -- by :user:bdraco.Related issues and pull requests on GitHub:
#1348. -
Enabled the
PLC0415(import-outside-top-level) rule in
[tool.ruff.lint]. Function-scoped imports must now opt in
with# noqa: PLC0415plus a comment explaining the reason
(typically avoiding a heavy optional dependency at import time).
This catches a pattern that LLM contributors frequently introduce.
There are currently no opt-outs in the tree, so the rule is
enforced everywhereruff checkruns
-- by :user:bdraco.Related issues and pull requests on GitHub:
#1349. -
Switched the
cibuildwheelbuild frontend tobuild[uv]so
thatuvprovisions every build and test virtual environment
in the wheel matrix. Test-dependency installation in particular
drops from a multi-secondpip installper ABI to a roughly
sub-seconduvresolve
-- by :user:bdraco.Related issues and pull requests on GitHub:
#1350. -
Overrode
CIBW_BUILD_FRONTEND=buildfor the odd-arch wheel
matrix; the upstreammanylinux/musllinuximages for
those arches do not shipuv
-- by :user:bdraco.Related issues and pull requests on GitHub:
#1352. -
Allowed re-running the deploy job after a partial release failure: the
Make Releasestep now skips when the GitHub Release already exists,
and the PyPI publish step usesskip-existingso dists that were
already uploaded on a prior attempt do not break the retry
-- by :user:bdraco.Related issues and pull requests on GitHub:
#1353. -
Switched the aarch64 and armv7l wheel builds to GitHub's native ARM
runners. The aarch64 wheels now build without QEMU emulation, and
armv7l runs on aarch64 hosts so its 32-bit ARM execution is far
cheaper than the previous aarch64-on-x86_64 path
-- by :user:bdraco.Related issues and pull requests on GitHub:
#1354. -
Documented design principles for pure-Python :class:
~multidict.istrimplementation
-- by :user:asvetlovRelated issues and pull requests on GitHub:
#1360. -
Pinned
coverageto thectracemeasurement core so the test suite runs
on Python 3.14. Coverage pickssysmonthere, which cannot record the
dynamic contextspytest-covswitches at runtime, and the resulting warning
became an error under the suite'sfilterwarningssetting
-- by :user:rodrigobnogueira.Related issues and pull requests on GitHub:
#1393. -
Dependabot has been restricted to the
requirementssubdirectory to
avoid unintended updates outside dependency requirement files
-- by :user:aiolibsbot.Related issues and pull requests on GitHub:
#1400. -
Enabled automatic upgrades from ruff after each python version that is dropped
-- by :user:Vizonex.Related issues and pull requests on GitHub:
#1325.
Miscellaneous internal changes
-
Explicitly marked
empty_htkeysasconst.Moved the structure to
.rodatalinker section.Unexpected modification of the structure will lead to segfault instead
of corrupting the data and fail fast..rodatais mount as read-only-mapped, it is shared well across
multiple threads without cache misses.The change is pretty trivial, it is made for the sake of correctness,
not for speedup.-- by :user:
asvetlov.Related issues and pull requests on GitHub:
#1405. -
Slightly reorganized multidict creating process.
- Removed unnecessary calculations when
md_init()creates an empty multidict. - Now
tp_allocslot is used in object cloning instead of bare :c:func:PyType_GenericNewcall.
-- by :user:
asvetlovRelated issues and pull requests on GitHub:
#1420. - Removed unnecessary calculations when
-
Dropped dead code,
_md_del_at()and_md_del_at_for_upd()never fails
-- by :user:asvetlovRelated issues and pull requests on GitHub:
#1423. -
Fixed a missing space in the :exc:
ValueErrormessage
raised when constructing a multidict from a sequence
-- by :user:veeceey.Related issues and pull requests on GitHub:
#1295. -
Renamed the benchmark script from
benchmarks/becnhmark.pyto
benchmarks/benchmark.pyso it matches the invocation documented in
:doc:benchmark-- by :user:aiolibsbot.Related issues and pull requests on GitHub:
#1335.