Skip to content

chore(deps): update dependency multidict to v7 - #18617

Open
renovate-bot wants to merge 1 commit into
googleapis:mainfrom
renovate-bot:renovate/multidict-7.x
Open

renovate-bot wants to merge 1 commit into
googleapis:mainfrom
renovate-bot:renovate/multidict-7.x

Conversation

@renovate-bot

@renovate-bot renovate-bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

ℹ️ Note

This PR body was truncated due to platform limits.

This PR contains the following updates:

Package Change Age Confidence
multidict ==6.9.1 → ==7.1.0 age confidence

Release Notes

aio-libs/multidict (multidict)

v7.1.0

Compare Source

=====

(2026-10-08)

7.1.0 is a correctness and housekeeping release. It changes what
:meth:~multidict.MultiDict.keys yields, fixes a long list of crashes and
inconsistencies that Python code run in the middle of an operation could
trigger, stores the common all-:class:str multidicts in half the space, and
deprecates multidict.upstr.

Behaviour changes. :meth:~multidict.MultiDict.keys and iteration over a
multidict yield each key once, as spelled in its first item, so
len(d.keys()) counts distinct keys; :meth:~multidict.MultiDict.items,
:meth:~multidict.MultiDict.values and len(d) still see every item. A
:class:str subclass's own lower() is no longer called to compute the
case-insensitive identity of a :class:~multidict.CIMultiDict key.

Robustness. Finalizers, __eq__(), __repr__(), lower() and
argument iterators that read or mutate the multidict in the middle of
__init__(), update(), merge(), del d[key],
:meth:~multidict.MultiDict.popall, :meth:~multidict.MultiDict.popitem,
:meth:~multidict.MultiDict.to_dict or :func:repr no longer crash the C
extension, lose or duplicate pairs, or make the two backends disagree. Two
data races against lock-free readers on the free-threaded build are gone
too.

Memory. A :class:~multidict.MultiDict whose keys are all exact
:class:str, and a :class:~multidict.CIMultiDict whose keys are all exact
:class:~multidict.istr, are stored in a compact table without a separate
identity and hash per entry, which cuts their size by about 45%.

Bug fixes

  • Fixed building the C extension on platforms where char is unsigned,
    such as ARM, PowerPC, s390x and RISC-V, and with GCC 9, which flags
    CPython's own headers from 3.12 on; both failed on a sign conversion
    warning since -Wconversion took effect. GCC 9 builds the extension
    without -Wconversion -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1759, :issue:1760.

  • Fixed :func:~multidict.getversion returning only the low 32 bits of
    the version on Windows and on 32-bit platforms, where two different
    versions could compare equal, and fixed a race in the pure-Python
    implementation on GIL builds that could give two multidicts mutated at
    the same time from different threads the same version
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1607, :issue:1610.

  • Fixed in on an items view truncating the length of a non-tuple,
    non-list operand to 32 bits in the C extension, which let an object
    reporting 2**32 + 2 items be indexed as a pair and raise
    :exc:TypeError instead of returning False -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1611.

  • Fixed adding to a full table of the C extension, which compacted it in
    place whenever it held deleted entries and so regained only as many
    slots as were deleted: a delete-then-add loop on a full table rebuilt
    it on every add. The table was resized by the number of live entries
    instead, as the pure-Python implementation did, which also let it
    shrink after deletions. Fixed :meth:~multidict.MultiDict.extend,
    :meth:~multidict.MultiDict.update and :meth:~multidict.MultiDict.merge
    reserving room by comparing table sizes, which ignored the room taken by
    deleted entries; the reservation checked the free room instead, in both
    implementations -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1615, :issue:1617.

  • Fixed calling __init__() again on a live multidict losing and leaking
    whatever a finalizer of an old value added to it in the C extension, and
    miscounting :func:len in the pure-Python implementation, by installing
    the new contents and size before releasing the old pairs
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1619, :issue:1626.

  • Fixed two data races on free-threaded builds against lock-free readers:
    copy() and __init__() from a multidict of the same kind read the
    source's reader count while its readers updated it, and __init__()
    on a live multidict rewrote the case-insensitivity flag its lookups
    read -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1621.

  • Fixed MultiDict.__init__() called on a :class:~multidict.CIMultiDict making it
    case-sensitive in the C extension, and CIMultiDict.__init__()
    rejecting a :class:~multidict.MultiDict there, by taking the mode from the
    instance rather than from the class whose __init__() was called, as
    the pure-Python implementation already did -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1622.

  • Fixed the C extension dropping the existing items when a multidict was
    re-initialized from itself, or from a proxy of itself, together with keyword
    arguments, as in d.__init__(d, key=value); the pure-Python
    implementation was not affected -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1666.

  • Fixed a use-after-free in :meth:~multidict.MultiDict.popitem when
    building the result ran Python code that mutated the multidict: a
    :class:str subclass key's __str__() on
    :class:~multidict.CIMultiDict, or on Python 3.10 and 3.11 a garbage
    collection's finalizers, by removing the pair first, and fixed the
    pure-Python popitem() miscounting :func:len in the same case
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1625.

  • Fixed del md[key] and :meth:~multidict.MultiDict.popall also
    removing, and popall() also returning, the pairs a finalizer of a
    removed key or value added while the call was still running, by
    releasing the removed pairs only once every match is gone, and fixed the
    pure-Python implementation raising :exc:IndexError or miscounting
    :func:len in the same case, for md[key] = value too
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1627.

  • Fixed a crash when Python code run between the items of
    :meth:~multidict.MultiDict.update (the argument's own iterator, a key's
    lower() or a finalizer) read the multidict, where the pure-Python
    implementation returned None for the pairs the call was about to
    remove, and fixed update() and :meth:~multidict.MultiDict.merge
    overwriting or losing pairs when that code changed the multidict. An
    update from another multidict raised :exc:RuntimeError when a key's
    lower() mutated the source -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1628, :issue:1744.

  • Fixed a use-after-free in :meth:~multidict.MultiDict.to_dict on Python
    3.10 and 3.11, where allocating a value list can run a garbage collection
    whose finalizers mutate the multidict, by refusing that mutation with
    :exc:RuntimeError as it is refused from a key's __hash__()
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1630.

  • Fixed :func:repr of a multidict reading freed memory on the GIL build
    when a key's or value's __repr__ called an extend(), update()
    or merge() that grew the table and then failed, by bumping the version
    on every resize -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1658.

  • Fixed :class:~multidict.CIMultiDict returning a key added as a
    :class:str subclass with the spelling of the subclass's own
    :meth:~object.__str__ instead of the key's, and stopped the C extension from
    replacing such a key with its :class:~multidict.istr while reading it, which
    could run the key's finalizer inside the read. Documented that
    :class:~multidict.CIMultiDict converts keys to :class:~multidict.istr
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1640.

  • Fixed the :class:~multidict.istr keys that
    :meth:~multidict.MultiDict.extend, :meth:~multidict.MultiDict.update,
    :meth:~multidict.MultiDict.merge and the :class:~multidict.MultiDict
    constructor took from a :class:~multidict.CIMultiDict: they carried the
    key's own spelling as their case-insensitive identity instead of its
    lower-cased form, so a :class:~multidict.CIMultiDict keyed by one of them
    found it under no spelling of the key. Changed the pure-Python
    implementation to hand over those keys as :class:~multidict.istr too, as
    the C extension did -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1647.

  • Fixed a lock-free lookup on the free-threaded build reading the hash
    through a key of a compact table that a concurrent delete had already
    freed, by taking a reference to such a key first unless it is the very
    object being looked up -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1653.

  • Changed CIMultiDictProxy.__init__() called without an argument to
    report the same :exc:TypeError message as MultiDictProxy and the
    pure-Python implementation -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1677.

