Python C API: Subclassing (builtin) types with Py_LIMITED_API

Viewed 59

I am trying to port an existing C Python extension to use the Py_LIMITED_API, so that we don't have to build the extension for all the python versions we support.

One of the issues I'm facing is, that this library provides a type that subclasses the python float type.

The last official documentation with examples ( https://docs.python.org/3.5/extending/newtypes.html#subclassing-other-types) does not mention the limited API, and I'm facing the problem, that PyFloatObject is not part of the stable ABI, which would be necessary to create the base struct:

typedef struct {
    PyFloatObject base;
    int state;
} SubclassedFloat;

Is there a way to subclass a builtin type when using Py_LIMITED_API, especially float, or is this impossible?

1 Answers

This was answered on the Python c-extensions discord: https://discord.com/channels/267624335836053506/728390945384431688/981133468652154920

Basically, you have to fetch the __basicsize__ type attribute from PyFloat_Type at runtime to declare your object size:

//Code simplified for brevity

struct SubclassedFloatObject
{
  // put your data members here
};

Py_ssize_t PyFloat_basicsize = PyLong_AsSsize_t(
    PyObject_GetAttrString(
        (PyObject*)&PyFloat_Type,
        "__basicsize__"));
PyType_Spec SubclassedFloat_spec = {
    "SubclassedFloat",                                                      //name
    static_cast<int>(PyFloat_basicsize + sizeof(SubclassedFloatObject)),    //basicsize
    0,                                                                      //itemsize
    Py_TPFLAGS_DEFAULT,                                                     //flags
    SubclassedFloat_slots                                                   //slots; /* terminated by slot==0. */
};

Whenever you access your object, you'll have to offset the PyObject* pointer for this size:

int SomePythonFunc(PyObject* self)
{
    SubclassedFloat* p_data = (SubclassedFloat*)((uintptr_t)(self) + PyFloat_basicsize);
    // do something with p_data
}
Related