mirror of https://github.com/python/cpython
bpo-39960: Allow heap types in the "Carlo Verre" hack check that override "tp_setattro()" (GH-21092)
Automerge-Triggered-By: @gvanrossum
This commit is contained in:
parent
67673b08ea
commit
148f329135
|
@ -516,6 +516,14 @@ class CAPITest(unittest.TestCase):
|
||||||
# Test that subtype_dealloc decref the newly assigned __class__ only once
|
# Test that subtype_dealloc decref the newly assigned __class__ only once
|
||||||
self.assertEqual(new_type_refcnt, sys.getrefcount(_testcapi.HeapCTypeSubclass))
|
self.assertEqual(new_type_refcnt, sys.getrefcount(_testcapi.HeapCTypeSubclass))
|
||||||
|
|
||||||
|
def test_heaptype_with_setattro(self):
|
||||||
|
obj = _testcapi.HeapCTypeSetattr()
|
||||||
|
self.assertEqual(obj.pvalue, 10)
|
||||||
|
obj.value = 12
|
||||||
|
self.assertEqual(obj.pvalue, 12)
|
||||||
|
del obj.value
|
||||||
|
self.assertEqual(obj.pvalue, 0)
|
||||||
|
|
||||||
def test_pynumber_tobase(self):
|
def test_pynumber_tobase(self):
|
||||||
from _testcapi import pynumber_tobase
|
from _testcapi import pynumber_tobase
|
||||||
self.assertEqual(pynumber_tobase(123, 2), '0b1111011')
|
self.assertEqual(pynumber_tobase(123, 2), '0b1111011')
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
The "hackcheck" that prevents sneaking around a type's __setattr__() by calling the
|
||||||
|
superclass method was rewritten to allow C implemented heap types.
|
|
@ -6528,6 +6528,80 @@ static PyType_Spec HeapCTypeWithWeakref_spec = {
|
||||||
HeapCTypeWithWeakref_slots
|
HeapCTypeWithWeakref_slots
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PyDoc_STRVAR(heapctypesetattr__doc__,
|
||||||
|
"A heap type without GC, but with overridden __setattr__.\n\n"
|
||||||
|
"The 'value' attribute is set to 10 in __init__ and updated via attribute setting.");
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PyObject_HEAD
|
||||||
|
long value;
|
||||||
|
} HeapCTypeSetattrObject;
|
||||||
|
|
||||||
|
static struct PyMemberDef heapctypesetattr_members[] = {
|
||||||
|
{"pvalue", T_LONG, offsetof(HeapCTypeSetattrObject, value)},
|
||||||
|
{NULL} /* Sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
heapctypesetattr_init(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||||
|
{
|
||||||
|
((HeapCTypeSetattrObject *)self)->value = 10;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
heapctypesetattr_dealloc(HeapCTypeSetattrObject *self)
|
||||||
|
{
|
||||||
|
PyTypeObject *tp = Py_TYPE(self);
|
||||||
|
PyObject_Del(self);
|
||||||
|
Py_DECREF(tp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
heapctypesetattr_setattro(HeapCTypeSetattrObject *self, PyObject *attr, PyObject *value)
|
||||||
|
{
|
||||||
|
PyObject *svalue = PyUnicode_FromString("value");
|
||||||
|
if (svalue == NULL)
|
||||||
|
return -1;
|
||||||
|
int eq = PyObject_RichCompareBool(svalue, attr, Py_EQ);
|
||||||
|
Py_DECREF(svalue);
|
||||||
|
if (eq < 0)
|
||||||
|
return -1;
|
||||||
|
if (!eq) {
|
||||||
|
return PyObject_GenericSetAttr((PyObject*) self, attr, value);
|
||||||
|
}
|
||||||
|
if (value == NULL) {
|
||||||
|
self->value = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
PyObject *ivalue = PyNumber_Long(value);
|
||||||
|
if (ivalue == NULL)
|
||||||
|
return -1;
|
||||||
|
long v = PyLong_AsLong(ivalue);
|
||||||
|
Py_DECREF(ivalue);
|
||||||
|
if (v == -1 && PyErr_Occurred())
|
||||||
|
return -1;
|
||||||
|
self->value = v;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyType_Slot HeapCTypeSetattr_slots[] = {
|
||||||
|
{Py_tp_init, heapctypesetattr_init},
|
||||||
|
{Py_tp_members, heapctypesetattr_members},
|
||||||
|
{Py_tp_setattro, heapctypesetattr_setattro},
|
||||||
|
{Py_tp_dealloc, heapctypesetattr_dealloc},
|
||||||
|
{Py_tp_doc, (char*)heapctypesetattr__doc__},
|
||||||
|
{0, 0},
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyType_Spec HeapCTypeSetattr_spec = {
|
||||||
|
"_testcapi.HeapCTypeSetattr",
|
||||||
|
sizeof(HeapCTypeSetattrObject),
|
||||||
|
0,
|
||||||
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
|
||||||
|
HeapCTypeSetattr_slots
|
||||||
|
};
|
||||||
|
|
||||||
static PyMethodDef meth_instance_methods[] = {
|
static PyMethodDef meth_instance_methods[] = {
|
||||||
{"meth_varargs", meth_varargs, METH_VARARGS},
|
{"meth_varargs", meth_varargs, METH_VARARGS},
|
||||||
{"meth_varargs_keywords", (PyCFunction)(void(*)(void))meth_varargs_keywords, METH_VARARGS|METH_KEYWORDS},
|
{"meth_varargs_keywords", (PyCFunction)(void(*)(void))meth_varargs_keywords, METH_VARARGS|METH_KEYWORDS},
|
||||||
|
@ -6834,6 +6908,12 @@ PyInit__testcapi(void)
|
||||||
}
|
}
|
||||||
PyModule_AddObject(m, "HeapCTypeWithBuffer", HeapCTypeWithBuffer);
|
PyModule_AddObject(m, "HeapCTypeWithBuffer", HeapCTypeWithBuffer);
|
||||||
|
|
||||||
|
PyObject *HeapCTypeSetattr = PyType_FromSpec(&HeapCTypeSetattr_spec);
|
||||||
|
if (HeapCTypeSetattr == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyModule_AddObject(m, "HeapCTypeSetattr", HeapCTypeSetattr);
|
||||||
|
|
||||||
PyObject *subclass_with_finalizer_bases = PyTuple_Pack(1, HeapCTypeSubclass);
|
PyObject *subclass_with_finalizer_bases = PyTuple_Pack(1, HeapCTypeSubclass);
|
||||||
if (subclass_with_finalizer_bases == NULL) {
|
if (subclass_with_finalizer_bases == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -97,6 +97,9 @@ clear_slotdefs(void);
|
||||||
static PyObject *
|
static PyObject *
|
||||||
lookup_maybe_method(PyObject *self, _Py_Identifier *attrid, int *unbound);
|
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.
|
* finds the beginning of the docstring's introspection signature.
|
||||||
* if present, returns a pointer pointing to the first '('.
|
* 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.
|
/* 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
|
static int
|
||||||
hackcheck(PyObject *self, setattrofunc func, const char *what)
|
hackcheck(PyObject *self, setattrofunc func, const char *what)
|
||||||
{
|
{
|
||||||
PyTypeObject *type = Py_TYPE(self);
|
PyTypeObject *type = Py_TYPE(self);
|
||||||
while (type && type->tp_flags & Py_TPFLAGS_HEAPTYPE)
|
PyObject *mro = type->tp_mro;
|
||||||
type = type->tp_base;
|
if (!mro) {
|
||||||
/* If type is NULL now, this is a really weird type.
|
/* Probably ok not to check the call in this case. */
|
||||||
In the spirit of backwards compatibility (?), just shut up. */
|
return 1;
|
||||||
if (type && type->tp_setattro != func) {
|
|
||||||
PyErr_Format(PyExc_TypeError,
|
|
||||||
"can't apply this %s to %s object",
|
|
||||||
what,
|
|
||||||
type->tp_name);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue