ctypes en Python: usa bibliotecas C

Publicado el: 24/08/2026
Tempo de leitura: 6 minutos
African American man using a laptop in a well-organized library setting with colorful bookshelves.

El módulo ctypes permite cargar bibliotecas compartidas y llamar funciones C directamente desde Python. Proporciona tipos compatibles con C, punteros, estructuras, unions, arrays, callbacks y acceso a símbolos exportados por DLL, archivos .so y .dylib.

Este poder implica un riesgo elevado. ctypes trabaja con memoria nativa y evita varias garantías de seguridad de Python. Una firma incorrecta, un puntero inválido, una calling convention equivocada o un callback recolectado pueden corromper datos, revelar memoria, provocar access violations o cerrar el proceso.

Cuándo usar ctypes

Usa ctypes cuando existe una biblioteca C estable, no hay un binding mantenido y la API requerida puede describirse con precisión. Para APIs grandes, complejas o críticas, considera CFFI, Cython, pybind11 o una extensión compilada dedicada.

Antes de escribir el wrapper, confirma ABI, arquitectura, calling convention, tamaño de tipos, ownership de memoria, thread safety y contrato de errores.

Encontrar y cargar una biblioteca

from ctypes import CDLL
from ctypes.util import find_library

nombre = find_library("m")
if not nombre:
    raise RuntimeError("No se encontró la biblioteca matemática")

libm = CDLL(nombre)

find_library() depende de la plataforma. Un wrapper distribuible suele funcionar mejor con nombres y rutas probados por sistema operativo que aceptando una ruta arbitraria del usuario.

Cargar una biblioteca nativa ejecuta código dentro del proceso. No pases un archivo subido o una ruta no confiable directamente a CDLL(). Valida origen, versión, integridad, permisos y directorios de búsqueda.

Calling conventions

CDLL usa la convención C estándar. En Windows, WinDLL usa stdcall y OleDLL interpreta retornos como HRESULT. Elegir la convención incorrecta puede corromper la pila.

Consulta headers y documentación del proveedor. No deduzcas la convención por el nombre del archivo o función.

Define argtypes y restype

Las funciones extranjeras se consideran como si devolvieran c_int cuando no se define restype. Ese default puede truncar punteros, enteros de 64 bits, timestamps y tamaños.

from ctypes import CDLL, c_double
from ctypes.util import find_library

libm = CDLL(find_library("m"))
cos = libm.cos
cos.argtypes = [c_double]
cos.restype = c_double

print(cos(0.0))

Define prototipos antes de la primera llamada. argtypes valida y convierte argumentos; restype interpreta correctamente el retorno.

Tipos compatibles con C

El módulo incluye c_int, c_uint, c_long, c_size_t, c_float, c_double, c_char_p, c_wchar_p, c_void_p y enteros de ancho fijo. El tamaño de C long cambia entre plataformas.

Para protocolos binarios con formato explícito, compara con struct en Python. struct trabaja con layouts de bytes definidos; ctypes.Structure suele seguir la ABI nativa.

Strings y buffers mutables

c_char_p representa un puntero a string terminado en NUL. No debe usarse cuando la función C escribirá en memoria. Usa un buffer mutable.

from ctypes import create_string_buffer

buffer = create_string_buffer(256)
# funcion_c(buffer, len(buffer))
print(buffer.value)

Confirma si el tamaño incluye el NUL final y cómo la API informa truncamiento. Nunca declares un tamaño mayor que la asignación real.

Punteros y byref()

byref() pasa un objeto por referencia con poco overhead. pointer() crea un objeto puntero reutilizable.

from ctypes import c_int, byref

resultado = c_int()
# estado = biblioteca.calcular(10, byref(resultado))
# print(estado, resultado.value)

ctypes detecta acceso a puntero NULL, pero no puede validar una dirección no nula incorrecta. Indexar fuera de un array puede leer o sobrescribir memoria arbitraria.

Estructuras, alineación y layout

from ctypes import Structure, c_int

class Punto(Structure):
    _fields_ = [
        ("x", c_int),
        ("y", c_int),
    ]

El layout depende de la ABI. Verifica sizeof(), offsets, alineación y byte order contra el header y un programa C de referencia. No ajustes _pack_, _align_ o _layout_ por prueba y error.

Bit fields y unions dependen del compilador. La documentación recomienda pasar estructuras o unions con bit fields por puntero, no por valor.

Ownership de memoria

Una función C puede devolver memoria estática, un puntero prestado, una referencia ligada a otro objeto o un buffer nuevo que debe liberarse. Un wrapper seguro documenta ownership y expone la función correcta de liberación.

No liberes memoria con un allocator distinto. En Windows, runtimes C diferentes pueden usar heaps incompatibles.

from ctypes import c_void_p

lib.crear_buffer.restype = c_void_p
lib.liberar_buffer.argtypes = [c_void_p]

puntero = lib.crear_buffer()
if not puntero:
    raise MemoryError("Falló la asignación nativa")
try:
    pass  # Usar solo dentro de límites documentados
finally:
    lib.liberar_buffer(puntero)

errno y errores del sistema

Carga la biblioteca con use_errno=True cuando la API informa fallos mediante errno. Lee la copia thread-local de ctypes inmediatamente después.

import os
from ctypes import CDLL, get_errno

lib = CDLL("libejemplo.so", use_errno=True)
resultado = lib.operacion()
if resultado == -1:
    codigo = get_errno()
    raise OSError(codigo, os.strerror(codigo))

