This is a strange behavior for static type:
#include <Python.h>
typedef struct {
PyObject_HEAD
PyObject* proxy;
} ProxyObject;
static PyObject* ProxyObject_getattr(ProxyObject* self, PyObject* key) {
if (!self->proxy) {
PyErr_SetString(PyExc_ValueError, "no proxy");
return NULL;
}
return PyObject_GetAttr(self->proxy, key);
}
static PyMethodDef Proxy_methods[] = {
{"__getattr__", (PyCFunction)ProxyObject_getattr, METH_O,
"Get attribute from proxied object"},
{NULL} /* Sentinel */
};
static int ProxyObject_init(ProxyObject* self, PyObject* args, PyObject* kwds) {
PyObject* obj;
if (!PyArg_ParseTuple(args, "O", &obj)) {
return -1;
}
self->proxy = obj;
return 0;
}
static PyTypeObject ProxyType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "proxy.Proxy",
.tp_basicsize = sizeof(ProxyObject),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_methods = Proxy_methods,
.tp_new = PyType_GenericNew,
.tp_init = (initproc)ProxyObject_init,
};
static struct PyModuleDef proxymodule = {
PyModuleDef_HEAD_INIT,
"proxy",
"Proxy module",
-1,
NULL, NULL, NULL, NULL, NULL
};
PyMODINIT_FUNC PyInit_proxy(void) {
PyObject* m;
if (PyType_Ready(&ProxyType) < 0)
return NULL;
m = PyModule_Create(&proxymodule);
if (m == NULL)
return NULL;
Py_INCREF(&ProxyType);
PyModule_AddObject(m, "Proxy", (PyObject*)&ProxyType);
return m;
}
Then we can test:
>>> import proxy
>>> x = proxy.Proxy(1)
>>> x.real
Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
x.real
AttributeError: 'proxy.Proxy' object has no attribute 'real'
>>> x.__getattr__("real")
1
>>> proxy.Proxy.__getattribute__ is object.__getattribute__
True
>>> proxy.Proxy.__getattr__
<method '__getattr__' of 'proxy.Proxy' objects>
As you see: the “__getattr__” didn’t be called when “__getattribute__” raise AttributeError (we didn’t change “__getattribute__”).
Here is the document:
object.__getattr__(self, name )
Called when the default attribute access fails with an
AttributeError(either__getattribute__()raises anAttributeErrorbecause name is not an instance attribute or an attribute in the class tree forself; or__get__()of a name property raisesAttributeError). This method should either return the (computed) attribute value or raise anAttributeErrorexception. Theobjectclass itself does not provide this method.Note that if the attribute is found through the normal mechanism,
__getattr__()is not called. (This is an intentional asymmetry between__getattr__()and__setattr__().) This is done both for efficiency reasons and because otherwise__getattr__()would have no way to access other attributes of the instance. Note that at least for instance variables, you can take total control by not inserting any values in the instance attribute dictionary (but instead inserting them in another object). See the__getattribute__()method below for a way to actually get total control over attribute access.
This might be an explaination for it:
If the type is a static type, its tp_getattro defaults to just find attribute on type and won’t respect “__getattr__”. So we need to document that when write c extern module, don’t use “__getattr__” to control when attribute not found.
Test on 3.14