Features

  • Made :class:~multidict.MultiDict in the C extension store a mapping whose
    keys are all exact :class:str in half the space, keeping no separate
    identity or hash per entry, and did the same for a
    :class:~multidict.CIMultiDict whose keys are all exact
    :class:~multidict.istr, taking each identity from the key's canonical
    form, which cut its size by about 45%. Copying such a mapping became
    about 55% cheaper in instructions and :meth:~multidict.MultiDict.clear
    about 27%; any other key moves the mapping to the full layout, and
    :meth:~multidict.MultiDict.clear lets it start compact again. A lookup
    also stopped taking a reference to the identity of an exact :class:str
    or :class:~multidict.istr key, which made d[key] and key in d up
    to 7% faster on the GIL build and up to 20% cheaper in instructions on the
    free-threaded one -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1644, :issue:1648, :issue:1651.

  • Sped up :meth:~multidict.MultiDict.getall, del d[key], replacing a
    key with d[key] = value and :meth:~multidict.MultiDict.update by 5
    to 14 percent, and up to 26 percent in the pure-Python implementation,
    when no key repeats, which each hash table started to track; the
    tracking made :meth:~multidict.MultiDict.add up to 10 percent slower,
    and building a small multidict from items up to 18 percent, both up to
    16 percent in the pure-Python implementation -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1654.

  • Replaced the set of visited entries that :meth:~multidict.MultiDict.getall,
    :meth:~multidict.MultiDict.to_dict and the items view's & kept with a
    check that each match's entry index is above the previous one's, since
    equal keys are first reached in insertion order. A
    :meth:~multidict.MultiDict.getall of a key with many values needed up to
    half fewer instructions -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1752, :issue:1756.

  • Sped up :meth:~multidict.MultiDict.update and
    :meth:~multidict.MultiDict.merge: they stopped growing the table up front
    when it could hold the argument even empty, as :meth:dict.update does,
    stopped holding extra references to the keys and values of a :class:dict
    argument, and walked the hash chain for each item without a function
    call. An update of keys already present needed 13% to 29% fewer
    instructions -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1747, :issue:1755, :issue:1757.

  • Made d[key] = value in the C extension keep the collector for
    duplicate keys off the stack until a duplicate turns up, which made
    replacing a key about 4-6% and adding a new one about 2-3% cheaper in
    instructions -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1650.

  • Sped up building an :class:~multidict.istr from a :class:str in the C
    extension by copying the string directly instead of going through
    str.__new__(); reading a :class:str key of a
    :class:~multidict.CIMultiDict for the first time and
    :meth:~multidict.MultiDict.popitem became about 30% cheaper in
    instructions -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1645.

  • Stopped a multidict with no watchers from testing for them on every
    record made by del d[key], d[key] = value and pop(): each
    operation checked once and ran a copy without the watcher code, which
    recovered most of the cost the watchers C API had added to them. Changed
    free-threaded builds to reserve versions for each multidict in blocks of
    256, so a mutation advanced a counter in the object itself instead of
    looking up a thread-local one, which cut 2 to 5 percent of the
    instructions of every mutation -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1604, :issue:1605.

  • Stopped clearing each field of a hash table that is being freed, and read
    its entry count once instead of after every release, cutting about 8% off
    tearing down a table of 200 entries in clear() and on deallocation;
    replaced Py_CLEAR() on success paths with Py_DECREF() or
    Py_XDECREF(), which made building a multidict from items up to 1.7%
    and comparing one with another mapping up to 4% faster
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1639, :issue:1642.

