• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

aio-libs / multidict / 36012420435
87%

Build:
DEFAULT BRANCH: master
Ran 24 Sep 2026 02:25PM UTC
Jobs 1
Files 43
Run time 1min
Badge
Embed ▾
README BADGES
x

If you need to use a raster PNG badge, change the '.svg' to '.png' in the link

Markdown

Textile

RDoc

HTML

Rst

24 Sep 2026 02:23PM UTC coverage: 87.629% (-0.03%) from 87.662%
36012420435

push

github

web-flow
Bind the CIMultiDict method descriptors to the type itself (#1567)

<!-- Thank you for your contribution! -->

## What do these changes do?

`CIMultiDict` and `CIMultiDictProxy` had no `Py_tp_methods` slot of
their own, so their method descriptors were the ones inherited from
`MultiDict` and `MultiDictProxy`: `CIMultiDict.get.__objclass__ is
MultiDict`. CPython's `CALL_METHOD_DESCRIPTOR_*` specializations guard
on the receiver being exactly the descriptor's `d_type`, so every
method call on a `CIMultiDict` instance deopted to the generic call
path (`method_vectorcall_*`, `_Py_CheckFunctionResult`, the stack-ref
array copy and a TLS lookup), about 250 instructions and 10 to 15 ns
per call. Slot-dispatched operations such as `d[key]`, `key in d` and
`iter(d)` were never affected, which is why the published tables
showed `CIMultiDict` 1.6x to 1.8x `dict` on `get()`, `pop()`,
`setdefault()`, `items()` and `clear()` but only 1.3x on `d[key]`.

Both types now list the same `PyMethodDef` table as their base;
`PyType_FromSpec` creates descriptors bound to each type from it.
`CIMultiDictProxy` previously carried a one-entry table overriding
`copy()`; that override called the same `multidict_copy(self->md)` as
the base, so it is dropped rather than merged.

Only the C extension changes. The pure-Python classes define their
methods as Python functions, which specialize on the function object,
not on the receiver type.

Instruction counts per operation from `benchmarks/callgrind_driver.py`,
CPython 3.14.7, `PYTHONHASHSEED=0`, before and after on this branch's
merge base:

| operation, `CIMultiDict` | GIL before | GIL after | | FT before | FT
after | |
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
| `d.get(key)`, miss | 582 | 392 | -33% | 660 | 484 | -27% |
| `d.setdefault(key, v)`, new key | 784 | 594 | -24% | 970 | 794 | -18%
|
| `d.pop(key)` | 672 | 481 | -28% | 775 | 598 | -23% |
| `d.add(key, v)` | 765 | 576 | -25% | 939 | 763 | -19% |
| `d.add(istr, v)` ... (continued)

775 of 1550 branches covered (50.0%)

Branch coverage included in aggregate %.

10 of 15 new or added lines in 1 file covered. (66.67%)

7647 of 8061 relevant lines covered (94.86%)

1.9 hits per line

Uncovered Changes

Lines Coverage ∆ File
5
49.12
2.66% tests/test_types.py
Jobs
ID Job ID Ran Files Coverage
1 MyPy - 36012420435.1 24 Sep 2026 02:25PM UTC 86
87.63
GitHub Action Run
Source Files on build 36012420435
  • Tree
  • List 43
  • Changed 3
  • Source Changed 1
  • Coverage Changed 3
Coverage ∆ File Lines Relevant Covered Missed Hits/Line Branch Hits Branch Misses
  • Back to Repo
  • Github Actions Build #36012420435
  • b9cd6475 on github
  • Prev Build on master (#36010336083)
  • Next Build on master (#36018545646)
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc