CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-cffi

Foreign Function Interface for Python calling C code.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

error-handling.mddocs/

Error Handling and Utilities

Error management, system integration utilities, and platform-specific functionality including errno handling and Windows Unicode support.

Capabilities

System Error Handling

Access and manipulation of C library error codes through the errno mechanism.

errno: property  # Property for C errno access

def _get_errno(self):
    """Get current errno value"""
    
def _set_errno(self, errno):
    """Set errno value"""

Usage Examples:

import os

# Access C errno
ffi = FFI()
ffi.cdef("FILE* fopen(const char* filename, const char* mode);")
libc = ffi.dlopen(None)

# Try to open non-existent file
result = libc.fopen(b"/nonexistent/file.txt", b"r")
if result == ffi.NULL:
    error_code = ffi.errno
    print(f"C errno: {error_code}")
    print(f"Python equivalent: {os.strerror(error_code)}")

# Set errno manually
ffi.errno = 42
print(f"Set errno to: {ffi.errno}")

Windows Error Handling

Windows-specific error code retrieval and formatting.

def getwinerror(self, code=-1):
    """
    Get Windows error information.
    
    Parameters:
    - code (int): Error code (-1 for GetLastError())
    
    Returns:
    tuple: (error_code, error_message) on Windows, OSError on other platforms
    """

Usage Example:

# Windows-specific error handling
if sys.platform == "win32":
    try:
        # Some Windows API call that might fail
        ffi.cdef("void* CreateFileA(const char* name, int access, int share, void* security, int creation, int flags, void* template);")
        kernel32 = ffi.dlopen("kernel32.dll")
        
        handle = kernel32.CreateFileA(b"invalid\\path\\file.txt", 0x80000000, 0, ffi.NULL, 3, 0, ffi.NULL)
        if handle == ffi.cast("void*", -1):  # INVALID_HANDLE_VALUE
            error_code, error_msg = ffi.getwinerror()
            print(f"Windows Error {error_code}: {error_msg}")
    except OSError:
        print("Not on Windows platform")

FFI Instance Inclusion

Includes type definitions from another FFI instance for modular design.

def include(self, ffi_to_include):
    """
    Include types from another FFI instance.
    
    Parameters:
    - ffi_to_include: Another FFI instance to include types from
    
    Returns:
    None
    
    Note: Only includes types, not functions or variables
    """

Usage Example:

# Base types FFI
base_ffi = FFI()
base_ffi.cdef("""
    typedef struct {
        int x, y;
    } point_t;
    
    typedef struct {
        point_t top_left;
        point_t bottom_right;
    } rect_t;
""")

# Graphics FFI that uses base types
graphics_ffi = FFI()
graphics_ffi.include(base_ffi)  # Include point_t and rect_t
graphics_ffi.cdef("""
    // Can now use point_t and rect_t
    void draw_rect(rect_t rect);
    point_t get_center(rect_t rect);
""")

Windows Unicode Support

Configures Windows-specific Unicode type definitions and macros.

def set_unicode(self, enabled_flag):
    """
    Configure Windows Unicode support.
    
    Parameters:
    - enabled_flag (bool): Enable Unicode types and macros
    
    Returns:
    None
    
    Note: Can only be called once per FFI instance
    """

Usage Example:

# Configure for Unicode Windows API
ffi = FFI()
ffi.set_unicode(True)  # Enables UNICODE and _UNICODE macros

# Now TCHAR maps to wchar_t, LPTSTR to wchar_t*, etc.
ffi.cdef("""
    int MessageBoxW(void* hWnd, const TCHAR* text, const TCHAR* caption, unsigned int type);
""")

# For ANSI API
ansi_ffi = FFI()
ansi_ffi.set_unicode(False)  # TCHAR maps to char

ansi_ffi.cdef("""
    int MessageBoxA(void* hWnd, const TCHAR* text, const TCHAR* caption, unsigned int type);
""")

One-Time Initialization

Executes functions exactly once per tag, useful for expensive initialization operations.

def init_once(self, func, tag):
    """
    Execute function once per tag.
    
    Parameters:
    - func: Function to execute
    - tag: Unique identifier for this initialization
    
    Returns:
    Result of func() on first call, cached result on subsequent calls
    """

Usage Example:

# Expensive initialization
def initialize_crypto():
    print("Initializing cryptographic library...")
    # Expensive setup code here
    return {"initialized": True, "algorithms": ["AES", "RSA"]}

ffi = FFI()

# Called multiple times, but initialization happens only once
result1 = ffi.init_once(initialize_crypto, "crypto_init")
result2 = ffi.init_once(initialize_crypto, "crypto_init")  # Uses cached result

print(result1 is result2)  # True - same object returned

Embedding Support

Advanced features for embedding Python in C applications.

def embedding_api(self, csource, packed=False, pack=None):
    """
    Define API for embedding Python in C.
    
    Parameters:
    - csource (str): C declarations for embedding API
    - packed (bool): Pack structures
    - pack (int): Packing alignment
    
    Returns:
    None
    """

def embedding_init_code(self, pysource):
    """
    Set Python initialization code for embedding.
    
    Parameters:
    - pysource (str): Python code to execute on embedding initialization
    
    Returns:
    None
    """

def def_extern(self, *args, **kwds):
    """
    Define external function for API mode (embedding).
    
    Note: Only available on API-mode FFI objects
    """

Usage Example:

# Embedding setup
embed_ffi = FFI()

