Skip to content

Commit 1d36a9c

Browse files
committed
Update backported code for 3.11 specifically
1 parent c0ba6b4 commit 1d36a9c

File tree

3 files changed

+71
-115
lines changed

3 files changed

+71
-115
lines changed

Lib/test/test_type_cache.py

Lines changed: 64 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,16 @@
1212
_testcapi = import_helper.import_module("_testcapi")
1313
type_get_version = _testcapi.type_get_version
1414
type_assign_specific_version_unsafe = _testcapi.type_assign_specific_version_unsafe
15-
type_assign_version = _testcapi.type_assign_version
1615
type_modified = _testcapi.type_modified
1716

1817

18+
def type_assign_version(type_):
19+
try:
20+
type_.x
21+
except AttributeError:
22+
pass
23+
24+
1925
@support.cpython_only
2026
@unittest.skipIf(_clear_type_cache is None, "requires sys._clear_type_cache")
2127
class TypeCacheTests(unittest.TestCase):
@@ -47,19 +53,6 @@ def test_tp_version_tag_unique(self):
4753
self.assertEqual(len(set(all_version_tags)), 30,
4854
msg=f"{all_version_tags} contains non-unique versions")
4955

50-
def test_type_assign_version(self):
51-
class C:
52-
x = 5
53-
54-
self.assertEqual(type_assign_version(C), 1)
55-
c_ver = type_get_version(C)
56-
57-
C.x = 6
58-
self.assertEqual(type_get_version(C), 0)
59-
self.assertEqual(type_assign_version(C), 1)
60-
self.assertNotEqual(type_get_version(C), 0)
61-
self.assertNotEqual(type_get_version(C), c_ver)
62-
6356
def test_type_assign_specific_version(self):
6457
"""meta-test for type_assign_specific_version_unsafe"""
6558
class C:
@@ -108,134 +101,103 @@ def _check_specialization(self, func, arg, opname, *, should_specialize):
108101
else:
109102
self.assertIn(opname, self._all_opnames(func))
110103

111-
def test_class_load_attr_specialization_user_type(self):
104+
def test_load_method_specialization_user_type(self):
112105
class A:
113106
def foo(self):
114107
pass
115108

116109
self._assign_valid_version_or_skip(A)
117110

118-
def load_foo_1(type_):
119-
type_.foo
111+
def load_foo_1(instance):
112+
instance.foo()
120113

121-
self._check_specialization(load_foo_1, A, "LOAD_ATTR", should_specialize=True)
114+
self._check_specialization(
115+
load_foo_1, A(), "LOAD_METHOD_ADAPTIVE", should_specialize=True
116+
)
122117
del load_foo_1
123118

124119
self._assign_and_check_version_0(A)
125120

126-
def load_foo_2(type_):
127-
return type_.foo
128-
129-
self._check_specialization(load_foo_2, A, "LOAD_ATTR", should_specialize=False)
130-
131-
def test_class_load_attr_specialization_static_type(self):
132-
self._assign_valid_version_or_skip(str)
133-
self._assign_valid_version_or_skip(bytes)
134-
135-
def get_capitalize_1(type_):
136-
return type_.capitalize
137-
138-
self._check_specialization(get_capitalize_1, str, "LOAD_ATTR", should_specialize=True)
139-
self.assertEqual(get_capitalize_1(str)('hello'), 'Hello')
140-
self.assertEqual(get_capitalize_1(bytes)(b'hello'), b'Hello')
141-
del get_capitalize_1
142-
143-
# Permanently overflow the static type version counter, and force str and bytes
144-
# to have tp_version_tag == 0
145-
for _ in range(2**16):
146-
type_modified(str)
147-
type_assign_version(str)
148-
type_modified(bytes)
149-
type_assign_version(bytes)
150-
151-
self.assertEqual(type_get_version(str), 0)
152-
self.assertEqual(type_get_version(bytes), 0)
153-
154-
def get_capitalize_2(type_):
155-
return type_.capitalize
121+
def load_foo_2(instance):
122+
instance.foo()
156123

