I am having trouble making a simple "Hello World" python function which I can call from a C program.
Here is the contents of my helloworld.py file:
def hw():
print("Hello World")
Here is the contents of the caller.pyx file:
from helloworld import hw
cdef public void call_hw():
hw()
And here is the contents of my main.c file:
#include <Python.h>
#include "caller.h"
int
main()
{
Py_Initialize();
call_hw();
Py_Finalize();
}
Here are the commands I do:
$ cython caller.pyx
$ gcc -g -Wall -I/usr/include/python3.12 -c caller.c
$ gcc -g -Wall -I/usr/include/python3.12 -c main.c
$ gcc -g -Wall -I/usr/include/python3.12 -o main *.o -lpython3.12
$ ./main
Segmentation fault (core dumped)
Here is a backtrace:
Program received signal SIGSEGV, Segmentation fault.
0x0000555555558898 in __Pyx__GetModuleGlobalName (name=0x0) at caller.c:2739
2739 result = _PyDict_GetItem_KnownHash(__pyx_d, name, ((PyASCIIObject *) name)->hash);
(gdb) bt
#0 0x0000555555558898 in __Pyx__GetModuleGlobalName (name=0x0) at caller.c:2739
#1 0x0000555555556d9a in call_hw () at caller.c:2002
#2 0x000055555555cef6 in main () at main.c:8
Can anyone tell me what I'm doing wrong?
caller is still a Python module, and it needs to be initialised and imported before you can use it or any of its functions. That is, you need to ensure the from helloworld import hw bit of python code is run. See the documentation on embedding for more details. Further documentation on using Cython Declarations from C can be found here.
The key additions you need are:
PyImport_AppendInittabPyImport_ImportModuleFor example, main.c would become:
#include <Python.h>
#include <stdio.h>
#include "caller.h"
int
main()
{
PyObject *pmodule;
/* Add a built-in module, before Py_Initialize
PyInit_caller is generated code from caller.c
* NB.the name will vary with the module name */
if (PyImport_AppendInittab("caller", PyInit_caller) == -1) {
fprintf(stderr, "Error: could not extend in-built modules table\n");
exit(1);
}
/* Initialize the Python interpreter. Required.
If this step fails, it will be a fatal error. */
Py_Initialize();
/* Optionally set import path here */
// PyRun_SimpleString("import sys\nsys.path.insert(0,'')");
/* Import the module.
If this step fails, it will be a fatal error. */
pmodule = PyImport_ImportModule("caller");
if (!pmodule) {
PyErr_Print();
fprintf(stderr, "Error: could not import module 'caller'\n");
goto exit_with_error;
}
/* your code goes here */
call_hw();
/* end of program */
Py_Finalize();
return 0;
/* Clean up in the error cases above. */
exit_with_error:
Py_Finalize();
return 1;
}
However, that is not all you need to do. You will also need to tell the interpreter where it can look when importing modules. By default, the current working directory is added when running normal python scripts. This is not the case for embedded scripts. Instead you will need to manually do this. One simple way is to set the environment variable PYTHONPATH to .. For instance:
PYTHONPATH=. ./main
Alternatively, you can put the following before your first module import (but after you initialise the interpreter) in main.c:main()
PyRun_SimpleString("import sys\nsys.path.insert(0,'')");
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With