Deprecations (removal in next major release)

  • Started emitting :exc:DeprecationWarning on access to multidict.upstr,
    the alias for :class:~multidict.istr deprecated since 2.0, and removed it
    from multidict.__all__
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1680.

Removals and backward incompatible breaking changes

  • Changed :meth:~multidict.MultiDict.keys and iteration over
    :class:~multidict.MultiDict, :class:~multidict.CIMultiDict and their
    proxies to yield each key once, as spelled in its first item, so that
    CIMultiDict([('X-Foo', '1'), ('x-foo', '2')]) iterates over 'X-Foo'
    alone and len(d.keys()) counts distinct keys; :meth:~multidict.MultiDict.items,
    :meth:~multidict.MultiDict.values and len(d) kept every item
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1654.

  • Stopped calling a :class:str subclass's own lower() for the
    case-insensitive identity of a key in :class:~multidict.CIMultiDict and in
    :c:func:IStr_FromUnicode; the identity came from :meth:str.lower instead,
    so computing it ran no user code -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1746.

Packaging updates and notes for downstreams

  • Moved the project metadata from setup.cfg to pyproject.toml per
    :pep:621, and removed the now-empty setup.cfg along with its obsolete
    [bdist_wheel] universal flag. Adopted :pep:639 license metadata:
    declared the license as the SPDX expression Apache-2.0 and moved
    license-files into the [project] table, which raised the
    build-time requirement to setuptools >= 77.0. Built distributions
    started carrying License-Expression in place of the legacy
    License field -- by :user:aiolibsbot.

    Related issues and pull requests on GitHub:
    :issue:1646.

  • Added the benchmarks/ scripts to the source distribution, so that the
    test suite run from an unpacked sdist also covers the benchmark driver
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1624.

Contributor-facing changes

  • Stopped the wheel jobs that run under QEMU from running the pure-Python
    half of the tests marked expensive, and the tests marked threaded
    on GIL builds, which had grown enough since 7.0.0 to take the manylinux
    riscv64 job past its two-hour timeout; native jobs still run them
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1762.

  • Added tools/check_inlining.py and an Inlining CI job, which
    fail when GCC stops inlining a helper that a hot path depends on, or
    makes an out-of-line copy of an inline function from the CPython headers
    or from pythoncapi_compat.h such as Py_DECREF(), since each time
    that happened a benchmark regressed on code the change never touched
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1612, :issue:1686.

  • Stopped passing -Wno-conversion to the C extension build, which
    had silently turned -Wconversion off, and added the explicit casts
    it then required -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1611.

  • Dropped the macOS and Windows legs of the CI test matrix, since
    cibuildwheel already ran the same test suite against the very wheels
    those jobs installed. Moved every artifact the CI test jobs consume into
    one first stage, the source distribution together with the pure-Python
    and Linux binary wheels, and switched the remaining test jobs to that
    source distribution instead of a repository checkout. The wheel builds
    for the other platforms stopped gating the test matrix, the
    windows-11-arm wheel build got limited to release tags again, and
    x86_64 macOS wheels started being tested under Rosetta outside pull
    requests only -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1575, :issue:1638.

  • Added CodSpeed benchmarks for mutating a multidict that a watcher is
    attached to and for re-initializing a populated multidict, started
    benchmarking the FT build alongside the GIL one, and split each build's
    benchmark CI job into two parallel shards of similar length, selected by
    a new benchmark_shard_2 pytest mark -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1608, :issue:1620.

  • Changed benchmarks/callgrind_driver.py to run its Callgrind children from a
    fixed-length staging directory holding only the modules they import, so that
    two checkouts of one commit at different paths no longer measure up to 3%
    apart on allocating operations, and pinned mimalloc's arena address and purge
    timer, which had moved free-threaded counts between two runs of one tree.
    Stopped the driver from collecting whole-process counts, which cannot be
    compared with bracketed ones, unless it is given --whole-process, and
    made its checks before measuring, including --self-check, cover only
    the implementations selected with --impl, so a pure-Python install can
    be measured -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1623, :issue:1624, :issue:1637.

  • Added a test that runs every mutating method of both multidict classes
    with keys, values and argument items whose finalizers mutate the
    multidict, and checks the C implementation against the pure-Python one,
    along with reusable helpers for writing such tests, and a threaded stress
    test that re-initializes a shared multidict whose old values each have
    such a finalizer while other threads run lock-free lookups and iterate it
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1621, :issue:1631, :issue:1633.

  • Made the ThreadSanitizer run reliable: added
    tools/tsan_suppressions.txt for the faulthandler watchdog's
    traceback dumps, used by the CI job and by the documented local command,
    which also gained --no-cov; and changed the two pure-Python deadlock
    tests for reciprocal view operations to fail only when their workers stop
    making progress for 20 seconds, rather than when they have not finished
    within 20 seconds; and rewrote the retry loop of the test for
    reference leaks on memory errors so that it takes the same branches on
    every Python version
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1655.

  • Renamed the hypothesis-freethreading CI job to hypothesis-ft
    (Hypothesis FT), on a par with the GIL one, and removed the update
    argument of the debug-only ASSERT_CONSISTENT() check, which stopped
    doing anything once :meth:~multidict.MultiDict.update kept its doomed
    entries whole -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1609, :issue:1629.

  • Moved the sanitizer build recipes and the performance measurement
    procedure out of AGENTS.md into Claude Code skills under
    .claude/skills/, and dropped its file layout table, so that less of
    it is loaded into every agent session
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1692.

