diff options
author | scoder <stefan_ml@behnel.de> | 2020-07-03 00:09:28 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-07-03 00:09:28 (GMT) |
commit | 148f32913573c29250dfb3f0d079eb8847633621 (patch) | |
tree | c13dbb1fa7eb316f45374b3972396619a2a7145c /Objects | |
parent | 67673b08ea302174ca9c6559c04311550ae9f189 (diff) | |
download | cpython-148f32913573c29250dfb3f0d079eb8847633621.zip cpython-148f32913573c29250dfb3f0d079eb8847633621.tar.gz cpython-148f32913573c29250dfb3f0d079eb8847633621.tar.bz2 |
bpo-39960: Allow heap types in the "Carlo Verre" hack check that override "tp_setattro()" (GH-21092)
Automerge-Triggered-By: @gvanrossum
Diffstat (limited to 'Objects')
-rw-r--r-- | Objects/typeobject.c | 41 |
1 files changed, 30 insertions, 11 deletions
diff --git a/Objects/typeobject.c b/Objects/typeobject.c index f0e349e..16f95f0 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -97,6 +97,9 @@ clear_slotdefs(void); static PyObject * lookup_maybe_method(PyObject *self, _Py_Identifier *attrid, int *unbound); +static int +slot_tp_setattro(PyObject *self, PyObject *name, PyObject *value); + /* * finds the beginning of the docstring's introspection signature. * if present, returns a pointer pointing to the first '('. @@ -5945,22 +5948,38 @@ wrap_delitem(PyObject *self, PyObject *args, void *wrapped) } /* Helper to check for object.__setattr__ or __delattr__ applied to a type. - This is called the Carlo Verre hack after its discoverer. */ + This is called the Carlo Verre hack after its discoverer. See + https://mail.python.org/pipermail/python-dev/2003-April/034535.html + */ static int hackcheck(PyObject *self, setattrofunc func, const char *what) { PyTypeObject *type = Py_TYPE(self); - while (type && type->tp_flags & Py_TPFLAGS_HEAPTYPE) - type = type->tp_base; - /* If type is NULL now, this is a really weird type. - In the spirit of backwards compatibility (?), just shut up. */ - if (type && type->tp_setattro != func) { - PyErr_Format(PyExc_TypeError, - "can't apply this %s to %s object", - what, - type->tp_name); - return 0; + PyObject *mro = type->tp_mro; + if (!mro) { + /* Probably ok not to check the call in this case. */ + return 1; + } + assert(PyTuple_Check(mro)); + Py_ssize_t i, n; + n = PyTuple_GET_SIZE(mro); + for (i = 0; i < n; i++) { + PyTypeObject *base = (PyTypeObject*) PyTuple_GET_ITEM(mro, i); + if (base->tp_setattro == func) { + /* 'func' is the earliest non-Python implementation in the MRO. */ + break; + } else if (base->tp_setattro != slot_tp_setattro) { + /* 'base' is not a Python class and overrides 'func'. + Its tp_setattro should be called instead. */ + PyErr_Format(PyExc_TypeError, + "can't apply this %s to %s object", + what, + type->tp_name); + return 0; + } } + /* Either 'func' is not in the mro (which should fail when checking 'self'), + or it's the right slot function to call. */ return 1; } |