# Define embedding API
embed_ffi.embedding_api("""
    int process_data(int* input, int count);
    char* get_status();
""")

# Set initialization code
embed_ffi.embedding_init_code("""
def process_data(input_ptr, count):
    # Convert C array to Python list
    data = ffi.unpack(ffi.cast("int*", input_ptr), count)
    
    # Process in Python
    result = sum(x * 2 for x in data)
    return result

def get_status():
    return ffi.new("char[]", b"Ready")

# Register functions
ffi.def_extern(process_data)
ffi.def_extern(get_status)
""")

Exception Classes and Handling

Exception Hierarchy

class FFIError(Exception):
    """Base exception for CFFI errors"""

class CDefError(Exception):
    """C declaration parsing errors"""
    
class VerificationError(Exception):
    """Code verification and compilation errors"""
    
class VerificationMissing(Exception):
    """Incomplete structure definition errors"""
    
class PkgConfigError(Exception):
    """Package configuration errors"""

Usage Examples:

try:
    ffi = FFI()
    ffi.cdef("invalid C syntax here")
except CDefError as e:
    print(f"C definition error: {e}")

try:
    ffi.cdef("struct incomplete;")
    incomplete = ffi.new("struct incomplete *")  # Error: incomplete type
except VerificationMissing as e:
    print(f"Incomplete structure: {e}")

try:
    ffi.verify("int func() { syntax error }")
except VerificationError as e:
    print(f"Compilation failed: {e}")

Advanced Error Handling Patterns

Error Context Management

class CFfiErrorContext:
    def __init__(self, ffi, operation_name):
        self.ffi = ffi
        self.operation_name = operation_name
        self.saved_errno = None
    
    def __enter__(self):
        # Save current errno
        self.saved_errno = self.ffi.errno
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is None:
            return
        
        # Add context to exceptions
        if isinstance(exc_val, (OSError, FFIError)):
            current_errno = self.ffi.errno
            if current_errno != self.saved_errno:
                exc_val.args = exc_val.args + (f"errno changed to {current_errno} during {self.operation_name}",)
        
        # Restore errno
        self.ffi.errno = self.saved_errno

# Usage
with CFfiErrorContext(ffi, "file operations"):
    # File operations that might change errno
    pass

Comprehensive Error Checking

def safe_library_call(ffi, lib, func_name, *args, check_errno=True, check_return=None):
    """Safely call C library function with error checking"""
    
    # Save errno
    old_errno = ffi.errno if check_errno else None
    if check_errno:
        ffi.errno = 0
    
    try:
        # Get function
        func = getattr(lib, func_name)
        
        # Call function
        result = func(*args)
        
        # Check return value
        if check_return and not check_return(result):
            raise RuntimeError(f"{func_name} returned error value: {result}")
        
        # Check errno
        if check_errno and ffi.errno != 0:
            error_msg = os.strerror(ffi.errno)
            raise OSError(ffi.errno, f"{func_name} failed: {error_msg}")
        
        return result
    
    finally:
        # Restore errno
        if check_errno and old_errno is not None:
            ffi.errno = old_errno

# Usage
ffi.cdef("FILE* fopen(const char* name, const char* mode);")
libc = ffi.dlopen(None)

try:
    file_ptr = safe_library_call(
        ffi, libc, "fopen", 
        b"test.txt", b"r",
        check_return=lambda x: x != ffi.NULL
    )
    print("File opened successfully")
except (OSError, RuntimeError) as e:
    print(f"Failed to open file: {e}")

Cross-Platform Error Handling

def get_system_error(ffi):
    """Get system error in a cross-platform way"""
    if sys.platform == "win32":
        try:
            error_code, error_msg = ffi.getwinerror()
            return f"Windows Error {error_code}: {error_msg}"
        except OSError:
            pass
    
    # Fall back to errno
    errno_val = ffi.errno
    if errno_val != 0:
        return f"System Error {errno_val}: {os.strerror(errno_val)}"
    
    return "No system error"

# Usage in error handling
try:
    # Some system operation
    pass
except Exception as e:
    system_error = get_system_error(ffi)
    print(f"Operation failed: {e}")
    print(f"System error: {system_error}")

Logging Integration

import logging

class CFfiLogger:
    def __init__(self, ffi, logger_name="cffi"):
        self.ffi = ffi
        self.logger = logging.getLogger(logger_name)
    
    def log_system_state(self, level=logging.DEBUG):
        """Log current system error state"""
        errno_val = self.ffi.errno
        self.logger.log(level, f"Current errno: {errno_val}")
        
        if sys.platform == "win32":
            try:
                win_error = self.ffi.getwinerror()
                self.logger.log(level, f"Windows error: {win_error}")
            except OSError:
                pass
    
    def wrap_call(self, func, *args, **kwargs):
        """Wrap function call with logging"""
        self.logger.debug(f"Calling {func.__name__} with args: {args}")
        self.log_system_state()
        
        try:
            result = func(*args, **kwargs)
            self.logger.debug(f"{func.__name__} returned: {result}")
            return result
        except Exception as e:
            self.logger.error(f"{func.__name__} failed: {e}")
            self.log_system_state(logging.ERROR)
            raise

# Usage
logger = CFfiLogger(ffi)
logger.wrap_call(libc.fopen, b"test.txt", b"r")

Install with Tessl CLI

npx tessl i tessl/pypi-cffi

docs

callbacks-handles.md

core-ffi.md

data-conversion.md

error-handling.md

index.md

memory-management.md

source-generation.md

type-system.md

tile.json