Miscellaneous internal changes

  • Reworked how the C extension spends GCC's inlining budget, which it sits
    at as a single translation unit. Dropped the ALWAYS_INLINE macro and
    inline from almost every helper, leaving the decision to the
    compiler, and turned the hash table index lookup, store and probe helpers
    into macros. Moved the table resize and reservation, clear(),
    popitem(), the equality comparison, the iterator and view
    constructors, the end of a lock-free read and other rarely run code out
    of line, and made the proxy methods tail-call the shared ones. Marked the
    error-raising helpers, __sizeof__, __reduce__() and the proxy
    constructors' error paths cold, and kept NOINLINE only for the few
    helpers GCC re-inlined without it. Moved argument parsing into the method
    entry points and returned the keyword-bound arguments by value, which
    dropped a stack protector canary from every get(), getone(),
    getall(), add() and setdefault() call
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1613, :issue:1614, :issue:1661, :issue:1664, :issue:1667,
    :issue:1670, :issue:1671, :issue:1672, :issue:1683, :issue:1690,
    :issue:1691, :issue:1693, :issue:1694, :issue:1695, :issue:1697,
    :issue:1698, :issue:1699, :issue:1700, :issue:1701, :issue:1702,
    :issue:1703, :issue:1704, :issue:1705, :issue:1707, :issue:1709,
    :issue:1711, :issue:1712, :issue:1713, :issue:1714, :issue:1717,
    :issue:1718, :issue:1719, :issue:1720, :issue:1722, :issue:1723,
    :issue:1724, :issue:1726, :issue:1727, :issue:1730, :issue:1732,
    :issue:1733, :issue:1734, :issue:1735, :issue:1737, :issue:1739,
    :issue:1740, :issue:1749, :issue:1750, :issue:1751.

  • Shrank the C extension by merging duplicated code: the bodies of
    __init__(), extend(), update() and merge(), of pop()
    and popone(), the per-class copies of the entry points, the update
    loops and the tp_vectorcall slots, which read the class at run time
    instead, the repeated tails of the table rebuilds and of getall() and
    popall(), and the module setup, which became two table loops. The
    set operators and comparisons of the keys and items views were rewritten
    around shared helpers compiled for size. When they landed, the view
    rewrite made the compiled extension about 6% smaller and reading the
    class at run time about 15%, which left room for the compact tables; the
    view set operators became up to 17% slower and update() and
    merge() up to 3% slower -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1669, :issue:1673, :issue:1675, :issue:1677, :issue:1678,
    :issue:1679, :issue:1706, :issue:1715, :issue:1716, :issue:1725,
    :issue:1728, :issue:1729, :issue:1731.

  • Reorganized how the C extension's hash table lays out and reaches its
    entries: split the entry into a key and value layout that every table
    kind shares and a layout that adds the identity and its hash, computed
    the entry size with sizeof, told a deleted entry by its key alone,
    replaced the md_pos_t cursor and the entry stepping helpers with
    typed, indexed access, and walked a table in one loop per table kind and,
    for compact tables, per class. repr(), the views' | and -,
    the update from another multidict and the comparison with a plain
    mapping moved onto the shared walk over all entries
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1641, :issue:1649, :issue:1656, :issue:1659, :issue:1660,
    :issue:1662, :issue:1663, :issue:1665, :issue:1674, :issue:1676,
    :issue:1741, :issue:1742, :issue:1743, :issue:1745, :issue:1748.

  • Merged the C extension's two ways of dropping a hash table it no longer
    used, freeing it at once on GIL builds and retiring it on free-threaded
    ones, into one helper -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1618.

  • Upgraded pythoncapi_compat, and stopped copying the fast
    path of :c:func:!PyUnstable_TryIncRef into the free-threaded C
    extension, where it read CPython's private reference count fields, by
    calling the function itself instead -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1687, :issue:1688.

  • Removed dead code from the C extension: a self-update branch no caller
    could reach, version guards that are always true on the supported Python
    versions, unused helpers and single-use forwarding wrappers. Renamed the
    header-local helpers to carry a leading underscore, moved
    istr_canonical() next to the istrobject struct, and folded the
    pure-Python update() match test back into one condition
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1635, :issue:1668, :issue:1689, :issue:1753, :issue:1754.

  • Added const qualifiers to internal C helpers that read table, reference list,
    and watcher metadata -- by :user:anshurajbisoyi98-ctrl.

    Related issues and pull requests on GitHub:
    :issue:1643.


v7.0.0

Compare Source

=====

(2026-09-26)

7.0.0 is a major release. It makes :class:~multidict.istr final, adds a
public C API for third-party extensions, and makes the C extension
substantially faster across the board.

Breaking change. :class:~multidict.istr can no longer be subclassed,
on either backend. Code that subclassed it has to wrap or convert instead.
The C extension also started rejecting an argument passed both
positionally and by name with :exc:TypeError, where it used to silently
misbehave.

