|
47 | 47 | static PyObject* get_tp_attro(PyObject* unused, PyObject* object) {
|
48 | 48 | return PyLong_FromVoidPtr(Py_TYPE(object)->tp_getattro);
|
49 | 49 | }
|
| 50 | + static PyObject* get_tp_setattr(PyObject* unused, PyObject* object) { |
| 51 | + return PyLong_FromVoidPtr(Py_TYPE(object)->tp_setattr); |
| 52 | + } |
| 53 | + static PyObject* get_tp_setattro(PyObject* unused, PyObject* object) { |
| 54 | + return PyLong_FromVoidPtr(Py_TYPE(object)->tp_setattro); |
| 55 | + } |
50 | 56 | static PyObject* get_tp_descr_get(PyObject* unused, PyObject* object) {
|
51 | 57 | return PyLong_FromVoidPtr(Py_TYPE(object)->tp_descr_get);
|
52 | 58 | }
|
53 | 59 | """,
|
54 | 60 | tp_methods=
|
55 | 61 | '{"get_tp_attr", (PyCFunction)get_tp_attr, METH_O | METH_STATIC, ""},' +
|
56 | 62 | '{"get_tp_attro", (PyCFunction)get_tp_attro, METH_O | METH_STATIC, ""},' +
|
| 63 | + '{"get_tp_setattr", (PyCFunction)get_tp_setattr, METH_O | METH_STATIC, ""},' + |
| 64 | + '{"get_tp_setattro", (PyCFunction)get_tp_setattro, METH_O | METH_STATIC, ""},' + |
57 | 65 | '{"get_tp_descr_get", (PyCFunction)get_tp_descr_get, METH_O | METH_STATIC, ""}')
|
58 | 66 |
|
59 | 67 |
|
@@ -205,6 +213,45 @@ def test_setattr_wrapper():
|
205 | 213 | assert x.__setattr__("bar", 42) is None
|
206 | 214 |
|
207 | 215 |
|
| 216 | +def test_setattr_vs_setattro_inheritance(): |
| 217 | + TestSetAttrOInheritance = CPyExtType("TestSetAttrOInheritance", |
| 218 | + ''' |
| 219 | + int testattr_set(PyObject* self, char* key, PyObject* value) { |
| 220 | + Py_XDECREF(((TestSetAttrOInheritanceObject*)self)->payload); |
| 221 | + if (value != NULL) { |
| 222 | + Py_INCREF(value); |
| 223 | + } |
| 224 | + ((TestSetAttrOInheritanceObject*)self)->payload = value; |
| 225 | + return 0; |
| 226 | + } |
| 227 | +
|
| 228 | + PyObject* get_payload(PyObject *self) { |
| 229 | + PyObject* r = ((TestSetAttrOInheritanceObject*)self)->payload; |
| 230 | + if (r == NULL) Py_RETURN_NONE; |
| 231 | + Py_INCREF(r); |
| 232 | + return r; |
| 233 | + } |
| 234 | + ''', |
| 235 | + cmembers='PyObject* payload;', |
| 236 | + tp_methods='{"get_payload", (PyCFunction)get_payload, METH_NOARGS, ""}', |
| 237 | + tp_setattr="testattr_set") |
| 238 | + |
| 239 | + x = TestSetAttrOInheritance() |
| 240 | + x.foo = 42 |
| 241 | + assert x.get_payload() == 42 |
| 242 | + |
| 243 | + class Managed(TestSetAttrOInheritance): |
| 244 | + pass |
| 245 | + |
| 246 | + x = Managed() |
| 247 | + x.foo = 42 # calls slot_tp_setattro, which calls __setattr__, which wraps the object.tp_setattro |
| 248 | + assert x.get_payload() is None |
| 249 | + |
| 250 | + assert SlotsGetter.get_tp_setattro(object()) == SlotsGetter.get_tp_setattro(Managed()) |
| 251 | + assert SlotsGetter.get_tp_setattro(TestSetAttrOInheritance()) == 0 |
| 252 | + assert SlotsGetter.get_tp_setattr(TestSetAttrOInheritance()) != 0 |
| 253 | + |
| 254 | + |
208 | 255 | def test_sq_ass_item_wrapper():
|
209 | 256 | TestSqAssItemWrapperReturn = CPyExtType("TestSqAssItemWrapperReturn",
|
210 | 257 | "static int myassitem(PyObject *self, Py_ssize_t i, PyObject *v) { return 0; }",
|
|
0 commit comments