Skip to content

Commit

Permalink
bpo-42266: Handle monkey-patching descriptors in LOAD_ATTR cache (GH-…
Browse files Browse the repository at this point in the history
  • Loading branch information
pablogsal authored Nov 5, 2020
1 parent 178695b commit 80449f2
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 7 deletions.
23 changes: 23 additions & 0 deletions Lib/test/test_opcache.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import unittest

class TestLoadAttrCache(unittest.TestCase):
def test_descriptor_added_after_optimization(self):
class Descriptor:
pass

class C:
def __init__(self):
self.x = 1
x = Descriptor()

def f(o):
return o.x

o = C()
for i in range(1025):
assert f(o) == 1

Descriptor.__get__ = lambda self, instance, value: 2
Descriptor.__set__ = lambda *args: None

self.assertEqual(f(o), 2)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fixed a bug with the LOAD_ATTR opcode cache that was not respecting
monkey-patching a class-level attribute to make it a descriptor. Patch by
Pablo Galindo.
1 change: 1 addition & 0 deletions PCbuild/lib.pyproj
Original file line number Diff line number Diff line change
Expand Up @@ -1196,6 +1196,7 @@
<Compile Include="test\test_nntplib.py" />
<Compile Include="test\test_ntpath.py" />
<Compile Include="test\test_numeric_tower.py" />
<Compile Include="test\test_opcache.py" />
<Compile Include="test\test_opcodes.py" />
<Compile Include="test\test_openpty.py" />
<Compile Include="test\test_operator.py" />
Expand Down
8 changes: 1 addition & 7 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -3179,7 +3179,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
if (co_opcache != NULL && /* co_opcache can be NULL after a DEOPT() call. */
type->tp_getattro == PyObject_GenericGetAttr)
{
PyObject *descr;
Py_ssize_t ret;

if (type->tp_dictoffset > 0) {
Expand All @@ -3190,12 +3189,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
goto error;
}
}

descr = _PyType_Lookup(type, name);
if (descr == NULL ||
Py_TYPE(descr)->tp_descr_get == NULL ||
!PyDescr_IsData(descr))
{
if (_PyType_Lookup(type, name) == NULL) {
dictptr = (PyObject **) ((char *)owner + type->tp_dictoffset);
dict = *dictptr;

Expand Down

0 comments on commit 80449f2

Please sign in to comment.