Reputation: 1718
I have a custom Python module written in C, and I want to add an attribute to the module which is dynamically populated. E.g.:
import mymod
print(mymod.x) # At this point, the value of x is computed
The name of the attribute is known in advance.
From what I understand, this should be possible using descriptors, but it is not working as expected. I implemented a custom type, implemented the tp_descr_get
function for the type, and assigned an instance of the type to my module, but the tp_descr_get
function is never called.
Here is my test module:
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <stdio.h>
static struct PyModuleDef testmod = {
PyModuleDef_HEAD_INIT,
"testmod",
NULL,
-1
};
typedef struct testattrib_s {
PyObject_HEAD
} testattrib;
static PyObject *testattrib_descr_get(PyObject *self, PyObject *obj, PyObject *type);
static int testattrib_descr_set(PyObject *self, PyObject *obj, PyObject *value);
PyTypeObject testattribtype = {
PyVarObject_HEAD_INIT (NULL, 0)
"testattrib", /* tp_name */
sizeof (testattrib), /* tp_basicsize */
/* lots of zeros omitted for brevity */
testattrib_descr_get, /* tp_descr_get */
testattrib_descr_set /* tp_descr_set */
};
PyMODINIT_FUNC
PyInit_testmod(void)
{
if (PyType_Ready(&testattribtype)) {
return NULL;
}
testattrib *attrib = PyObject_New(testattrib, &testattribtype);
if (attrib == NULL) {
return NULL;
}
PyObject *m = PyModule_Create(&testmod);
if (m == NULL) {
return NULL;
}
if (PyModule_AddObject(m, "myattrib", (PyObject *) attrib)) {
return NULL;
}
return m;
}
static PyObject *testattrib_descr_get(PyObject *self, PyObject *obj, PyObject *type)
{
printf("testattrib_descr_get called\n");
Py_INCREF(self);
return self;
}
static int testattrib_descr_set(PyObject *self, PyObject *obj, PyObject *value)
{
printf("testattrib_descr_set called\n");
return 0;
}
I test it like this:
import testmod
print(testmod.myattrib) # should call tp_descr_get
testmod.myattrib = 1 # should call tp_descr_set
The getter/setter functions are never called. What am I doing wrong?
I am running Python 3.8.5 on macOS 12.0.1 with a build from Anaconda:
>>> sys.version
'3.8.5 (default, Sep 4 2020, 02:22:02) \n[Clang 10.0.0 ]'
Upvotes: 2
Views: 307
Reputation: 39838
Descriptors operate only as attributes on a type. You would have to create your module as an instance of a module subclass equipped with the descriptor. The easiest way to do that is to use the Py_mod_create
slot (not to be confused with __slots__
).
Upvotes: 1