157-
self._check_specialization(get_capitalize_2, str, "LOAD_ATTR", should_specialize=False)
158-
self.assertEqual(get_capitalize_2(str)('hello'), 'Hello')
159-
self.assertEqual(get_capitalize_2(bytes)(b'hello'), b'Hello')
160-
161-
def test_property_load_attr_specialization_user_type(self):
162-
class G:
163-
@property
164-
def x(self):
165-
return 9
166-
167-
self._assign_valid_version_or_skip(G)
168-
169-
def load_x_1(instance):
170-
instance.x
171-
172-
self._check_specialization(load_x_1, G(), "LOAD_ATTR", should_specialize=True)
173-
del load_x_1
174-
175-
self._assign_and_check_version_0(G)
176-
177-
def load_x_2(instance):
178-
instance.x
179-
180-
self._check_specialization(load_x_2, G(), "LOAD_ATTR", should_specialize=False)
124+
self._check_specialization(
125+
load_foo_2, A(), "LOAD_METHOD_ADAPTIVE", should_specialize=False
126+
)
181127

182128
def test_store_attr_specialization_user_type(self):
183129
class B:
184130
__slots__ = ("bar",)
185131

186132
self._assign_valid_version_or_skip(B)
187133

188-
def store_bar_1(type_):
189-
type_.bar = 10
134+
def store_bar_1(instance):
135+
instance.bar = 10
190136

191-
self._check_specialization(store_bar_1, B(), "STORE_ATTR", should_specialize=True)
137+
self._check_specialization(
138+
store_bar_1, B(), "STORE_ATTR_ADAPTIVE", should_specialize=True
139+
)
192140
del store_bar_1
193141

194142
self._assign_and_check_version_0(B)
195143

196-
def store_bar_2(type_):
197-
type_.bar = 10
144+
def store_bar_2(instance):
145+
instance.bar = 10
198146

199-
self._check_specialization(store_bar_2, B(), "STORE_ATTR", should_specialize=False)
147+
self._check_specialization(
148+
store_bar_2, B(), "STORE_ATTR_ADAPTIVE", should_specialize=False
149+
)
200150

201-
def test_class_call_specialization_user_type(self):
202-
class F:
151+
def test_load_attr_specialization_user_type(self):
152+
class C:
153+
__slots__ = ("biz",)
203154
def __init__(self):
204-
pass
155+
self.biz = 8
205156

206-
self._assign_valid_version_or_skip(F)
157+
self._assign_valid_version_or_skip(C)
207158

208-
def call_class_1(type_):
209-
type_()
159+
def load_biz_1(type_):
160+
type_.biz
210161

211-
self._check_specialization(call_class_1, F, "CALL", should_specialize=True)
212-
del call_class_1
162+
self._check_specialization(
163+
load_biz_1, C(), "LOAD_ATTR_ADAPTIVE", should_specialize=True
164+
)
165+
del load_biz_1
213166

214-
self._assign_and_check_version_0(F)
167+
self._assign_and_check_version_0(C)
215168

216-
def call_class_2(type_):
217-
type_()
169+
def load_biz_2(type_):
170+
type_.biz
218171

219-
self._check_specialization(call_class_2, F, "CALL", should_specialize=False)
172+
self._check_specialization(
173+
load_biz_2, C(), "LOAD_ATTR_ADAPTIVE", should_specialize=False
174+
)
220175

221-
def test_to_bool_specialization_user_type(self):
222-
class H:
223-
pass
176+
def test_binary_subscript_specialization_user_type(self):
177+
class D:
178+
def __getitem__(self, _):
179+
return 1
180+
181+
self._assign_valid_version_or_skip(D)
182+
183+
def subscript_1(instance):
184+
instance[6]
224185

225-
self._assign_valid_version_or_skip(H)
186+
self._check_specialization(
187+
subscript_1, D(), "BINARY_SUBSCR_ADAPTIVE", should_specialize=True
188+
)
189+
del subscript_1
226190

227-
def to_bool_1(instance):
228-
not instance
191+
self._assign_and_check_version_0(D)
229192

