Foreign Function Interface for Python calling C code.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Advanced features for generating and compiling C extensions at runtime. These capabilities enable complex integration scenarios and performance optimization through automatic code generation and compilation.
Associates C source code with FFI declarations for compilation into Python extensions.
def set_source(self, module_name, source, source_extension='.c', **kwds):
"""
Set C source code for compilation.
Parameters:
- module_name (str): Name of the generated module
- source (str): C source code
- source_extension (str): File extension ('.c' or '.cpp')
- **kwds: Compilation options (include_dirs, libraries, etc.)
Returns:
None (configures FFI for compilation)
"""Usage Examples:
ffi = FFI()
ffi.cdef("""
int add(int a, int b);
double calculate_pi(int iterations);
""")
# Set C source code
ffi.set_source("_math_ext", """
#include <math.h>
int add(int a, int b) {
return a + b;
}
double calculate_pi(int iterations) {
double pi = 0.0;
for (int i = 0; i < iterations; i++) {
pi += (i % 2 == 0 ? 1.0 : -1.0) / (2 * i + 1);
}
return pi * 4.0;
}
""",
libraries=['m'], # Link with math library
include_dirs=['/usr/local/include']
)Automatically configures compilation flags using pkg-config for system libraries.
def set_source_pkgconfig(self, module_name, pkgconfig_libs, source, source_extension='.c', **kwds):
"""
Set source with pkg-config library detection.
Parameters:
- module_name (str): Generated module name
- pkgconfig_libs (list): List of pkg-config package names
- source (str): C source code
- source_extension (str): File extension
- **kwds: Additional compilation options
Returns:
None
"""Usage Example:
ffi = FFI()
ffi.cdef("""
// OpenSSL functions
void SHA256_Init(void *ctx);
void SHA256_Update(void *ctx, const void *data, size_t len);
void SHA256_Final(unsigned char *md, void *ctx);
""")
# Use pkg-config to find OpenSSL
ffi.set_source_pkgconfig("_crypto_ext",
["openssl"],
"""
#include <openssl/sha.h>
// Wrapper functions if needed
""")Compiles the configured source code into a loadable Python extension module.
def compile(self, tmpdir='.', verbose=0, target=None, debug=None):
"""
Compile configured source into extension module.
Parameters:
- tmpdir (str): Directory for temporary and output files
- verbose (int): Verbosity level for compilation
- target (str): Target filename ('*' for default, '*.ext' for custom)
- debug (bool): Enable debug compilation
Returns:
str: Path to compiled extension module
"""Usage Example:
# After set_source(), compile the extension
extension_path = ffi.compile(tmpdir='./build', verbose=1)
# Load the compiled module
spec = importlib.util.spec_from_file_location("_math_ext", extension_path)
math_ext = importlib.util.module_from_spec(spec)
spec.loader.exec_module(math_ext)
# Use the compiled functions
result = math_ext.lib.add(5, 3) # 8
pi_approx = math_ext.lib.calculate_pi(100000)Generates C or Python source code files without compilation.
def emit_c_code(self, filename):
"""
Generate C source code file.
Parameters:
- filename (str): Output C file path
Returns:
None
"""
def emit_python_code(self, filename):
"""
Generate Python wrapper code file.
Parameters:
- filename (str): Output Python file path
Returns:
None
"""Usage Examples:
# Generate C extension code
ffi.emit_c_code("generated_extension.c")
# Generate Python wrapper for dlopen() style
ffi_pure = FFI()
ffi_pure.cdef("int add(int a, int b);")
ffi_pure.set_source("_math_pure", None) # None for dlopen() style
ffi_pure.emit_python_code("math_wrapper.py")Creates distutils Extension objects for integration with setup.py build systems.
def distutils_extension(self, tmpdir='build', verbose=True):
"""
Create distutils Extension object.
Parameters:
- tmpdir (str): Build directory
- verbose (bool): Print generation status
Returns:
distutils.extension.Extension: Extension object for setup.py
"""Usage Example:
# In build script or setup.py
ffi = FFI()
ffi.cdef("int multiply(int a, int b);")
ffi.set_source("_calc_ext", "int multiply(int a, int b) { return a * b; }")
# Get extension for setup.py
ext = ffi.distutils_extension()
# Use in setup.py
from setuptools import setup
setup(
name="my_package",
ext_modules=[ext],
zip_safe=False,
)def create_complex_extension():
ffi = FFI()
# Define complete interface
ffi.cdef("""
// Data structures
typedef struct {
double x, y, z;
} vector3_t;
// Core functions
vector3_t vector_add(vector3_t a, vector3_t b);
double vector_length(vector3_t v);
// Utility functions
void print_vector(vector3_t v);
""")
# Multi-part source code
includes = """
#include <math.h>
#include <stdio.h>
"""
data_structures = """
typedef struct {
double x, y, z;
} vector3_t;
"""
implementations = """
vector3_t vector_add(vector3_t a, vector3_t b) {
vector3_t result = {a.x + b.x, a.y + b.y, a.z + b.z};
return result;
}
double vector_length(vector3_t v) {
return sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
}
void print_vector(vector3_t v) {
printf("Vector(%.2f, %.2f, %.2f)\\n", v.x, v.y, v.z);
}
"""
full_source = includes + data_structures + implementations
ffi.set_source("_vector_ext", full_source,
libraries=['m'])
return ffi
# Build and use
vector_ffi = create_complex_extension()
lib_path = vector_ffi.compile()def create_platform_extension():
ffi = FFI()
# Platform-specific declarations
ffi.cdef("""
#ifdef _WIN32
void windows_specific_func();
#else
void unix_specific_func();
#endif
void cross_platform_func();
""")
# Platform-specific source
source = """
#ifdef _WIN32
#include <windows.h>
void windows_specific_func() {
// Windows implementation
OutputDebugStringA("Windows function called\\n");
}
#else
#include <unistd.h>
void unix_specific_func() {
// Unix implementation
write(STDOUT_FILENO, "Unix function called\\n", 22);
}
#endif
void cross_platform_func() {
// Cross-platform implementation
}
"""
# Platform-specific compilation options
import sys
if sys.platform == "win32":
libraries = ['kernel32']
define_macros = [('_WIN32', '1')]
else:
libraries = []
define_macros = []
ffi.set_source("_platform_ext", source,
libraries=libraries,
define_macros=define_macros)
return ffidef create_templated_extension(type_name, c_type):
"""Generate type-specific extensions"""
ffi = FFI()
# Template declarations
declarations = f"""
typedef struct {{
{c_type} data;
size_t size;
}} {type_name}_container_t;
{type_name}_container_t* {type_name}_create(size_t size);
void {type_name}_destroy({type_name}_container_t* container);
{c_type} {type_name}_get({type_name}_container_t* container, size_t index);
void {type_name}_set({type_name}_container_t* container, size_t index, {c_type} value);
"""
ffi.cdef(declarations)
# Template implementation
source = f"""
#include <stdlib.h>
typedef struct {{
{c_type}* data;
size_t size;
}} {type_name}_container_t;
{type_name}_container_t* {type_name}_create(size_t size) {{
{type_name}_container_t* container = malloc(sizeof({type_name}_container_t));
container->data = calloc(size, sizeof({c_type}));
container->size = size;
return container;
}}
void {type_name}_destroy({type_name}_container_t* container) {{
if (container) {{
free(container->data);
free(container);
}}
}}
{c_type} {type_name}_get({type_name}_container_t* container, size_t index) {{
if (index < container->size) {{
return container->data[index];
}}
return 0; // Error value
}}
void {type_name}_set({type_name}_container_t* container, size_t index, {c_type} value) {{
if (index < container->size) {{
container->data[index] = value;
}}
}}
"""
ffi.set_source(f"_{type_name}_container", source)
return ffi
# Generate specialized containers
int_container = create_templated_extension("int", "int")
float_container = create_templated_extension("float", "float")# build_extensions.py
from cffi import FFI
def build_math_extension():
ffi = FFI()
ffi.cdef("double fast_sin(double x);")
ffi.set_source("_fast_math", """
#include <math.h>
double fast_sin(double x) {
// Optimized sine implementation
return sin(x);
}
""", libraries=['m'])
return ffi.distutils_extension()
# setup.py
from setuptools import setup
from build_extensions import build_math_extension
setup(
name="my_math_package",
ext_modules=[build_math_extension()],
cffi_modules=["build_extensions.py:build_math_extension"],
setup_requires=["cffi>=1.0.0"],
install_requires=["cffi>=1.0.0"],
)def generate_cmake_compatible():
"""Generate extensions compatible with CMake builds"""
ffi = FFI()
ffi.cdef("void process_data(double* input, double* output, size_t count);")
# Generate source that can be built with CMake
ffi.emit_c_code("cffi_generated.c")
ffi.emit_c_code("cffi_generated.h") # If header generation supported
# Create CMakeLists.txt content
cmake_content = """
cmake_minimum_required(VERSION 3.10)
project(CFfiExtension)
find_package(Python3 COMPONENTS Interpreter Development REQUIRED)
add_library(cffi_extension SHARED
cffi_generated.c
)
target_link_libraries(cffi_extension Python3::Python)
"""
with open("CMakeLists.txt", "w") as f:
f.write(cmake_content)CFFI provides utilities for integrating with setuptools build systems.
# From cffi.setuptools_ext module
def add_cffi_module(dist, mod_spec):
"""
Add CFFI module to distribution.
Parameters:
- dist: Distribution object
- mod_spec (str): Module specification in format "path/build.py:ffi_variable"
Returns:
None (modifies distribution)
"""Usage Example:
# myproject_build.py
from cffi import FFI
ffi = FFI()
ffi.cdef("int calculate(int x, int y);")
ffi.set_source("_myproject", """
int calculate(int x, int y) {
return x * y + x + y;
}
""")
# setup.py
from setuptools import setup
setup(
name="myproject",
cffi_modules=["myproject_build.py:ffi"],
setup_requires=["cffi>=1.0.0"],
install_requires=["cffi>=1.0.0"],
)def create_optimized_extension():
ffi = FFI()
ffi.cdef("""
double fast_math_operation(double x, double y);
""")
# Use inline functions for performance
source = """
#include <math.h>
static inline double inline_helper(double x) {
return x * x + 2 * x + 1;
}
double fast_math_operation(double x, double y) {
return inline_helper(x) + inline_helper(y);
}
"""
ffi.set_source("_optimized_math", source,
extra_compile_args=['-O3', '-finline-functions'])
return ffidef create_simd_extension():
ffi = FFI()
ffi.cdef("""
void vector_add_simd(float* a, float* b, float* result, size_t count);
""")
source = """
#ifdef __SSE__
#include <xmmintrin.h>
#endif
void vector_add_simd(float* a, float* b, float* result, size_t count) {
#ifdef __SSE__
size_t simd_count = count & ~3; // Process 4 elements at a time
for (size_t i = 0; i < simd_count; i += 4) {
__m128 va = _mm_load_ps(&a[i]);
__m128 vb = _mm_load_ps(&b[i]);
__m128 vr = _mm_add_ps(va, vb);
_mm_store_ps(&result[i], vr);
}
// Handle remaining elements
for (size_t i = simd_count; i < count; i++) {
result[i] = a[i] + b[i];
}
#else
for (size_t i = 0; i < count; i++) {
result[i] = a[i] + b[i];
}
#endif
}
"""
ffi.set_source("_simd_math", source,
extra_compile_args=['-msse', '-O3'])
return ffiInstall with Tessl CLI
npx tessl i tessl/pypi-cffi