Skip to content

Commit 93f05ba

Browse files
committed
bpo-42266: Handle monkey-patching descriptors in LOAD_ATTR cache
1 parent 789359f commit 93f05ba

File tree

4 files changed

+28
-7
lines changed

4 files changed

+28
-7
lines changed

Lib/test/test_opcache.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import unittest
2+
3+
class TestLoadAttrCache(unittest.TestCase):
4+
def test_descriptor_added_after_optimization(self):
5+
class Descriptor:
6+
pass
7+
8+
class C:
9+
def __init__(self):
10+
self.x = 1
11+
x = Descriptor()
12+
13+
def f(o):
14+
return o.x
15+
16+
o = C()
17+
for i in range(1025):
18+
assert f(o) == 1
19+
20+
Descriptor.__get__ = lambda self, instance, value: 2
21+
Descriptor.__set__ = lambda *args: None
22+
23+
self.assertEqual(f(o), 2)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fixed a bug with the LOAD_ATTR opcode cache that was not respecting
2+
monkey-patching a class-level attribute to make it a descriptor. Patch by
3+
Pablo Galindo.

PCbuild/lib.pyproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1196,6 +1196,7 @@
11961196
<Compile Include="test\test_nntplib.py" />
11971197
<Compile Include="test\test_ntpath.py" />
11981198
<Compile Include="test\test_numeric_tower.py" />
1199+
<Compile Include="test\test_opcache.py" />
11991200
<Compile Include="test\test_opcodes.py" />
12001201
<Compile Include="test\test_openpty.py" />
12011202
<Compile Include="test\test_operator.py" />

Python/ceval.c

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3179,7 +3179,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
31793179
if (co_opcache != NULL && /* co_opcache can be NULL after a DEOPT() call. */
31803180
type->tp_getattro == PyObject_GenericGetAttr)
31813181
{
3182-
PyObject *descr;
31833182
Py_ssize_t ret;
31843183

31853184
if (type->tp_dictoffset > 0) {
@@ -3190,12 +3189,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
31903189
goto error;
31913190
}
31923191
}
3193-
3194-
descr = _PyType_Lookup(type, name);
3195-
if (descr == NULL ||
3196-
Py_TYPE(descr)->tp_descr_get == NULL ||
3197-
!PyDescr_IsData(descr))
3198-
{
3192+
if (_PyType_Lookup(type, name) == NULL) {
31993193
dictptr = (PyObject **) ((char *)owner + type->tp_dictoffset);
32003194
dict = *dictptr;
32013195

0 commit comments

Comments
 (0)