230-
self._check_specialization(to_bool_1, H(), "TO_BOOL", should_specialize=True)
231-
del to_bool_1
193+
def subscript_2(instance):
194+
instance[6]
232195

233-
self._assign_and_check_version_0(H)
196+
self._check_specialization(
197+
subscript_2, D(), "BINARY_SUBSCR_ADAPTIVE", should_specialize=False
198+
)
234199

235-
def to_bool_2(instance):
236-
not instance
237200

238-
self._check_specialization(to_bool_2, H(), "TO_BOOL", should_specialize=False)
239201

240202
if __name__ == "__main__":
241203
unittest.main()

Modules/_testcapimodule.c

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5926,17 +5926,6 @@ type_assign_specific_version_unsafe(PyObject *self, PyObject *args)
59265926
Py_RETURN_NONE;
59275927
}
59285928

5929-
static PyObject *
5930-
type_assign_version(PyObject *self, PyObject *type)
5931-
{
5932-
if (!PyType_Check(type)) {
5933-
PyErr_SetString(PyExc_TypeError, "argument must be a type");
5934-
return NULL;
5935-
}
5936-
int res = PyUnstable_Type_AssignVersionTag((PyTypeObject *)type);
5937-
return PyLong_FromLong(res);
5938-
}
5939-
59405929
// Test PyThreadState C API
59415930
static PyObject *
59425931
test_tstate_capi(PyObject *self, PyObject *Py_UNUSED(args))
@@ -6822,7 +6811,6 @@ static PyMethodDef TestMethods[] = {
68226811
{"type_modified", type_modified, METH_O, PyDoc_STR("PyType_Modified")},
68236812
{"type_assign_specific_version_unsafe", type_assign_specific_version_unsafe, METH_VARARGS,
68246813
PyDoc_STR("forcefully assign type->tp_version_tag")},
6825-
{"type_assign_version", type_assign_version, METH_O, PyDoc_STR("PyUnstable_Type_AssignVersionTag")},
68266814
{"test_tstate_capi", test_tstate_capi, METH_NOARGS, NULL},
68276815
{"float_pack", test_float_pack, METH_VARARGS, NULL},
68286816
{"float_unpack", test_float_unpack, METH_VARARGS, NULL},

Python/specialize.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -896,7 +896,7 @@ specialize_class_load_method(PyObject *owner, _Py_CODEUNIT *instr,
896896
PyObject *descr = NULL;
897897
DescriptorClassification kind = 0;
898898
kind = analyze_descriptor((PyTypeObject *)owner, name, &descr, 0);
899-
if (type_get_version((PyTypeObject *)owner, LOAD_ATTR) == 0) {
899+
if (type_get_version((PyTypeObject *)owner, LOAD_METHOD) == 0) {
900900
return -1;
901901
}
902902
switch (kind) {
@@ -960,6 +960,9 @@ _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name)
960960
PyObject *descr = NULL;
961961
DescriptorClassification kind = 0;
962962
kind = analyze_descriptor(owner_cls, name, &descr, 0);
963+
if (type_get_version(owner_cls, LOAD_METHOD) == 0) {
964+
goto fail;
965+
}
963966
assert(descr != NULL || kind == ABSENT || kind == GETSET_OVERRIDDEN);
964967
if (kind != METHOD) {
965968
SPECIALIZATION_FAIL(LOAD_METHOD, load_method_fail_kind(kind));
@@ -1253,6 +1256,9 @@ _Py_Specialize_BinarySubscr(
12531256
SPECIALIZATION_FAIL(BINARY_SUBSCR, SPEC_FAIL_WRONG_NUMBER_ARGUMENTS);
12541257
goto fail;
12551258
}
1259+
if (type_get_version(cls, BINARY_SUBSCR) == 0) {
1260+
goto fail;
1261+
}
12561262
assert(cls->tp_version_tag != 0);
12571263
write_u32(cache->type_version, cls->tp_version_tag);
12581264
int version = _PyFunction_GetVersionForCurrentState(func);

0 commit comments

Comments
 (0)