Public C and Cython API. Other C extensions and Cython modules can now
create, read and mutate multidicts through a capsule, without going through
the Python-level API. The capsule comes with multidict_capi.h, a
cimport-able multidict/__init__.pxd and
:func:multidict.get_include. The API includes MultiDict_ForEach() and
a watchers API modeled on CPython's dict watchers. See the :doc:C API <capi> and
:doc:Cython API <cyapi> references.

Performance. On CodSpeed's GIL-build benchmarks against 6.9.1, 82
C-extension benchmarks got faster and none got slower. Rough figures:

  • :class:~multidict.CIMultiDict keyed by plain :class:str: construction,
    add(), extend(), update() and item assignment got 2 to 3.8
    times faster, and lookups about 2 times faster.
  • :class:~multidict.CIMultiDict keyed by :class:~multidict.istr, and
    case-sensitive :class:~multidict.MultiDict: lookups, insertion and
    deletion got 10% to 70% faster.
  • :meth:~multidict.MultiDict.getall and iterating
    :meth:~multidict.MultiDict.items got about 35% faster, and the view set
    operations 10% to 85% faster.
  • :meth:~multidict.MultiDict.popitem on a whole mapping went from
    quadratic to linear time.
  • On the free-threaded build, measured in instructions against 6.9.1,
    lookups got 24% to 33% cheaper on :class:~multidict.MultiDict and 4 to 5
    times cheaper on :class:~multidict.CIMultiDict with lowercase :class:str
    keys, and item assignment 15% to 19% cheaper. Construction and deletion on
    :class:~multidict.MultiDict cost 2% to 7% more. With several threads
    mutating at once, a d[key] = value got about four times faster.

The pure-Python backend is unchanged in speed.

Robustness. This release fixed several crashes and leaks in the C
extension, including use-after-free bugs when a key's lower() or a
value's __eq__ mutated a multidict, a crash at interpreter shutdown,
and table and reference leaks on the free-threaded build.

Bug fixes

  • Fixed a segmentation fault in the C extension when a finalizer let the
    same multidict be resized while del md[key] or
    :meth:~multidict.MultiDict.popall was still scanning the table, and a
    spurious :exc:RuntimeError from :meth:~multidict.MultiDict.getall
    when a garbage collection triggered while building its result mutated
    the multidict -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1492, :issue:1512.

  • Fixed a reference leak of the key and value in the C extension when
    growing the hash table fails with :exc:MemoryError during
    :meth:~multidict.MultiDict.add, :meth:~multidict.MultiDict.update,
    :meth:~multidict.MultiDict.merge, :meth:~multidict.MultiDict.setdefault
    or item assignment -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1507.

  • Fixed a data race on the free-threaded build of the C extension where a
    MultiDict's own version counter was read by
    :func:~multidict.getversion and written on every mutation with a plain,
    non-atomic load and store. Both sides switched to a relaxed atomic
    load/store, matching the pattern already used for the object's item
    count -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1514.

  • Fixed the C extension discarding a :exc:MemoryError or
    :exc:KeyboardInterrupt raised by a positional argument's __len__
    while the size of an update was estimated. extend(), update(),
    merge() and subclass construction started propagating it, matching the
    pure-Python backend; only the :exc:TypeError from an unusable
    __length_hint__ stayed ignored -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1517.

  • Ensured that canonical keys in pure-Python :class:~multidict.MultiDict and
    :class:~multidict.CIMultiDict are converted to exact :class:str instances
    without invoking :meth:object.__str__ overrides, matching the C extension --
    by :user:agustin18.

    Related issues and pull requests on GitHub:
    :issue:1535.

  • Fixed the argument counts reported by the C extension when a method is
    called with too many positional arguments; methods with an optional
    second argument claimed to take exactly one, and methods requiring two
    claimed to take a range. A method called with none of its two required
    arguments now reports both as missing -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1545.

  • Fixed the C extension accepting an argument passed both positionally and
    by name instead of raising :exc:TypeError. md.add("a", key="k", value="v") stored a wrong value, and md.get("a", "b", key="c")
    silently ignored the surplus arguments -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1545.

  • Fixed a memory leak in the free-threaded build of the C extension.
    :meth:~multidict.MultiDict.clear hands its hash table to a drain that
    frees it once no lock-free reader is in flight, and a drain that finds
    one leaves the table for the next drain to free. Deallocation ran no
    drain of its own, so a multidict dropped after such a clear, with no
    operation in between, never freed that table nor released the references
    its entries still held. Builds with the GIL enabled emit byte-identical
    code and are unaffected -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1555, :issue:1562.

  • Fixed a crash during interpreter shutdown in the C extension: a
    multidict started keeping a reference to its own module, so the module
    state it reads while being torn down could not be freed first
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1563.

  • Fixed retired hash tables lingering in the free-threaded build of the C
    extension. A drain that finds a lock-free reader in flight puts the tables
    it holds back for the next drain to run, and the reader it saw could
    already have looked, so nothing was left to free them: they stayed until
    the multidict was next used, and if it was dropped instead, the references
    their entries held were never released. Builds with the GIL enabled emit
    byte-identical code and are unaffected -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1565.

  • Fixed reference cycles through a multidict going uncollected in the
    free-threaded build of the C extension. A hash table waiting to be freed
    still owns the references of the entries left in it, and those were not
    reported to the garbage collector, which then read the values as reachable
    from outside the cycle and kept it alive. Builds with the GIL enabled emit
    byte-identical code and are unaffected -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1566.

  • Fixed extend(), update() and merge() of the C extension
    raising :exc:SystemError when given an object whose
    __length_hint__() reported close to :data:sys.maxsize together
    with keyword arguments, and overflowing the table size computation for
    such a hint; a hint too large to reserve for is ignored, as
    :meth:list.extend does -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1577.

  • Fixed a use-after-free in the C extension's MultiDict.__eq__
    when a value's __eq__ mutated either multidict; comparing
    exact :class:str values also became faster -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1579.

  • Fixed a use-after-free in the C extension when a :class:str subclass
    key's lower() mutated the source while a
    :class:~multidict.CIMultiDict was built from, extended, updated or
    merged with a :class:dict or a multidict of the other case
    sensitivity. The extra references this takes are limited to the keys
    whose lower() can run Python code, so other updates do not pay for
    them -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1581, :issue:1598.

