From 80449f243b13311d660eab3a751648029bcdd833 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Thu, 5 Nov 2020 09:23:15 +0000 Subject: [PATCH] bpo-42266: Handle monkey-patching descriptors in LOAD_ATTR cache (GH-23157) --- Lib/test/test_opcache.py | 23 +++++++++++++++++++ .../2020-11-04-23-03-25.bpo-42266.G4hGDe.rst | 3 +++ PCbuild/lib.pyproj | 1 + Python/ceval.c | 8 +------ 4 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 Lib/test/test_opcache.py create mode 100644 Misc/NEWS.d/next/Core and Builtins/2020-11-04-23-03-25.bpo-42266.G4hGDe.rst diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py new file mode 100644 index 00000000000..61f337d70ea --- /dev/null +++ b/Lib/test/test_opcache.py @@ -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) diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-11-04-23-03-25.bpo-42266.G4hGDe.rst b/Misc/NEWS.d/next/Core and Builtins/2020-11-04-23-03-25.bpo-42266.G4hGDe.rst new file mode 100644 index 00000000000..a8598cfde04 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-11-04-23-03-25.bpo-42266.G4hGDe.rst @@ -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. diff --git a/PCbuild/lib.pyproj b/PCbuild/lib.pyproj index a15165d92ef..1be60b1a11b 100644 --- a/PCbuild/lib.pyproj +++ b/PCbuild/lib.pyproj @@ -1196,6 +1196,7 @@ + diff --git a/Python/ceval.c b/Python/ceval.c index 13b209fc706..32e3019682f 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3179,7 +3179,6 @@ main_loop: 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) { @@ -3190,12 +3189,7 @@ main_loop: 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;