summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
authorscoder <stefan_ml@behnel.de>2020-07-03 00:09:28 (GMT)
committerGitHub <noreply@github.com>2020-07-03 00:09:28 (GMT)
commit148f32913573c29250dfb3f0d079eb8847633621 (patch)
treec13dbb1fa7eb316f45374b3972396619a2a7145c /Objects
parent67673b08ea302174ca9c6559c04311550ae9f189 (diff)
downloadcpython-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.c41
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;
}