2

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 ]'

1 Answer 1

1

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__).

Sign up to request clarification or add additional context in comments.

4 Comments

Could you give a brief description of how to create a module subclass?
@chris: Do you mean how to create a subclass in C? There’s nothing special about this notional class either way, of course.
Just looking for an idea of what py_mod_init should look like. I don't see any examples in the docs. I guess I define a subclass of PyModule_Type following the example here: docs.python.org/3.5/extending/…. Then my PyInit_xxx function creates and returns a PyModuleDef instance whose py_mod_new function creates and returns an instance of my subclass?
I have so far failed to create a module subclass. I have asked a second question about this: stackoverflow.com/questions/70608438/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.