El próximo artículo de este lote profundiza en las constantes de errno.

Centralizar validación con errcheck

def comprobar(resultado, funcion, argumentos):
    if resultado == 0:
        codigo = get_errno()
        raise OSError(codigo, os.strerror(codigo))
    return resultado

lib.operacion.errcheck = comprobar

El contrato de éxito depende de la API. Algunas funciones devuelven cero en éxito; otras, cero o NULL en fallo; otras usan valores negativos.

Callbacks de C a Python

CFUNCTYPE y WINFUNCTYPE crean punteros C respaldados por callables de Python.

from ctypes import CFUNCTYPE, c_int

COMPARAR = CFUNCTYPE(c_int, c_int, c_int)

@COMPARAR
def comparar(a, b):
    return (a > b) - (a < b)

Mantén una referencia fuerte mientras el código nativo pueda llamar el callback. Si se recolecta, una invocación posterior puede cerrar el intérprete.

No permitas que excepciones escapen del callback. Captúralas, guarda un estado controlado y devuelve un valor permitido por el contrato C.

Threads y GIL

Las llamadas por CDLL normalmente liberan el GIL. Eso no vuelve thread-safe la biblioteca. Sincroniza el estado según su documentación.

En builds free-threaded, accesos simultáneos a la misma dirección mediante diferentes punteros pueden requerir threading.Lock.

Segmentation faults e aislamiento

Un segmentation fault no es una excepción normal. Activa faulthandler, ejecuta tests riesgosos en subprocesses y usa AddressSanitizer, Valgrind o debugger nativo.

Para bibliotecas inestables, aísla el binding en un worker. Un crash termina solo el worker y no el servicio principal.

Validar tamaños y rangos

Antes de entrar en C, verifica longitudes, rangos, cantidades de elementos, relaciones entre parámetros y supuestos de punteros. No dependas del truncamiento silencioso. Confirma que los valores caben en el tipo C declarado.

Evitar cast() innecesario

cast() reinterpreta la misma dirección como otro tipo. No convierte datos, no corrige alineación y no valida tamaño. Úsalo solo cuando el contrato nativo lo exige y la vida del objeto original está garantizada.

Tests multiplataforma

Prueba cada sistema operativo, arquitectura y versión de biblioteca soportada. Verifica símbolos, tamaños, calling convention, offsets, alineación, Unicode y errores.

inspect en Python ayuda a validar la capa Python, pero no demuestra la ABI nativa. Los tests contra headers y código C de referencia siguen siendo obligatorios.

Seguridad de la cadena de suministro

Las bibliotecas nativas ejecutan con los privilegios del proceso. Fija versiones, valida hashes, usa fuentes confiables y restringe directorios de búsqueda. Variables como LD_LIBRARY_PATH o la ruta de DLL de Windows pueden alterar el binario cargado.

Para verificar integridad, consulta hashlib en Python.

Errores comunes

Los fallos frecuentes son omitir argtypes, aceptar el restype default, pasar strings inmutables como buffers, elegir calling convention incorrecta, perder referencias de callbacks, liberar con otro allocator, indexar punteros sin longitud y cargar DLL desde rutas no confiables.

Conclusión

ctypes permite crear bindings nativos sin compilar una extensión, pero exige disciplina similar a C. Define prototipos, valida ABI y tamaños, documenta ownership, conserva callbacks y trata los crashes como posibilidad real.

Consulta la documentación oficial de ctypes y la documentación oficial sobre extensión e integración con C.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    A person reads 'Python for Unix and Linux System Administration' indoors.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    expat en Python: parser XML de bajo nivel

    Aprende expat en Python para parsing XML de bajo nivel, handlers, namespaces, diagnósticos y protección contra amplificación y DoS.

    Ler mais

    Tempo de leitura: 5 minutos
    23/08/2026
    Detailed shot of a Jungle Carpet Python (Morelia spilota cheynei) in its natural habitat.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    ElementInclude en Python: XInclude seguro

    Aprende ElementInclude en Python para usar XInclude con loaders seguros, base URL, profundidad máxima y bloqueo de rutas externas.

    Ler mais

    Tempo de leitura: 5 minutos
    23/08/2026
    A person reads 'Python for Unix and Linux System Administration' indoors.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    xmlreader en Python: controla parsers SAX

    Aprende xmlreader en Python para configurar parsers SAX, InputSource, parsing incremental, atributos, locators y seguridad XML.

    Ler mais

    Tempo de leitura: 5 minutos
    23/08/2026
    High-angle view of woman coding on a laptop, with a Python book nearby. Ideal for programming and tech content.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    saxutils en Python: utilidades para XML

    Aprende saxutils en Python para escapar XML, preparar atributos, generar documentos, crear filtros SAX y evitar errores de contexto.

    Ler mais

    Tempo de leitura: 5 minutos
    23/08/2026
    High-angle view of woman coding on a laptop, with a Python book nearby. Ideal for programming and tech content.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    pulldom en Python: DOM parcial para XML

    Aprende pulldom en Python para procesar XML por eventos, expandir subárboles selectivos y reducir memoria con límites seguros.

    Ler mais

    Tempo de leitura: 5 minutos
    23/08/2026
    Close-up of a laptop screen with code and a coffee mug, perfect for tech abstract themes.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    xml.sax en Python: procesa XML por eventos

    Aprende xml.sax en Python para procesar XML por eventos con poca memoria, namespaces, handlers, límites y entidades seguras.

    Ler mais

    Tempo de leitura: 5 minutos
    22/08/2026