Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
1db2d00
fix ref bug with iter, views and istr
Vizonex Mar 28, 2026
f1c199f
add bugfix to timeline
Vizonex Mar 28, 2026
8756a8c
fix crash
Vizonex Mar 28, 2026
ac7e50e
fix crash
Vizonex Mar 28, 2026
b105a4a
Merge branch 'master' into iterator-ref-leak
bdraco Mar 29, 2026
81aa7b7
add type leak test to testsuite
Vizonex Mar 30, 2026
7e0f887
Merge branch 'iterator-ref-leak' of https://github.com/Vizonex/multid…
Vizonex Mar 30, 2026
ec06238
rather than stick to python's dictionary traditions try freeing the t…
Vizonex Mar 31, 2026
5d7d955
cast str object
Vizonex Mar 31, 2026
5faf6d6
fix tests must've been mistakeful programming
Vizonex Mar 31, 2026
731c82d
turns out freethreaded mode cleans up more types so use a lt operator…
Vizonex Mar 31, 2026
acbe739
add a test back in seems it was missing on accident.
Vizonex Mar 31, 2026
1d0b323
try to make code-coverage little bit more happy
Vizonex Apr 2, 2026
2c11dd9
fix ranges in code coverage
Vizonex Apr 2, 2026
064710a
Merge branch 'master' into iterator-ref-leak
Vizonex Apr 3, 2026
d8093f7
add #1314 to timeline
Vizonex Apr 3, 2026
c06e3a0
Delete CHANGES/1314.feature.rst WRONG BRANCH SORRY :(
Vizonex Apr 3, 2026
3eb26a1
Merge branch 'master' into iterator-ref-leak
bdraco Apr 11, 2026
e777b36
fix missing self traverse
bdraco Apr 11, 2026
f0ede3d
cleanup
bdraco Apr 11, 2026
a9007d7
0 leaks allowed, skip on ft
bdraco Apr 11, 2026
324d624
items values as well
bdraco Apr 11, 2026
f94782d
Merge branch 'master' into iterator-ref-leak
bdraco Apr 13, 2026
9ff2c61
newline
bdraco Apr 13, 2026
f94b029
Merge branch 'master' into iterator-ref-leak
bdraco Apr 13, 2026
959a16b
Merge branch 'master' into iterator-ref-leak
Vizonex Apr 14, 2026
c7d3d1d
Merge branch 'master' into iterator-ref-leak
bdraco Apr 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES/1311.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fixed reference leak in iterators, views and ``istr``
-- by :user:`Vizonex`.
5 changes: 4 additions & 1 deletion multidict/_multilib/istr.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@ PyDoc_STRVAR(istr__doc__, "istr class implementation");
static inline void
istr_dealloc(istrobject *self)
{
PyTypeObject *tp = Py_TYPE(self);
Py_XDECREF(self->canonical);
PyUnicode_Type.tp_dealloc((PyObject *)self);
PyUnicode_Type.tp_dealloc((PyObject *)(&self->str));
/* Clear it because sometimes this might appear NULL */
Comment thread
bdraco marked this conversation as resolved.
Outdated
Py_CLEAR(tp);
}

static inline PyObject *
Expand Down
5 changes: 4 additions & 1 deletion multidict/_multilib/iter.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,11 @@ multidict_keys_iter_iternext(MultidictIter *self)
static inline void
multidict_iter_dealloc(MultidictIter *self)
{
PyTypeObject *tp = Py_TYPE(self);
PyObject_GC_UnTrack(self);
Py_XDECREF(self->md);
PyObject_GC_Del(self);
tp->tp_free(self);
Py_DECREF(tp);
}

static inline int
Expand Down Expand Up @@ -221,6 +223,7 @@ static PyType_Slot multidict_keys_iter_slots[] = {
{Py_tp_clear, multidict_iter_clear},
{Py_tp_iter, PyObject_SelfIter},
{Py_tp_iternext, multidict_keys_iter_iternext},
{Py_tp_free, PyObject_GC_Del},
{0, NULL},
};

Expand Down
5 changes: 4 additions & 1 deletion multidict/_multilib/views.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ _init_view(_Multidict_ViewObject *self, MultiDictObject *md)
static inline void
multidict_view_dealloc(_Multidict_ViewObject *self)
{
PyTypeObject *tp = Py_TYPE(self);
PyObject_GC_UnTrack(self);
Py_XDECREF(self->md);
PyObject_GC_Del(self);
tp->tp_free(self);
Py_DECREF(tp);
}

static inline int
Expand Down Expand Up @@ -1678,6 +1680,7 @@ static PyType_Slot multidict_valuesview_slots[] = {
{Py_tp_traverse, multidict_view_traverse},
{Py_tp_clear, multidict_view_clear},
{Py_tp_iter, multidict_valuesview_iter},
{Py_tp_free, PyObject_GC_Del},
{0, NULL},
};

Expand Down
35 changes: 35 additions & 0 deletions tests/isolated/multidict_type_leak.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from multidict import MultiDict, istr
import sys

md = MultiDict([("a", "1"), ("b", "2")])

if __name__ == "__main__":
# XXX: Code Coverage misbehaves so do this instead
it = iter(md.keys())
iter_type = type(it)
del it
baseline = sys.getrefcount(iter_type)
for _ in range(1000):
it = iter(md.keys())
list(it)
del it
after = sys.getrefcount(iter_type)

# NOTE: On Freethreaded Mode this value can be negative
assert (after - baseline) <= 0, "iterator type leaked"

# Test view type leak
view_type = type(md.keys())
baseline = sys.getrefcount(view_type)
for _ in range(1000):
v = md.keys()
Comment thread Fixed
del v
after = sys.getrefcount(view_type)
assert (after - baseline) <= 0, "view type leaked"

baseline = sys.getrefcount(istr)
for _ in range(1000):
s = istr("hello")
Comment thread Fixed
del s
after = sys.getrefcount(istr)
assert (after - baseline) <= 0, "istr type leaked"
1 change: 1 addition & 0 deletions tests/test_leaks.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"multidict_extend_multidict.py",
"multidict_extend_tuple.py",
"multidict_update_multidict.py",
"multidict_type_leak.py",
"multidict_pop.py",
),
)
Expand Down
Loading