🌐
Python
docs.python.org › 3 › c-api › index.html
Python/C API reference manual — Python 3.14.7 documentation
This manual documents the API used by C and C++ programmers who want to write extension modules or embed Python. It is a companion to Extending and Embedding the Python Interpreter, which describes...
🌐
GitHub
github.com › fpoli › python-c-api
GitHub - fpoli/python-c-api: Python/C API quick example · GitHub
A quick example of Python 3.10 modules implemented in C using the Python/C API.
Author: fpoli
🌐
Medium
medium.com › @wansac › the-python-c-api-a-brief-introduction-7926ea0ef488
The Python/C API: A Brief Introduction | by Ashley Wans | Medium
September 14, 2020 - The Python/C API allows C programmers to embed Python directly into C code by exposing aspects of CPython internals. It provides direct access to the Python interpreter from C, acting as a bridge between the two languages.
🌐
Python
docs.python.org › 3 › c-api › intro.html
Introduction — Python 3.14.7 documentation
Convert x to a C string. For example, Py_STRINGIFY(123) returns "123". Added in version 3.4. The following macros can be used in declarations. They are most useful for defining the C API itself, and have limited use for extension authors.
🌐
Cornell Virtual Workshop
cvw.cac.cornell.edu › python-performance › intro › cpython-api
Cornell Virtual Workshop > Python for High Performance > Overview > CPython and the Python/C API
The Python/C API allows for compiled pieces of code to be called from Python programs or executed within the CPython interpreter.
🌐
GitHub
github.com › dexpota › cpython-api-examples
GitHub - dexpota/cpython-api-examples: Some examples of using the C API of python 2.7. · GitHub
02 Python callback inside a C/C++ module: this recipe shows how to pass a python function into the module and call it inside the C/C++ module;
Starred by 5 users
Forked by 2 users
Languages: C++ 56.8% | CMake 21.2% | C 19.1% | Python 2.9%
🌐
Real Python
realpython.com › build-python-c-extension-module
Building a Python C Extension Module – Real Python
March 18, 2026 - The Python API has all the standard exceptions pre-defined as PyObject types. While you can’t raise exceptions in C, the Python API will allow you to raise exceptions from your Python C extension module. Let’s test this functionality by adding PyErr_SetString() to your code.
Find elsewhere
Top answer
1 of 1
3

When using functions exported by cdll.LoadLibrary, you're releasing the Global Interpreter Lock (GIL) as you enter the method. If you want to call python code, you need to re-acquire the lock.

e.g.

void someFunctionWithPython()
{
    ...
    PyGILState_STATE state = PyGILState_Ensure();
    printf("importing numpy...\n");
    PyObject* numpy = PyImport_ImportModule("numpy");
    if (numpy == NULL)
    {
        printf("Warning: error during import:\n");
        PyErr_Print();
        Py_Finalize();
        PyGILState_Release(state);
        exit(1);
    }

    PyObject* repr = PyObject_Repr(numpy);
    PyObject* str = PyUnicode_AsEncodedString(repr, "utf-8", "~E~");
    const char *bytes = PyBytes_AS_STRING(str);

    printf("REPR: %s\n", bytes);

    Py_XDECREF(repr);
    Py_XDECREF(str);

    PyGILState_Release(state);
    return;
}
$ gcc $(python3.9-config --includes --ldflags --embed) -shared -o mylibwithpy.so mylibwithpy.c
$ LD_LIBRARY_PATH=. python driver.py
opening mylibwithpy.so...
.so object:  <CDLL 'mylibwithpy.so', handle 1749f50 at 0x7fb603702fa0>
.so object's 'someFunctionWithPython':  <_FuncPtr object at 0x7fb603679040>
calling someFunctionWithPython...
python alread initialized.
importing numpy...
REPR: <module 'numpy' from '/home/me/test/.venv/lib/python3.9/site-packages/numpy/__init__.py'>

Also if you look at PyDLL it says:

Instances of this class behave like CDLL instances, except that the Python GIL is not released during the function call, and after the function execution the Python error flag is checked. If the error flag is set, a Python exception is raised.

So if you use PyDLL for your driver then you wouldn't need to re-acquire the lock in the C code:

from ctypes import PyDLL

if __name__ == "__main__":
    print("opening mylibwithpy.so...");
    my_so = PyDLL("mylibwithpy.so")

    print(".so object: ", my_so)
    print(".so object's 'someFunctionWithPython': ", my_so.someFunctionWithPython)

    print("calling someFunctionWithPython...");
    my_so.someFunctionWithPython()

UPDATE

Why do numpy's internal shared objects files not link to libpython3.8.so?

I believe numpy is setup this way because it expects to be called by the python interpreter where libpython will already be loaded and have the symbols made available.

That said, we can make the python libraries available for when mylibwithpy calls the import of numpy by using RTLD_GLOBAL.

The symbols defined by this shared object will be made available for symbol resolution of subsequently loaded shared objects.

The update to your code is simple:

void* mylibwithpy_so = dlopen("mylibwithpy.so", RTLD_LAZY | RTLD_GLOBAL);

Now all of the python libraries will be included because they are a dependency of mylibwithpy, meaning they will be available by the time that numpy loads its own shared libraries.

Alternatively, you could choose to load just libpythonX.Y.so with RTLD_GLOBAL to prior to loading mylibwithpy.so to minimize the amount symbols made globally available.

printf("opening libpython3.9.so...\n");
void* libpython3_so = dlopen("libpython3.9.so", RTLD_LAZY | RTLD_GLOBAL);
if (libpython3_so == NULL){
    printf("an error occurred during loading libpython3.9.so: \n%s\n", dlerror());
    exit(1);
}

printf("opening mylibwithpy.so...\n");
void* mylibwithpy_so = dlopen("mylibwithpy.so", RTLD_LAZY);
if (mylibwithpy_so == NULL){
    printf("an error occurred during loading mylibwithpy.so: \n%s\n", dlerror());
    exit(1);
}

Docker setup I used to recreate and test:

FROM ubuntu:20.04

ARG DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y \
   build-essential \
   python3.9-dev \
   python3.9-venv 

RUN mkdir /workspace
WORKDIR /workspace

RUN python3.9 -m venv .venv
RUN .venv/bin/python -m pip install numpy

COPY . /workspace

RUN gcc -o mylibwithpy.so mylibwithpy.c -fPIC -shared \
    $(python3.9-config --includes --ldflags --embed --cflags) 

RUN gcc -o cdriver driver.c -L/usr/lib/x86_64-linux-gnu -Wall -ldl

ENV LD_LIBRARY_PATH=/workspace
# Then run: . .venv/bin/activate && ./cdriver
🌐
GitHub
github.com › tbagrel1 › python_c_api
GitHub - tbagrel1/python_c_api: Example of use of Python-C API · GitHub
test__my_module.py: Python script which represents the project where the external module is required · Create Python module from the C one with python3 setup__my_module.py install (may need sudo rights on Linux)
Author: tbagrel1
🌐
SciPy Lecture Notes
scipy-lectures.org › advanced › interfacing_with_c › interfacing_with_c.html
2.8. Interfacing with C — Scipy lecture notes
Since reference counting bugs are easy to create and hard to track down, anyone really needing to use the Python C-API should read the section about objects, types and reference counts from the official python documentation. Additionally, there is a tool by the name of cpychecker which can help discover common errors with reference counting. The following C-extension module, make the cos function from the standard math library available to Python: /* Example of wrapping cos function from math.h with the Python-C-API.
🌐
Python
docs.python.org › 3 › c-api
Python/C API reference manual — Python 3.14.6 documentation
This manual documents the API used by C and C++ programmers who want to write extension modules or embed Python. It is a companion to Extending and Embedding the Python Interpreter, which describes...
Author: rpressiani
🌐
AskPython
askpython.com › python › examples › calling-python-scripts-from-c
Calling Python Scripts from C: A Step-by-Step Guide Using Python/C API - AskPython
April 10, 2025 - Here we don’t need to install any modules separately. Python/C API comes with Python packages and often provides header files like “Python.h”.
🌐
Python
docs.python.org › 3 › extending › extending.html
1. Extending Python with C or C++ — Python 3.14.7 documentation
For example, an extension module could implement a type “collection” which works like lists without order. Just like the standard Python list type has a C API which permits extension modules to create and manipulate lists, this new collection ...
🌐
Python Tips
book.pythontips.com › en › latest › python_c_extension.html
22. Python C extensions — Python Tips 0.1 documentation
In this example the C file is self explanatory - it contains two functions, one to add two integers and another to add two floats. In the python file, first the ctypes module is imported. Then the CDLL function of the ctypes module is used to load the shared lib file we created.
🌐
Cornell Virtual Workshop
cvw.cac.cornell.edu › python › api
Cornell Virtual Workshop: CPython and the Python/C API
The CPython interpreter (aka, "python") works by compiling Python source code to intermediate bytecodes, and then operating on those. CPython, which is written in C, is also accompanied by an Application Programming Interface (API) that enables communication between Python and C (and thus basically ...
🌐
GitHub
github.com › topics › python-c-api
python-c-api · GitHub Topics · GitHub
javascript python typescript ffi cpython hacktoberfest c-api python-c-api bun deno deno-ffi bun-ffi bun-python ... Examples of safe coding practice for Python C extensions.
🌐
Python
docs.python.org › 3.8 › c-api › intro.html
Introduction — Python 3.8.20 documentation
C++ users should note that although the API is defined entirely using C, the header files properly declare the entry points to be extern "C". As a result, there is no need to do anything special to use the API from C++. Several useful macros are defined in the Python header files. Many are defined closer to where they are useful (e.g. Py_RETURN_NONE). Others of a more general utility are defined here. This is not necessarily a complete listing. ... Use this when you have a code path that you do not expect to be reached. For example, in the default: clause in a switch statement for which all possible values are covered in case statements.