Skip to content

Commit 9985889

Browse files
generatedunixname1734921407115435meta-codesync[bot]
authored andcommitted
Sync pre-release CPython 3.15 branch from GitHub (2026-08-17)
Summary: Imported python/cpython `3.15.0rc1+dev` from upstream rev [`594d11a`](https://www.github.com/python/cpython/commit/594d11a5f55fc0d9631a6f14e38887fe76180d28) (committed 2026-08-17 03:58:10+00:00). # Commit Info - Base: (`3.15.0rc1+dev`) - [`39a0217`](https://www.github.com/python/cpython/commit/39a0217217ce4f50d8908883f6b835a53692e5f6) (commit date: 2026-08-15 09:43:48+00:00) - Imported: (`3.15.0rc1+dev`) - [`594d11a`](https://www.github.com/python/cpython/commit/594d11a5f55fc0d9631a6f14e38887fe76180d28) (commit date: 2026-08-17 03:58:10+00:00) # Noteworthy file changes - Low-signal files (1 added) (NEWS.d, docs, .github) Complete list of added/removed files: https://www.internalfb.com/intern/everpaste/?color=0&handle=GIGESC6epO8rVawDAIfkEr7eKqZ0br0LAAAz Reviewed By: yoney Differential Revision: D116253574 fbshipit-source-id: ff9a47cb81a1ee09ae5cdb8cc9d2e3f7750ce8bb
1 parent 4fbf6c5 commit 9985889

3 files changed

Lines changed: 38 additions & 0 deletions

File tree

Lib/test/test_context.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,6 +1282,31 @@ def test_hamt_gc_2(self):
12821282

12831283
self.assertIsNone(ref())
12841284

1285+
def test_hamt_gc_3(self):
1286+
# gh-154535: the iterators must be tracked by the GC, otherwise a
1287+
# cycle running through one is never collected and the HAMT it
1288+
# holds -- and everything in it -- leaks.
1289+
A = HashKey(100, 'A')
1290+
1291+
container = []
1292+
h = hamt()
1293+
h = h.set(A, container)
1294+
1295+
hi = h.items()
1296+
self.assertTrue(gc.is_tracked(hi))
1297+
1298+
# Close the cycle: hi -> h -> container -> hi.
1299+
container.append(hi)
1300+
ref = weakref.ref(h)
1301+
1302+
del h, hi, container
1303+
1304+
gc.collect()
1305+
gc.collect()
1306+
gc.collect()
1307+
1308+
self.assertIsNone(ref())
1309+
12851310
def test_hamt_in_1(self):
12861311
A = HashKey(100, 'A')
12871312
AA = HashKey(100, 'A')
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Track the internal HAMT iterators, which back iteration over a
2+
:class:`contextvars.Context`, with the garbage collector. A reference cycle
3+
running through such an iterator was never collected, leaking the whole
4+
context it iterated over.

Python/hamt.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2451,6 +2451,10 @@ hamt_baseiter_tp_clear(PyObject *op)
24512451
{
24522452
PyHamtIterator *it = (PyHamtIterator*)op;
24532453
Py_CLEAR(it->hi_obj);
2454+
/* i_nodes holds borrowed pointers into the tree that hi_obj was keeping
2455+
alive, so the cursor must not be used again. A negative i_level makes
2456+
hamt_iterator_next() report I_END without touching i_nodes. */
2457+
it->hi_iter.i_level = -1;
24542458
return 0;
24552459
}
24562460

@@ -2497,6 +2501,10 @@ static Py_ssize_t
24972501
hamt_baseiter_tp_len(PyObject *op)
24982502
{
24992503
PyHamtIterator *it = (PyHamtIterator*)op;
2504+
if (it->hi_obj == NULL) {
2505+
/* tp_clear() ran on this iterator. */
2506+
return 0;
2507+
}
25002508
return it->hi_obj->h_count;
25012509
}
25022510

@@ -2517,6 +2525,7 @@ hamt_baseiter_new(PyTypeObject *type, binaryfunc yield, PyHamtObject *o)
25172525

25182526
hamt_iterator_init(&it->hi_iter, o->h_root);
25192527

2528+
PyObject_GC_Track(it);
25202529
return (PyObject*)it;
25212530
}
25222531

0 commit comments

Comments
 (0)