Features

  • Added a public C and Cython API for third-party extensions. Its
    MultiDict_ForEach() family hands a visitor each entry's key, value,
    identity (the canonical key the mapping looks entries up by, lower-cased
    on a :class:~multidict.CIMultiDict) and the identity's hash
    -- by :user:Vizonex and :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1478.

  • Changed the C extension to track the entries that
    :meth:~multidict.MultiDict.getall, item assignment,
    :meth:~multidict.MultiDict.update, :meth:~multidict.MultiDict.merge
    and :meth:~multidict.MultiDict.to_dict have visited in a bitmap private
    to the call, instead of temporarily marking entry hashes in the table
    itself. Concurrent readers on free-threaded builds no longer have to
    tolerate marked entries. Item assignment,
    :meth:~multidict.MultiDict.to_dict and lookups of keys with many values
    got faster, while :meth:~multidict.MultiDict.update with many repeated
    keys got somewhat slower
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1505.

  • Replaced the C extension's per-key finder object with a single
    callback-driven walk in a new _multilib/walk.h header. The entries a
    walk has already reported were tracked by the walk itself rather than
    by every caller, and the walk kept its position in registers instead of
    reloading it from a struct on each step, so
    :meth:~multidict.MultiDict.getall and the items and keys view set
    operations got faster on keys with many values -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1522.

  • Sped up CIMultiDict operations keyed by a plain str in the C
    extension. An all-ASCII key was lowered directly instead of through a
    str.lower() call, and a key that is already lowercase became its own
    identity, which saved both a copy and a rehash. Looking up a lowercase key
    got about three times faster and inserting one about twice as fast, keys
    that do carry uppercase gained 20% to 35%, and bulk operations on
    already-lowercase keys, such as :meth:~multidict.MultiDict.extend and
    construction from a :class:dict, got up to three times faster
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1527.

  • Sped up building an istr in the C extension. The class gained its own
    vectorcall, so the one-argument form stopped packing its argument into a
    tuple for :c:func:!type_call only to unpack it again with
    :c:func:PyArg_ParseTupleAndKeywords. Building an istr from a
    :class:str got about 19% faster, and building one from an istr,
    which hands back the original, about three times faster. In exchange
    :meth:~multidict.MultiDict.getall costs nine more instructions per
    call, since the added code moves where the compiler draws its inlining
    line -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1537.

  • Sped up building and dropping multidicts in the C extension by reusing
    freed hash tables instead of returning them to the allocator. Small
    tables are kept in a bounded per-size-class pool held in the module
    state, the way CPython reuses key objects for :class:dict. Only on
    builds with the GIL: a shared pool needs an atomic exchange to take a
    table and another to put one back, which measured slower than the
    per-thread allocator a free-threaded build already has
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1539.

  • Sped up :meth:~multidict.MultiDict.copy, constructing a multidict from
    another one, and growing one past its current size in the C extension, by
    not zeroing the parts of a hash table that are about to be overwritten. A
    copied table is overwritten in full, and a resized one has the entries it
    carries over written straight onto it -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1540.

  • Sped up :meth:~multidict.MultiDict.items,
    :meth:~multidict.MultiDict.keys, :meth:~multidict.MultiDict.values
    and iterating a multidict in the C extension, by reusing the view and
    iterator objects they allocate instead of returning them to the
    allocator -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1541.

  • Sped up constructing a :class:~multidict.MultiDict,
    :class:~multidict.CIMultiDict, :class:~multidict.MultiDictProxy or
    :class:~multidict.CIMultiDictProxy in the C extension, by reusing the
    object itself as well as its hash table. Subclasses are unaffected and
    keep allocating as before -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1542.

  • Sped up :meth:~multidict.MultiDict.getall in the C extension, by
    shrinking the scratch buffer the walk carries on the stack from 4 KB to
    1 KB. The buffer was large enough to stop the compiler inlining the walk
    into its callers, which cost more than it saved -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1543.

  • Sped up lock-free reads on the free-threaded build of the C extension.
    Every reader that leaves a :class:~multidict.MultiDict last used to
    call into the retired-table drain, whose first act is a sequentially
    consistent read-modify-write, even though the retired list is empty
    almost every time; a plain load took over that case and the drain
    itself moved out of line. key in md got about 10% cheaper,
    md[key] about 8% and a missing md.get(key) about 5%, measured as
    instruction counts on CPython 3.14. Builds with the GIL enabled emit
    byte-identical code and are unaffected -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1546.

  • Added a watchers C API, so another C extension can be notified when a
    :class:~multidict.MultiDict or :class:~multidict.CIMultiDict changes.
    It follows CPython's :c:func:!PyDict_AddWatcher family, with two
    differences: the callback is passed two context pointers, one fixed per
    registered callback and one per watched multidict, and events are
    delivered once the operation that produced them has finished rather than
    mid-mutation. The events are multi-value aware, and each one that concerns
    a key carries the identity and its hash alongside it. Up to
    :c:macro:MULTIDICT_MAX_WATCHERS (32) watchers can be registered, and a
    failing callback is reported through :func:sys.unraisablehook with the
    event and the multidict's type and address, as CPython does for dict
    watchers -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1551.

  • Sped up lookups, insertions and deletions in the C extension by always
    inlining the hash-table probe's initializer. Left to itself the compiler
    emitted it out of line, so every probe opened with a call for five
    stores. Free-threaded builds already inlined it and are unchanged
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1554.

  • Sped up every method call on :class:~multidict.CIMultiDict and
    :class:~multidict.CIMultiDictProxy in the C extension by binding
    their method descriptors to the class itself instead of inheriting
    them from :class:~multidict.MultiDict and
    :class:~multidict.MultiDictProxy; CPython only specializes a
    method call when the descriptor belongs to the exact type of the
    receiver, so every such call was taking the generic path
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1567.

  • Made :meth:~multidict.MultiDict.popitem in the C extension drop the
    trailing deleted entries it walked over, so that popping every item
    out of a mapping costs linear time rather than quadratic; the
    pure-Python implementation already did this -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1568.

  • Sped up iterating over :meth:~multidict.MultiDict.items in the C
    extension by handing out the same (key, value) tuple again when
    the caller has already let go of it, as for key, value in d.items() does on every step; a tuple the caller keeps is left
    alone and a new one is used from then on -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1569.

  • Sped up lookups on the free-threaded build by comparing the hash before
    touching an entry's key and by matching a key that is the very same
    object as the one looked up without taking a reference to it, so the
    common hit costs two fewer atomic operations -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1570.

  • Sped up mutations on the free-threaded build when several threads
    mutated multidicts at the same time: each thread reserved version
    numbers in batches instead of bumping one counter shared by every
    thread on each mutation, so with six threads a d[key] = value was
    about four times faster -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1587, :issue:1599.

  • Sped up the C extension by keeping the table resize on the insert path and
    the operations behind Python slots out of line, so they stopped crowding the
    hot paths out of the compiler's inlining budget. On GIL builds, building a
    multidict from 200 pairs got about 4% faster in instruction count and
    inserting a new key about 2%; on free-threaded builds, key lookups got up to
    5% faster -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1600, :issue:1601.

