Repository navigation
7.2.0 drops Python 3.10, fixes a crash in a proxy built without
__init__() and a MemoryError from a huge __length_hint__(),
and is otherwise a performance release for the C extension: lookups,
adds, updates and deletes each needed a few percent fewer instructions.
Bug fixes
-
Stopped a huge
__length_hint__()from raisingMemoryErrorin
extend(),update()
andmerge(); the C extension capped the room
it reserved for the hint at the largest table the constructor allocates
up front -- by @asvetlov.Related issues and pull requests on GitHub:
#1767. -
Fixed a segmentation fault when using a
MultiDictProxy
orCIMultiDictProxybuilt by__new__()without
__init__();__new__()started requiring the proxied multidict, the
same argument as the constructor -- by @asvetlov.Related issues and pull requests on GitHub:
#1782.
Features
-
Sped up reading a multidict in the C extension.
md[key],
get()andgetone()
became up to 7% faster by returning the lookup's result by value instead
of through a pointer to the caller's local, which had made
-fstack-protector-strongadd a stack canary to every call. On the GIL
build aget()miss became 5% faster and
key in mdinlined its hash table lookup. On the free-threaded build
getall()stopped taking the multidict's lock
for a multidict without repeated keys, the way
get()already avoided it.len(md.keys())
and iterating over the keys of a multidict that holds a key more than
once became 4% to 13% faster -- by @asvetlov.Related issues and pull requests on GitHub:
#1775, #1776, #1781, #1800, #1817. -
Sped up building and updating a multidict in the C extension. On the GIL
build, constructing a multidict from a sequence of pairs needed 8% to 10%
fewer instructions andupdate()3.5% to 4.5%
fewer. On the free-threaded build, building a
MultiDictbecame up to 14% faster, since a table that
is both full and unable to hold the new key in its compact form was grown
and moved in one rebuild, andadd(),
md[key] = valuefor a new key and
setdefault()needed 2% to 3% fewer
instructions, about 1% fewer on the GIL build. Updating a multidict with
existing keys needed 1.5% to 2.9% fewer instructions, while adding a new
key needed about 0.5% more on the GIL build -- by @asvetlov.Related issues and pull requests on GitHub:
#1763, #1774, #1780, #1785, #1802. -
Sped up replacing and removing items in the C extension.
add(),getall(),
md[key] = valueanddel md[key]stopped computing the identity
of a key that, or whoseistrcanonical form, already
serves as one, asmd[key]did, and checked a
CIMultiDictkey holding uppercase letters only once.
On the GIL builddel md[key]needed 4% to 5% fewer instructions,
md[key] = valueabout 2% fewer andpop()
1% fewer. Replacing a key that had duplicates needed 0.8% to 1.7% fewer
instructions, andclear()became 6% faster on
the free-threaded build -- by @asvetlov.Related issues and pull requests on GitHub:
#1769, #1770, #1779, #1796, #1809.
Removals and backward incompatible breaking changes
-
Dropped support for Python 3.10 as it has reached end of life
-- by @asvetlov.Related issues and pull requests on GitHub:
#1777.
Improved documentation
-
Refreshed the instruction-count tables in the benchmarking reference
against the current tree -- by @asvetlov.
Packaging updates and notes for downstreams
-
Added the CPython 3.15 trove classifier and started testing the
GIL and free-threaded 3.15 builds in CI -- by @asvetlov.Related issues and pull requests on GitHub:
#1766.
Contributor-facing changes
-
Split the release publishing and signing across four parallel jobs, so
that each one finishes well within the lifetime of its short-lived PyPI
upload token -- by @asvetlov. -
Changed the
clear()benchmarks to clear a thousand prebuilt
multidicts of 0 to 99 items each, copied outside the measured region,
since the copy inside the timed loop and the single small table it
measured made the result swing by up to 19% between runs of the same
commit -- by @asvetlov.Related issues and pull requests on GitHub:
#1784. -
Pinned Hypothesis in the MyPy pre-commit hooks and dropped a
type: ignorecomment from the Hypothesis tests that became unused
once Hypothesis annotatedrun_state_machine_as_test(), which failed
the MyPy lint job -- by @asvetlov.Related issues and pull requests on GitHub:
#1820. -
Fixed a test of a huge
__length_hint__()failing on 32-bit builds,
where its2**40hint did not fit aPy_ssize_t
-- by @asvetlov.Related issues and pull requests on GitHub:
#1821.
Miscellaneous internal changes
-
Split the C extension's helpers per table kind, so that the lookup
loops ofpop(),popall(), item replacement andupdate(), the
table rebuild and the deferred delete of an entry call the helper for
their own kind instead of testing it again, and moved the choice between
overwriting and dropping each entry of a repeated key into the per-kind
loops ofupdate(). Folded helpers with a single caller into that
caller: choosing the kind of a new table, reading a compact entry's
identity and hash, filling a new entry, replacing a key's values,
deleting a key and looking up the first value of a key that needs a
computed identity. Merged the two per-kind checks for a deleted entry
into one, and removed the separate watched copies of delete, pop and
replace, folding the add that follows a missed replace into the replace
itself -- by @asvetlov.Related issues and pull requests on GitHub:
#1771, #1772, #1773, #1786, #1788,
#1789, #1790, #1792, #1804, #1805,
#1810, #1811. -
Removed duplicated code from the C extension: the
(key, default)
methods shared their argument parsing and missing-key result, and one
macro each replaced the copies of the lock-free compact entry match on
the free-threaded build and of the per-kind dispatch of a single key
lookup. Turned the watcher record, flush and pending check helpers and
_growth_rate()into macros, so that a multidict without watchers
skipped evaluating the event arguments, renamed the lowercase
function-like macros to upper case, and dropped the leading underscore
from the watcher functions that the macros call from other headers
-- by @asvetlov.Related issues and pull requests on GitHub:
#1793, #1794, #1795, #1806, #1807,
#1808, #1812. -
Adjusted how the C extension spends GCC's inlining budget: marked most
checks that lead to a raised exception as unlikely, so the compiler
moves the error paths out of the hot code, kept the event name lookup of
a failing watcher callback report and the cleanup of a dropped watcher
event log out of line, and dropped theinlinehint from the helpers
that compute a table's size and that rebuild, build or free a table's
keys -- by @asvetlov.Related issues and pull requests on GitHub:
#1783, #1791, #1801, #1815. -
Moved the C extension's
repr()of multidicts, proxies and views and
its equality comparison out of the hash table header, each into a header
of its own -- by @asvetlov.