How the Python binding is created

Simplified overview

graph libnopegl_install(libnopegl install) pynopegl_setup(pynopegl setup.py) cython(Cython) cc(C compiler) specs(nodes.specs) c(_pynopegl.c) pyx(nodes_def.pyx) mod(_pynopegl.so) pymod(pynopegl) libnopegl_install -- write --> specs pynopegl_setup -- read --> specs pynopegl_setup -- write --> pyx cython -- read --> pyx cython -- write --> c cc -- read --> c cc -- write --> mod pymod -- import --> mod classDef installers fill:#8ee5ee,color:#222 classDef ctools fill:#ee6aa7,color:#222 classDef files fill:#7cfc00,color:#222 class libnopegl_install,pynopegl_setup installers class cython,cc ctools class specs,c,pyx files style mod fill:#ffd700,color:#222 style pymod fill:#ff7f50,color:#222

Detailed steps

libnopegl → nodes.specs

libnopegl writes the nodes.specs specifications in JSON using a dedicated tool (gen_specs.c) crawling the internal node C definitions.

This generated nodes.specs file (see updatespecs build rule in libnopegl’s generated Makefile) is installed on the system or targeted environment by the install rule.

nodes.specs ← pynopegl

In its setup.py, pynopegl uses pkg-config to query the libnopegl installation data directory, in order to obtain the path to the installed nodes.specs file. The file is then loaded as JSON file.

pynopegl → nodes_def.pyx

Using the loaded nodes.specs as JSON, setup.py writes the Cython code definitions into a nodes_def.pyx file.

nodes_def.pyx ← Cython

The setuptools module used to package pynopegl calls Cython to read the generated .pyx file (along with _pynopegl.pyx).

Cython → _pynopegl.c

From the .pyx files, Cython will generate a C source code using the C Python API. This source file declares the Python classes calling the libnopegl C functions, based on the rules contained in the .pyx files.

_pynopegl.c ← C compiler

In the Cython toolchain triggered by the setuptools, a C compiler will compile the generated C source.

C compiler → _pynopegl.so

Compiled source ends up being linked against Python library to create a _pynopegl.so loadable Python module.

pynopegl ← _pynopegl.so

The final binding exposed to the user is a traditional pure Python module, which imports the native _pynopegl module. All the nodes and their methods are dynamically generated into that module at runtime.