Extension Module for Python 3.6 +

MRAB python at mrabarnett.plus.com
Fri Mar 1 15:41:38 EST 2019


On 2019-03-01 01:19, Anthony Flury via Python-list wrote:
> In my brave and noble quest to get to grips with the CAPI - I am trying
> to write a C extension module which provides a new class
> 
> The exact details are not important, but what is important is that
> instances of my new class are imutable, and therefore from time to time,
> my extension module needs to build a new instance of my extension class.
> 
> The documentation is pretty sparse here, and suggests using PyObject_New
> & PyObject_Init but gives no examples.
> 
> An internet search suggests using PyObject_CallObject in some way - but
> the examples here are call backs to Python functions.
> 
> What is the canonical way to create New Instances of the Extension Type
> from within the Extension Module - even if someone can point me to a
> working example that would be great.
> 
> Another option would be to call the extension class new and init
> functions directly - is there a reason that is not allowed ?
> 
> PS - I think I also need to use Py_BuildValue to 'encapsulate' the
> initial arguments for the new instance - or could I just build a 'blank'
> instance and then directly set the fields as neccessary ?
> 
Here's an example that exports a 'make' factory function to create a new 
instance. Hope it helps:

----8<---- class_example.c ----8<----

#include "Python.h"
#include "structmember.h" /* offsetof */

/* The MyClassObject. */
typedef struct MyClassObject {
     PyObject_HEAD
     PyObject* weakreflist; /* List of weak references. */
     PyObject* value;
} MyClassObject;

/* The MyClass_Type. */
static PyTypeObject MyClass_Type = {
     PyVarObject_HEAD_INIT(NULL, 0)
     "class_example.MyClass",
     sizeof(MyClassObject)
};

/* Makes an instance with the given value. */
Py_LOCAL_INLINE(PyObject*) myclass_make(PyObject* self_, PyObject* args,
   PyObject* kwargs) {
     PyObject* value;
     MyClassObject* instance;

     static char* kwlist[] = { "value", NULL };
     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &value))
         return NULL;

    instance = PyObject_New(MyClassObject, &MyClass_Type);
    if (!instance)
     return NULL;

    instance->value = value;
    Py_INCREF(instance->value);

    return (PyObject*)instance;
}

/* Deallocates a MyClassObject. */
static void myclass_dealloc(PyObject* self_) {
     MyClassObject* self;

     self = (MyClassObject*)self_;

     Py_DECREF(self->value);

     PyObject_DEL(self);
}

/* The documentation of a MyClassObject. */
PyDoc_STRVAR(myclass_doc, "MyClass object");

/* The methods of a MyClassObject. */
static PyMethodDef myclass_methods[] = {
     /* The instance methods here. */
     {NULL, NULL}
};

/* The members of a MyClassObject. */
static PyMemberDef myclass_members[] = {
     {"value", T_OBJECT, offsetof(MyClassObject, value), READONLY,
       "The stored value."},
     {NULL} /* Sentinel */
};

PyDoc_STRVAR(myclass_make_doc,
     "make(value) --> MyClassObject.\n"
     "    Makes a MyClass object.");

/* The table of the module's functions. */
static PyMethodDef functions[] = {
     { "make", (PyCFunction)myclass_make, METH_VARARGS | METH_KEYWORDS,
       myclass_make_doc },
     { NULL, NULL }
};

/* The module definition. */
static struct PyModuleDef class_example_module = {
     PyModuleDef_HEAD_INIT,
     "class_example",
     NULL,
     -1,
     functions,
     NULL,
     NULL,
     NULL,
     NULL
};

/* Initialises the module. */
PyMODINIT_FUNC PyInit_class_example(void) {
     PyObject* this_module;

     /* Initialise the MyClass_Type. */
     MyClass_Type.tp_dealloc = myclass_dealloc;
     MyClass_Type.tp_flags = Py_TPFLAGS_DEFAULT;
     MyClass_Type.tp_doc = myclass_doc;
     MyClass_Type.tp_weaklistoffset = offsetof(MyClassObject, weakreflist);
     MyClass_Type.tp_methods = myclass_methods;
     MyClass_Type.tp_members = myclass_members;
     if (PyType_Ready(&MyClass_Type) < 0)
         return NULL;

     /* Create the module. */
     this_module = PyModule_Create(&class_example_module);
     if (!this_module)
         return NULL;

     /* This makes MyClass visible, but won't let you create an 
instance. Useful
      * with Python's isinstance function.
      */
     if (PyModule_AddObject(this_module, "MyClass", 
(PyObject*)&MyClass_Type) != 0)
         return NULL;

     return this_module;
}

----8<---- test_example.py ----8<----

#!python3.7
# -*- coding: utf-8 -*-
import class_example

from class_example import make
instance = make(0)
print(instance)
print(instance.value)

# Cannot change the attribute:
#instance.value = 1



More information about the Python-list mailing list