Removals and backward incompatible breaking changes

  • Made :class:~multidict.istr a final class to disallow subclassing in both
    the C extension and pure-Python implementations -- by :user:agustin18.

    Related issues and pull requests on GitHub:
    :issue:1535.

Improved documentation

  • Documented how :class:~multidict.MultiDict and
    :class:~multidict.CIMultiDict compare with :class:dict on both the
    default and the free-threaded builds, with per-operation measurements,
    and how to run the benchmarks in a stable environment
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1532.

  • Documented what :class:~multidict.istr keys are worth on a
    :class:~multidict.CIMultiDict, as instruction counts next to the
    equivalent plain :class:str keys. The advice to create them once and
    reuse them was already there; the numbers behind it were not
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1547.

  • Documented that a few rows of the benchmarking reference are measured on
    an empty or 20-item mapping rather than the 200-item one the section
    describes -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1558.

  • Mentioned in the README and docs index that multidict is optimized for
    both the GIL and free-threaded builds, and that its performance is typically
    within 20-30% of :class:dict for common operations, with a link to the
    benchmarks page for the full breakdown -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1559.

  • Fixed the C extension's popone() and pop() docstrings, which
    claimed the last occurrence of the key was removed; the first one is,
    as on the pure-Python backend. Added a note on rejecting duplicate
    values for keys that must carry exactly one, such as security-sensitive
    HTTP headers -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1578.

  • Added a C API example of a watcher that invalidates a cache: it unwatches
    from its own callback, watches again on the next rebuild, and gives up
    on a multidict that keeps changing -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1586.

  • Refreshed the tables in the benchmarking reference for the 7.0.0 release,
    and replaced the range quoted for the pure-Python backend with its average
    slowdown -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1602.

Packaging updates and notes for downstreams

  • Removed the C extension code path that existed only for free-threaded
    Python 3.13, whose support was dropped in v6.8.0. The free-threaded
    build started requiring CPython 3.14 or newer and failing with a clear
    compiler error on older free-threaded interpreters; builds with the GIL
    enabled were unaffected and still go down to Python 3.10
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1524.

