diff options
author | Serhiy Storchaka <storchaka@gmail.com> | 2017-05-20 07:04:53 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-05-20 07:04:53 (GMT) |
commit | e9f9b042781e3cb480315860bcdf7b7d22cba0f8 (patch) | |
tree | 6ed59ddc486a324e938c3ca947cb0799c21ddf26 | |
parent | c47c315812b1fa9acb16510a7aa3b37d113def48 (diff) | |
download | cpython-e9f9b042781e3cb480315860bcdf7b7d22cba0f8.zip cpython-e9f9b042781e3cb480315860bcdf7b7d22cba0f8.tar.gz cpython-e9f9b042781e3cb480315860bcdf7b7d22cba0f8.tar.bz2 |
[2.7] bpo-25794: Fix `type.__setattr__()` for non-interned or unicode attribute names. (GH-1652) (#1675)
Based on patch by Eryk Sun.
(cherry picked from commit d896985bb2de49046f9b6879e906d1e4db255e23)
-rw-r--r-- | Lib/test/test_class.py | 43 | ||||
-rw-r--r-- | Misc/NEWS | 3 | ||||
-rw-r--r-- | Objects/typeobject.c | 43 |
3 files changed, 84 insertions, 5 deletions
diff --git a/Lib/test/test_class.py b/Lib/test/test_class.py index e5cdf08..5cd138d 100644 --- a/Lib/test/test_class.py +++ b/Lib/test/test_class.py @@ -635,6 +635,49 @@ class ClassTests(unittest.TestCase): self.assertRaises(TypeError, type(c).__getattribute__, c, []) self.assertRaises(TypeError, type(c).__setattr__, c, [], []) + def testSetattrWrapperNameIntern(self): + # Issue #25794: __setattr__ should intern the attribute name + class A(object): + pass + + def add(self, other): + return 'summa' + + name = ''.join(list('__add__')) # shouldn't be optimized + self.assertIsNot(name, '__add__') # not interned + type.__setattr__(A, name, add) + self.assertEqual(A() + 1, 'summa') + + name2 = ''.join(list('__add__')) + self.assertIsNot(name2, '__add__') + self.assertIsNot(name2, name) + type.__delattr__(A, name2) + with self.assertRaises(TypeError): + A() + 1 + + @test_support.requires_unicode + def testSetattrWrapperNameUnicode(self): + # Issue #25794: __setattr__ should intern the attribute name + class A(object): + pass + + def add(self, other): + return 'summa' + + type.__setattr__(A, u'__add__', add) + self.assertEqual(A() + 1, 'summa') + + type.__delattr__(A, u'__add__') + with self.assertRaises(TypeError): + A() + 1 + + def testSetattrNonStringName(self): + class A(object): + pass + + with self.assertRaises(TypeError): + type.__setattr__(A, bytearray(b'x'), None) + def test_main(): with test_support.check_py3k_warnings( (".+__(get|set|del)slice__ has been removed", DeprecationWarning), @@ -10,6 +10,9 @@ What's New in Python 2.7.14? Core and Builtins ----------------- +- bpo-25794: Fixed type.__setattr__() and type.__delattr__() for + non-interned or unicode attribute names. Based on patch by Eryk Sun. + - bpo-29935: Fixed error messages in the index() method of tuple and list when pass indices of wrong type. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 3494fa9..eb5f5a4 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2687,6 +2687,7 @@ type_getattro(PyTypeObject *type, PyObject *name) static int type_setattro(PyTypeObject *type, PyObject *name, PyObject *value) { + int res; if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) { PyErr_Format( PyExc_TypeError, @@ -2694,9 +2695,39 @@ type_setattro(PyTypeObject *type, PyObject *name, PyObject *value) type->tp_name); return -1; } - if (PyObject_GenericSetAttr((PyObject *)type, name, value) < 0) - return -1; - return update_slot(type, name); +#ifdef Py_USING_UNICODE + if (PyUnicode_Check(name)) { + name = PyUnicode_AsEncodedString(name, NULL, NULL); + if (name == NULL) + return -1; + } + else +#endif + Py_INCREF(name); + + if (PyString_Check(name)) { + if (!PyString_CheckExact(name)) { + Py_SETREF(name, + PyString_FromStringAndSize(PyString_AS_STRING(name), + PyString_GET_SIZE(name)) + ); + if (name == NULL) + return -1; + } + PyString_InternInPlace(&name); + if (!PyString_CHECK_INTERNED(name)) { + PyErr_SetString(PyExc_MemoryError, + "Out of memory interning an attribute name"); + Py_DECREF(name); + return -1; + } + } + res = PyObject_GenericSetAttr((PyObject *)type, name, value); + if (res == 0) { + res = update_slot(type, name); + } + Py_DECREF(name); + return res; } static void @@ -6355,7 +6386,7 @@ init_slotdefs(void) /* Slots must be ordered by their offset in the PyHeapTypeObject. */ assert(!p[1].name || p->offset <= p[1].offset); p->name_strobj = PyString_InternFromString(p->name); - if (!p->name_strobj) + if (!p->name_strobj || !PyString_CHECK_INTERNED(p->name_strobj)) Py_FatalError("Out of memory interning slotdef names"); } initialized = 1; @@ -6370,6 +6401,9 @@ update_slot(PyTypeObject *type, PyObject *name) slotdef **pp; int offset; + assert(PyString_CheckExact(name)); + assert(PyString_CHECK_INTERNED(name)); + /* Clear the VALID_VERSION flag of 'type' and all its subclasses. This could possibly be unified with the update_subclasses() recursion below, but carefully: @@ -6380,7 +6414,6 @@ update_slot(PyTypeObject *type, PyObject *name) init_slotdefs(); pp = ptrs; for (p = slotdefs; p->name; p++) { - /* XXX assume name is interned! */ if (p->name_strobj == name) *pp++ = p; } |