[3.6] bpo-25794: Fix `type.__setattr__()` for non-interned attribute names. (GH-1652) (#1673)

Based on patch by Eryk Sun.
(cherry picked from commit d896985bb2)
This commit is contained in:
Serhiy Storchaka 2017-05-20 10:23:31 +03:00 committed by GitHub
parent 2773add19a
commit 193f7e094f
3 changed files with 64 additions and 5 deletions

View File

@ -568,5 +568,32 @@ class ClassTests(unittest.TestCase):
a = A(hash(A.f)^(-1)) a = A(hash(A.f)^(-1))
hash(a.f) hash(a.f)
def testSetattrWrapperNameIntern(self):
# Issue #25794: __setattr__ should intern the attribute name
class A:
pass
def add(self, other):
return 'summa'
name = str(b'__add__', 'ascii') # shouldn't be optimized
self.assertIsNot(name, '__add__') # not interned
type.__setattr__(A, name, add)
self.assertEqual(A() + 1, 'summa')
name2 = str(b'__add__', 'ascii')
self.assertIsNot(name2, '__add__')
self.assertIsNot(name2, name)
type.__delattr__(A, name2)
with self.assertRaises(TypeError):
A() + 1
def testSetattrNonStringName(self):
class A:
pass
with self.assertRaises(TypeError):
type.__setattr__(A, b'x', None)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -10,6 +10,9 @@ What's New in Python 3.6.2 release candidate 1?
Core and Builtins Core and Builtins
----------------- -----------------
- bpo-25794: Fixed type.__setattr__() and type.__delattr__() for
non-interned attribute names. Based on patch by Eryk Sun.
- bpo-12414: sys.getsizeof() on a code object now returns the sizes - bpo-12414: sys.getsizeof() on a code object now returns the sizes
which includes the code struct and sizes of objects which it references. which includes the code struct and sizes of objects which it references.
Patch by Dong-hee Na. Patch by Dong-hee Na.

View File

@ -3075,6 +3075,7 @@ type_getattro(PyTypeObject *type, PyObject *name)
static int static int
type_setattro(PyTypeObject *type, PyObject *name, PyObject *value) type_setattro(PyTypeObject *type, PyObject *name, PyObject *value)
{ {
int res;
if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) { if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
PyErr_Format( PyErr_Format(
PyExc_TypeError, PyExc_TypeError,
@ -3082,9 +3083,35 @@ type_setattro(PyTypeObject *type, PyObject *name, PyObject *value)
type->tp_name); type->tp_name);
return -1; return -1;
} }
if (PyObject_GenericSetAttr((PyObject *)type, name, value) < 0) if (PyUnicode_Check(name)) {
return -1; if (PyUnicode_CheckExact(name)) {
return update_slot(type, name); if (PyUnicode_READY(name) == -1)
return -1;
Py_INCREF(name);
}
else {
name = _PyUnicode_Copy(name);
if (name == NULL)
return -1;
}
PyUnicode_InternInPlace(&name);
if (!PyUnicode_CHECK_INTERNED(name)) {
PyErr_SetString(PyExc_MemoryError,
"Out of memory interning an attribute name");
Py_DECREF(name);
return -1;
}
}
else {
/* Will fail in _PyObject_GenericSetAttrWithDict. */
Py_INCREF(name);
}
res = PyObject_GenericSetAttr((PyObject *)type, name, value);
if (res == 0) {
res = update_slot(type, name);
}
Py_DECREF(name);
return res;
} }
extern void extern void
@ -6929,7 +6956,7 @@ init_slotdefs(void)
/* Slots must be ordered by their offset in the PyHeapTypeObject. */ /* Slots must be ordered by their offset in the PyHeapTypeObject. */
assert(!p[1].name || p->offset <= p[1].offset); assert(!p[1].name || p->offset <= p[1].offset);
p->name_strobj = PyUnicode_InternFromString(p->name); p->name_strobj = PyUnicode_InternFromString(p->name);
if (!p->name_strobj) if (!p->name_strobj || !PyUnicode_CHECK_INTERNED(p->name_strobj))
Py_FatalError("Out of memory interning slotdef names"); Py_FatalError("Out of memory interning slotdef names");
} }
slotdefs_initialized = 1; slotdefs_initialized = 1;
@ -6954,6 +6981,9 @@ update_slot(PyTypeObject *type, PyObject *name)
slotdef **pp; slotdef **pp;
int offset; int offset;
assert(PyUnicode_CheckExact(name));
assert(PyUnicode_CHECK_INTERNED(name));
/* Clear the VALID_VERSION flag of 'type' and all its /* Clear the VALID_VERSION flag of 'type' and all its
subclasses. This could possibly be unified with the subclasses. This could possibly be unified with the
update_subclasses() recursion below, but carefully: update_subclasses() recursion below, but carefully:
@ -6964,7 +6994,6 @@ update_slot(PyTypeObject *type, PyObject *name)
init_slotdefs(); init_slotdefs();
pp = ptrs; pp = ptrs;
for (p = slotdefs; p->name; p++) { for (p = slotdefs; p->name; p++) {
/* XXX assume name is interned! */
if (p->name_strobj == name) if (p->name_strobj == name)
*pp++ = p; *pp++ = p;
} }