Contributor-facing changes

  • Added CodSpeed benchmarks for getall() on a large table, update()
    with duplicate keys, merge(), to_dict() and items view containment
    with duplicate keys -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1506.

  • Added CodSpeed benchmarks for getall() on keys with many values, both
    in a small table and in a table large enough to need a heap allocated
    bitmap -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1509.

  • Moved the compiler hint macros used by the C extension out of
    htkeys.h into compiler.h and dropped the HT_ prefix from
    their names -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1510.

  • Moved the debug-only ASSERT_CONSISTENT macro and the
    _md_check_consistency() and _md_dump() helpers used by the C
    extension out of hashtable.h into their own debug.h header
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1513.

  • Added CodSpeed benchmarks for CIMultiDict keyed by plain str instead
    of istr, covering already-lowercase, mixed-case and non-ASCII keys
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1521.

  • Documented that the pure-Python test leg needs --no-c-extensions
    alongside MULTIDICT_NO_EXTENSIONS=1; the environment variable picks
    the backend that gets imported, but only the flag deselects the
    C-extension half of the test matrix -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1523.

  • Documented how to run the Hypothesis property tests, which a default
    pytest run excludes via the -m "not hypothesis" marker
    expression in pytest.ini and whose pin make install-dev does
    not install -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1525.

  • Added tools/codegen_diff.py, which reports the functions GCC emits
    differently before and after a change to the C extension. Addresses,
    RIP-relative displacements and branch offsets inside a symbol are
    normalized away first, so a single instruction-length change no longer
    makes every later function look modified, while constants and structure
    offsets are left alone so that a real change is not folded away. A tree
    that fails to build is reported by name rather than compared
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1529.

  • Fixed benchmarks/istr.py to import pyperf under its current
    name; it had been failing to start since the package was renamed from
    perf -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1531.

  • Rewrote the benchmark scripts around a single registry of benchmarked
    operations, and added a Callgrind driver that records deterministic
    instruction counts per operation. The measurements of destructive
    operations such as pop() and clear() stopped counting the cost
    of rebuilding the mapping -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1532.

  • Moved the bulk extend(), update() and merge() paths of the C
    extension out of hashtable.h into their own bulk_update.h
    header, leaving hashtable.h to the table itself and the single-key
    operations -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1533.

  • Moved the two list-reading helpers behind itemsview.__contains__() and
    the extend()/update()/merge() sequence parser out of
    hashtable.h and into a new multidict/_multilib/unpack.h, together with
    a shared unpack_pair() that both call to read a (key, value)
    pair out of an exact two-element tuple or list. C extension only; the pure
    Python implementation has no header layout to mirror
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1534.

  • Added a MULTIDICT_NO_FREELIST=1 build option that stops the C
    extension reusing freed blocks. A reused block never reaches free(),
    so AddressSanitizer can neither poison it nor report a use-after-free on
    it; an instrumented run wants a second pass with this set, as
    AGENTS.md describes.

    Also added a private _freelist_clear() to the C extension, for tests
    that inject an allocation failure: a reused block lets an operation run
    without calling the allocator at all, which would put the recovery path
    out of reach -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1539.

  • Added a visitor to the public C API test harnesses that mutates the multidict
    it is walking, covering the reentrancy guard in MultiDict_ForEach() from
    both multidict._testcapi and multidict._testcyapi
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1544.

  • Added benchmarks for the shapes where an allocation is most of the work:
    an empty mapping, a 20-item one built or copied, a view or an iterator
    taken on its own, and a proxy. The allocation an operation makes is a
    fixed cost, so at the existing 200-item size it was divided across 200
    entries and all but vanished; d.items() measured 1.7% faster where
    it is 46% faster -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1556.

  • Sorted docs/spelling_wordlist.txt case-insensitively, and documented
    in AGENTS.md that it is to be re-sorted rather than appended to. The
    list had grown in append order, which made a word hard to find and put
    every PR adding one on the same last line -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1557.

  • Stopped asking every performance pull request to regenerate the tables in
    the benchmarking reference, since each run shifts every row slightly and the
    churn hides the rows that moved, and moved the refresh to once per release.
    Added :file:RELEASE.md, which documents the release procedure, including
    that refresh -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1564.

  • Renamed the C extension's internal helpers in the _multilib headers
    so that a leading underscore marks exactly the functions used only
    within their own header; type slots and method callbacks keep their
    names -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1571.

  • Spelled out in AGENTS.md that every verb in a news fragment is
    past tense, and added a bad and good example pair plus a matching
    "Things not to do" entry -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1588.

  • Changed the release workflow to rewrite the Sphinx roles of the changelog
    section as Markdown before publishing it as the GitHub Release body, so
    that class, method and exception references, contributor handles and
    documentation links render there instead of showing as raw markup
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1596.

Miscellaneous internal changes

  • Changed the internal htkeys_build_indices() helper in the C
    extension to return void; it cannot fail, so both callers were
    checking for an error that never occurred -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1516.

  • Routed every field of the C extension's hash table that more than one
    thread can reach through a named accessor in the new
    _multilib/freethreading.h header, instead of each call site choosing
    between an atomic and a plain access behind #ifdef Py_GIL_DISABLED.
    Each accessor kept the operation and memory ordering its call sites
    already used, so neither build changed behavior -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1518.

  • Removed get_mod_state_by_cls(), an unused helper in the C extension
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1520.

  • Documented why md_clear() keeps clearing entry fields with
    Py_CLEAR() on the GIL build rather than routing them through
    _multilib/freethreading.h: Py_CLEAR() skips the store when a
    field is already NULL, so no accessor there is a substitute for it
    -- by :user:asvetlov.

    Related issues and pull requests on GitHub:
    :issue:1526.

  • Collapsed the two #ifdef Py_GIL_DISABLED arms that fill in a freshly
    inserted hash table entry in the C extension into one. Both builds started
    writing the entry's fields in the order the free-threaded build needs, and
    publishing the value witho

❗ Important

✂ PR body was truncated to here.


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate-bot
renovate-bot requested a review from a team as a code owner October 9, 2026 17:48
@trusted-contributions-gcf trusted-contributions-gcf Bot added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Oct 9, 2026
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Oct 9, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the multidict dependency in packages/gapic-generator/requirements.txt from version 6.9.1 to 7.1.0, along with its corresponding hashes. There are no review comments, and I have no feedback to provide.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants