Skip to content

Commit 71ba5f5

Browse files
[3.9] bpo-42332: Add weakref slot to types.GenericAlias (GH-23250) (GH-23309)
(cherry picked from commit 384b7a4)
1 parent 48a9c0e commit 71ba5f5

File tree

3 files changed

+59
-38
lines changed

3 files changed

+59
-38
lines changed

Lib/test/test_genericalias.py

Lines changed: 52 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313
from dataclasses import Field
1414
from functools import partial, partialmethod, cached_property
1515
from mailbox import Mailbox, _PartialFile
16-
from ctypes import Array, LibraryLoader
16+
try:
17+
import ctypes
18+
except ImportError:
19+
ctypes = None
1720
from difflib import SequenceMatcher
1821
from filecmp import dircmp
1922
from fileinput import FileInput
@@ -44,45 +47,46 @@
4447

4548
class BaseTest(unittest.TestCase):
4649
"""Test basics."""
50+
generic_types = [type, tuple, list, dict, set, frozenset, enumerate,
51+
defaultdict, deque,
52+
SequenceMatcher,
53+
dircmp,
54+
FileInput,
55+
OrderedDict, Counter, UserDict, UserList,
56+
Pattern, Match,
57+
partial, partialmethod, cached_property,
58+
AbstractContextManager, AbstractAsyncContextManager,
59+
Awaitable, Coroutine,
60+
AsyncIterable, AsyncIterator,
61+
AsyncGenerator, Generator,
62+
Iterable, Iterator,
63+
Reversible,
64+
Container, Collection,
65+
Callable,
66+
Mailbox, _PartialFile,
67+
ContextVar, Token,
68+
Field,
69+
Set, MutableSet,
70+
Mapping, MutableMapping, MappingView,
71+
KeysView, ItemsView, ValuesView,
72+
Sequence, MutableSequence,
73+
MappingProxyType, AsyncGeneratorType,
74+
DirEntry,
75+
chain,
76+
TemporaryDirectory, SpooledTemporaryFile,
77+
Queue, SimpleQueue,
78+
_AssertRaisesContext,
79+
SplitResult, ParseResult,
80+
ValueProxy, ApplyResult,
81+
WeakSet, ReferenceType, ref,
82+
ShareableList, MPSimpleQueue,
83+
Future, _WorkItem,
84+
Morsel]
85+
if ctypes is not None:
86+
generic_types.extend((ctypes.Array, ctypes.LibraryLoader))
4787

4888
def test_subscriptable(self):
49-
for t in (type, tuple, list, dict, set, frozenset, enumerate,
50-
defaultdict, deque,
51-
SequenceMatcher,
52-
dircmp,
53-
FileInput,
54-
OrderedDict, Counter, UserDict, UserList,
55-
Pattern, Match,
56-
partial, partialmethod, cached_property,
57-
AbstractContextManager, AbstractAsyncContextManager,
58-
Awaitable, Coroutine,
59-
AsyncIterable, AsyncIterator,
60-
AsyncGenerator, Generator,
61-
Iterable, Iterator,
62-
Reversible,
63-
Container, Collection,
64-
Callable,
65-
Mailbox, _PartialFile,
66-
ContextVar, Token,
67-
Field,
68-
Set, MutableSet,
69-
Mapping, MutableMapping, MappingView,
70-
KeysView, ItemsView, ValuesView,
71-
Sequence, MutableSequence,
72-
MappingProxyType, AsyncGeneratorType,
73-
DirEntry,
74-
chain,
75-
TemporaryDirectory, SpooledTemporaryFile,
76-
Queue, SimpleQueue,
77-
_AssertRaisesContext,
78-
Array, LibraryLoader,
79-
SplitResult, ParseResult,
80-
ValueProxy, ApplyResult,
81-
WeakSet, ReferenceType, ref,
82-
ShareableList, MPSimpleQueue,
83-
Future, _WorkItem,
84-
Morsel,
85-
):
89+
for t in self.generic_types:
8690
if t is None:
8791
continue
8892
tname = t.__name__
@@ -289,5 +293,15 @@ def test_dir(self):
289293
for generic_alias_property in ("__origin__", "__args__", "__parameters__"):
290294
self.assertIn(generic_alias_property, dir_of_gen_alias)
291295

296+
def test_weakref(self):
297+
for t in self.generic_types:
298+
if t is None:
299+
continue
300+
tname = t.__name__
301+
with self.subTest(f"Testing {tname}"):
302+
alias = t[int]
303+
self.assertEqual(ref(alias)(), alias)
304+
305+
292306
if __name__ == "__main__":
293307
unittest.main()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
:class:`types.GenericAlias` objects can now be the targets of weakrefs.

Objects/genericaliasobject.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ typedef struct {
99
PyObject *origin;
1010
PyObject *args;
1111
PyObject *parameters;
12+
PyObject* weakreflist;
1213
} gaobject;
1314

1415
static void
@@ -17,6 +18,9 @@ ga_dealloc(PyObject *self)
1718
gaobject *alias = (gaobject *)self;
1819

1920
_PyObject_GC_UNTRACK(self);
21+
if (alias->weakreflist != NULL) {
22+
PyObject_ClearWeakRefs((PyObject *)alias);
23+
}
2024
Py_XDECREF(alias->origin);
2125
Py_XDECREF(alias->args);
2226
Py_XDECREF(alias->parameters);
@@ -593,6 +597,7 @@ PyTypeObject Py_GenericAliasType = {
593597
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
594598
.tp_traverse = ga_traverse,
595599
.tp_richcompare = ga_richcompare,
600+
.tp_weaklistoffset = offsetof(gaobject, weakreflist),
596601
.tp_methods = ga_methods,
597602
.tp_members = ga_members,
598603
.tp_alloc = PyType_GenericAlloc,
@@ -624,6 +629,7 @@ Py_GenericAlias(PyObject *origin, PyObject *args)
624629
alias->origin = origin;
625630
alias->args = args;
626631
alias->parameters = NULL;
632+
alias->weakreflist = NULL;
627633
_PyObject_GC_TRACK(alias);
628634
return (PyObject *)alias;
629635
}

0 commit comments

Comments
 (0)