Skip to content

Commit 19a7027

Browse files
committed
[GR-34614] Call __getattr__ from tp_getattro wrapper
PullRequest: graalpython/2801
2 parents 3da18d6 + 17303b6 commit 19a7027

File tree

20 files changed

+602
-601
lines changed

20 files changed

+602
-601
lines changed

graalpython/com.oracle.graal.python.cext/src/typeobject.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -750,13 +750,13 @@ int type_ready_graalpy_slot_conv(PyTypeObject* cls, PyObject* dict) {
750750

751751
// NOTE: The slots may be called from managed code, i.e., we need to wrap the functions
752752
// and convert arguments that should be C primitives.
753-
ADD_SLOT_CONV("__getattr__", cls->tp_getattr, -2, JWRAPPER_GETATTR);
753+
ADD_SLOT_CONV("__getattribute__", cls->tp_getattr, -2, JWRAPPER_GETATTR);
754754
ADD_SLOT_CONV("__setattr__", cls->tp_setattr, -3, JWRAPPER_SETATTR);
755755
ADD_SLOT_CONV("__repr__", cls->tp_repr, -1, JWRAPPER_REPR);
756756
ADD_SLOT_CONV("__hash__", cls->tp_hash, -1, JWRAPPER_HASHFUNC);
757757
ADD_SLOT_CONV("__call__", cls->tp_call, METH_KEYWORDS | METH_VARARGS, JWRAPPER_CALL);
758758
ADD_SLOT_CONV("__str__", cls->tp_str, -1, JWRAPPER_STR);
759-
ADD_SLOT_CONV("__getattr__", cls->tp_getattro, -2, JWRAPPER_DIRECT);
759+
ADD_SLOT_CONV("__getattribute__", cls->tp_getattro, -2, JWRAPPER_DIRECT);
760760
ADD_SLOT_CONV("__setattr__", cls->tp_setattro, -3, JWRAPPER_SETATTRO);
761761
ADD_SLOT_CONV("__clear__", cls->tp_clear, -1, JWRAPPER_INQUIRY);
762762

graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_functions.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -375,10 +375,15 @@ def richcompare_bool(args):
375375
argspec="OOi",
376376
resultspec="i",
377377
)
378-
378+
379+
class TypeWithGetattr:
380+
def __getattr__(self, item):
381+
return item
382+
379383
__PyObject_GetAttrString_ARGS = (
380384
(MyObject(), "foo"),
381385
([], "__len__"),
386+
(TypeWithGetattr(), "foo"),
382387
)
383388
test_PyObject_GetAttrString = CPyExtFunction(
384389
lambda args: getattr(*args),
@@ -442,6 +447,7 @@ def _ref_hash_not_implemented(args):
442447
__PyObject_GetAttr_ARGS = (
443448
(MyObject(), "foo"),
444449
([], "__len__"),
450+
(TypeWithGetattr(), "foo"),
445451
)
446452
test_PyObject_GetAttr = CPyExtFunction(
447453
lambda args: getattr(*args),
@@ -480,4 +486,4 @@ def _ref_hash_not_implemented(args):
480486
arguments=["PyObject* func"],
481487
argspec="O",
482488
cmpfunc=lambda x, y: type(x) == staticmethod
483-
)
489+
)

graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_object.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,64 @@ def test_getattro(self):
334334
tester = TestInt()
335335
assert tester.foo == "foo"
336336

337+
def test_getattro_inheritance(self):
338+
TestWithGetattro = CPyExtType(
339+
'TestWithGetattro',
340+
'''
341+
static PyObject* getattro(PyObject* self, PyObject* key) {
342+
if (PyObject_IsTrue(key)) {
343+
Py_INCREF(key);
344+
return key;
345+
}
346+
return PyErr_Format(PyExc_AttributeError, "Nope");
347+
}
348+
349+
static PyObject* call_getattro_slot(PyObject* unused, PyObject* args) {
350+
PyObject* object;
351+
PyObject* key;
352+
if (!PyArg_ParseTuple(args, "OO", &object, &key))
353+
return NULL;
354+
return Py_TYPE(object)->tp_getattro(object, key);
355+
}
356+
''',
357+
tp_getattro='getattro',
358+
tp_methods='{"call_getattro_slot", (PyCFunction)call_getattro_slot, METH_VARARGS | METH_STATIC, ""}'
359+
)
360+
361+
def validate(cls, has_getattro, has_getattr):
362+
foo = cls()
363+
if has_getattro:
364+
assert foo.asdf == 'asdf'
365+
assert TestWithGetattro.call_getattro_slot(foo, 'asdf') == 'asdf'
366+
elif has_getattr:
367+
assert foo.asdf == 3
368+
assert TestWithGetattro.call_getattro_slot(foo, 'asdf') == 3
369+
if has_getattro and not has_getattr:
370+
assert_raises(AttributeError, lambda: getattr(foo, ''))
371+
assert_raises(AttributeError, TestWithGetattro.call_getattro_slot, foo, '')
372+
if has_getattr:
373+
assert getattr(foo, '') == 3
374+
assert TestWithGetattro.call_getattro_slot(foo, '') == 3
375+
376+
validate(TestWithGetattro, True, False)
377+
378+
class Subclass(TestWithGetattro):
379+
pass
380+
381+
validate(Subclass, True, False)
382+
383+
class ObjWithGetattr:
384+
def __getattr__(self, item):
385+
return 3
386+
387+
validate(ObjWithGetattr, False, True)
388+
389+
class SubclassWithGetattr(TestWithGetattro):
390+
def __getattr__(self, item):
391+
return 3
392+
393+
validate(SubclassWithGetattr, True, True)
394+
337395
def test_dict(self):
338396
TestDict = CPyExtType("TestDict",
339397
"""static PyObject* custom_dict = NULL;

0 commit comments

Comments
 (0)