7

Previously I have created some Python classes using C API. When I'm going to build the old project with Python 3+ it gives following compile error

PyClass_New was not declared in this scope
Py_InitModule was not declared in this scope

What are the equivalents?

PyObject *pClassDic = PyDict_New();
PyObject *pClassName = PyBytes_FromString("MyClass");
PyObject *pClass = PyClass_New(NULL, pClassDic, pClassName);
filmor
  • 30,840
  • 6
  • 50
  • 48
Sujith Gunawardhane
  • 1,251
  • 1
  • 10
  • 24
  • did you try grepping through the codebase ? –  Mar 27 '15 at 13:31
  • not available in the code base. – Sujith Gunawardhane Mar 27 '15 at 13:38
  • Perhaps there is something in Doc/extending/newtypes.rst that is useful. –  Mar 27 '15 at 13:41
  • I think you need to migrate to Type interface. See here: https://docs.python.org/release/2.7/c-api/type.html#typeobjects Specifically `PyType_GenericNew` and `PyType_Ready` – RomanK Mar 27 '15 at 13:43
  • Why are you giving him python 2 docs when he's asking about python3? –  Mar 27 '15 at 13:44
  • Here is an example of how the `partial` class is created: https://hg.python.org/cpython/file/35bc15fa4930/Modules/_functoolsmodule.c#l12 Maybe this helps. – Cilyan Mar 27 '15 at 14:19
  • 1
    Actually maybe even clearer: https://docs.python.org/3.3/extending/newtypes.html?highlight=pytypeobject :) – Cilyan Mar 27 '15 at 14:22

2 Answers2

5

If you don't want to go through the hassle of setting up your object and type structs, you should be able to create a new class by calling Python's type(name, bases, dict) from C:

PyObject *pClassName = PyBytes_FromString("MyClass");
PyObject *pClassBases = PyTuple_New(0); // An empty tuple for bases is equivalent to `(object,)`
PyObject *pClassDic = PyDict_New();

// pClass = type(pClassName, pClassBases, pClassDic)
PyObject *pClass = PyObject_CallFunctionObjArgs(PyType_Type, pClassName, pClassBases, pClassDic, NULL);

Py_CLEAR(pClassName);
Py_CLEAR(pClassBases);
Py_CLEAR(pClassDic);
Uyghur Lives Matter
  • 18,820
  • 42
  • 108
  • 144
  • Small fixup necessary `PyType_Type` ---> `(PyObject *)&PyType_Type`. Possibly with recent versions of Python. – Arty Jan 29 '21 at 12:26
3

to complete the answer from 'cpburnz', here's a function that creates a class object and add methods :

PyObject *createClassObject(const char *name, PyMethodDef methods[])
{
    PyObject *pClassName = PyUnicode_FromString(name);
    PyObject *pClassBases = PyTuple_New(0); // An empty tuple for bases is equivalent to `(object,)`
    PyObject *pClassDic = PyDict_New();


    PyMethodDef *def;
    // add methods to class 
    for (def = methods; def->ml_name != NULL; def++)
    {
        printf("     add method %s\n", def->ml_name);
        PyObject *func = PyCFunction_New(def, NULL);
        PyObject *method = PyInstanceMethod_New(func);
        PyDict_SetItemString(pClassDic, def->ml_name, method);
        Py_DECREF(func);
        Py_DECREF(method);
    }

    // pClass = type(pClassName, pClassBases, pClassDic)
    PyObject *pClass = PyObject_CallFunctionObjArgs((PyObject *)&PyType_Type, pClassName, pClassBases, pClassDic, NULL);

    Py_DECREF(pClassName);
    Py_DECREF(pClassBases);
    Py_DECREF(pClassDic);


    return pClass;
}

Then, you can use it like this :

static PyMethodDef foo_Methods[] =
{
    { "__init__", fooInit, METH_VARARGS, "doc" },
    { "do_something", fooDoSomething, METH_VARARGS, "doc" },
    { 0, 0 },
};

PyObject * fooClass = createClassObject("fooClass", foo_Methods);
PyModule_AddObject(module, "foo", fooClass );

PyModule_AddObject is requested to make the "foo" class visible to python code.

Note : as suggested by 'RomanK', using PyTypeObject is a good (and probably better) alternative.

EDIT : I confirm it's much better and easy to use PyTypeObject directly, fill the structure and call PyModule_AddObject() to make the new type available to Python. It's more flexible, offers more flexibility and it's the official way. As mentionned by Cilyan, everything is explained here : https://docs.python.org/3.3/extending/newtypes.html?highlight=pytypeobject