Sunday, June 20, 2021

Exercising to Create Basic Python C Extensions on Fedora Linux 34

According https://realpython.com/build-python-c-extension-module/

To write Python modules in C, you’ll need to use the Python API, which defines the various functions, macros, and variables that allow the Python interpreter to call your C code. All of these tools and more are collectively bundled in the <Python.h> header file.

This post follows up Creating Basic Python C Extensions - Tutorial  and does just one update required in static PyMethodDef myMethods[]  still having "METH_NOARGS" instead of "METH_VARARGS", 
what, actually, confuses  inexperienced learners, due to Python module runtime  failure with error "fib( ) expecting no arguments , but get (1)". As of now the only version available to follow :-
static PyMethodDef myMethods[] = {
    { "fib",fib, METH_NOARGS, "Prints Hello World" },
    { NULL, NULL, 0, NULL }
};
Should be
static PyMethodDef myMethods[] = {
    { "fib",fib,METH_VARARGS, "Fibonacci" },
    { NULL, NULL, 0, NULL }
};

Following below is working code follows up the link above, but  making  "code sample" easy to reproduce. Notice also that outside VENV you would have problems to run `python setup install`

(.env) [boris@fedora33server FIBONACHI]$ cat test.c

#include <Python.h>

int Cfib(int n)
{
    if (n < 2)
        return n;
    else
        return Cfib(n-1)+Cfib(n-2);
}

// Our Python binding to our C function
// This will take one and only one non-keyword argument
static PyObject *fib(PyObject* self, PyObject* args)
{
    // instantiate our `n` value
    int n;
    // if our `n` value
    if(!PyArg_ParseTuple(args, "i", &n))
        return NULL;
    // return our computed fib number
    

    int result = Cfib(n);
    return Py_BuildValue("i",result);
}

// Our Module's Function Definition struct
// We require this `NULL` to signal the end of our method
// definition
static PyMethodDef myMethods[] = {
    { "fib", fib, METH_VARARGS, "Calculate fibonacii value" },
    { NULL, NULL, 0, NULL }
};

// Our Module Definition struct
static struct PyModuleDef myModule = {
    PyModuleDef_HEAD_INIT,
    "myModule",
    "Test Module",
    -1,
    myMethods
};

// Initializes our module using our above struct
PyMODINIT_FUNC PyInit_myModule(void)
{
    return PyModule_Create(&myModule);
}

(.env) [boris@fedora33server FIBONACHI]$ cat setup.py
from distutils.core import setup, Extension
setup(name = 'myModule', version = '1.0',  \
   ext_modules = [Extension('myModule', ['test.c'])])

(.env) [boris@fedora33server FIBONACHI]python3 setup.py install

(.env) [boris@fedora33server FIBONACHI]$ cat MyProg.py
import myModule
a = int(input("Input number:"))
print(myModule.fib(a))

(.env) [boris@fedora33server FIBONACHI]$ python MyProg.py
Input number:11
89










PyCharm Project setup

Another Sample Factorial Recursion

(.env) [boris@fedora33server FACTORIAL]$ cat test.c
#include <Python.h>

    long factorial(int n)  
    {  
       if (n == 0)  
           return 1;  
       else  
          return(n * factorial(n-1));  
     }  

static PyObject *Factors(PyObject* self, PyObject* args)
{
    int n;
    if(!PyArg_ParseTuple(args, "i", &n))
        return NULL;
    int result = factorial(n);
    return Py_BuildValue("i",result);
}

// Our Module's Function Definition struct
// We require this `NULL` to signal the end of our method
// definition
static PyMethodDef myMethods[] = {
    { "Factors", Factors, METH_VARARGS, "Tracking factorial" },
    { NULL, NULL, 0, NULL }
};

// Our Module Definition struct
static struct PyModuleDef myModule = {
    PyModuleDef_HEAD_INIT,
    "myModule",
    "Test Module",
    -1,
    myMethods
};

// Initializes our module using our above struct
PyMODINIT_FUNC PyInit_myModule(void)
{
    return PyModule_Create(&myModule);
}

(.env) [boris@fedora33server FACTORIAL]$ cat setup.py 
from distutils.core import setup, Extension
setup(name = 'myModule', version = '1.0',  \
   ext_modules = [Extension('myModule', ['test.c'])])
   
(.env) [boris@fedora33server FACTORIAL]$ python setup.py install
running install
running build
running build_ext
building 'myModule' extension
creating build
creating build/temp.linux-x86_64-3.9
gcc -pthread -Wno-unused-result -Wsign-compare -DDYNAMIC_ANNOTATIONS_ENABLED=1 -DNDEBUG -O2 -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fstack-protector-strong -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -D_GNU_SOURCE -fPIC -fwrapv -O2 -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fstack-protector-strong -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -D_GNU_SOURCE -fPIC -fwrapv -O2 -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fstack-protector-strong -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -D_GNU_SOURCE -fPIC -fwrapv -fPIC -I/home/boris/FACTORIAL/.env/include -I/usr/include/python3.9 -c test.c -o build/temp.linux-x86_64-3.9/test.o
creating build/lib.linux-x86_64-3.9
gcc -pthread -shared -Wl,-z,relro -Wl,--as-needed -Wl,-z,now -g -Wl,-z,relro -Wl,--as-needed -Wl,-z,now -g build/temp.linux-x86_64-3.9/test.o -L/usr/lib64 -o build/lib.linux-x86_64-3.9/myModule.cpython-39-x86_64-linux-gnu.so
running install_lib
copying build/lib.linux-x86_64-3.9/myModule.cpython-39-x86_64-linux-gnu.so -> /home/boris/FACTORIAL/.env/lib64/python3.9/site-packages
running install_egg_info
Removing /home/boris/FACTORIAL/.env/lib64/python3.9/site-packages/myModule-1.0-py3.9.egg-info
Writing /home/boris/FACTORIAL/.env/lib64/python3.9/site-packages/myModule-1.0-py3.9.egg-info

(.env) [boris@fedora33server FACTORIAL]$ cat MyModule.py
import myModule
a = int(input("Input number:"))
print(myModule.Factors(a))

(.env) [boris@fedora33server FACTORIAL]$ python MyModule.py
Input number:7
5040
(.env) [boris@fedora33server FACTORIAL]$ python MyModule.py
Input number:6
720
(.env) [boris@fedora33server FACTORIAL]$ python MyModule.py
Input number:5
120

**************************************************************
You might want to take a look at following more complicated sample then considered earlier
**************************************************************

No comments:

Post a Comment