O módulo ctypes permite carregar bibliotecas compartilhadas e chamar funções C diretamente do Python. Ele fornece tipos compatíveis com C, ponteiros, estruturas, unions, arrays, callbacks e acesso a símbolos exportados por DLLs, arquivos .so e .dylib.
Esse poder vem com risco elevado. ctypes opera sobre memória nativa e contorna várias proteções do Python. Uma assinatura errada, ponteiro inválido ou callback coletado pelo garbage collector pode corromper dados, revelar memória, causar access violation ou encerrar o processo.
Quando usar ctypes
Use ctypes quando uma biblioteca C está disponível, não existe binding mantido e a API é estável o suficiente para ser descrita com precisão. Para APIs grandes, complexas ou críticas, considere gerar bindings com CFFI, Cython, pybind11 ou uma extensão C dedicada.
Antes de escrever o wrapper, confirme ABI, arquitetura, calling convention, tamanho dos tipos, ownership de memória, thread safety e política de erros.
Encontrar e carregar uma biblioteca
from ctypes import CDLL
from ctypes.util import find_library
nome = find_library("m")
if not nome:
raise RuntimeError("Biblioteca matemática não encontrada")
libm = CDLL(nome)
find_library() depende da plataforma. Em wrappers distribuídos, muitas vezes é melhor definir nomes e caminhos testados por sistema operacional em vez de aceitar qualquer arquivo fornecido pelo usuário.
Carregar uma biblioteca executa código nativo dentro do processo. Não permita upload ou caminho arbitrário para CDLL(). Verifique origem, assinatura e permissões.
Calling conventions
CDLL usa a convenção C padrão. No Windows, WinDLL usa stdcall e OleDLL interpreta retornos como HRESULT. Escolher a convenção errada altera a pilha e pode causar falhas.
Consulte o header C e a documentação da biblioteca. Não deduza a convenção pelo nome do arquivo.
Defina argtypes e restype
Funções são assumidas como retornando c_int quando restype não é definido. Essa suposição pode truncar ponteiros e inteiros de 64 bits.
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))
Defina protótipos antes da primeira chamada. argtypes valida e converte argumentos; restype interpreta corretamente o retorno.
Tipos C compatíveis
O módulo oferece c_int, c_uint, c_long, c_size_t, c_float, c_double, c_char_p, c_wchar_p, c_void_p e muitos outros. O tamanho de tipos como long varia entre plataformas.
Para protocolos binários, compare com o guia de struct no Python. struct é adequado para bytes com layout definido; ctypes.Structure tenta reproduzir o layout ABI do compilador.
Strings e buffers mutáveis
c_char_p aponta para string terminada em NUL e não deve ser usado como buffer mutável. Para uma função que escreve dados, crie um buffer.
from ctypes import create_string_buffer
buffer = create_string_buffer(256)
# função_c(buffer, len(buffer))
print(buffer.value)
Confirme se o tamanho inclui o terminador NUL e como a API comunica truncamento. Nunca entregue um buffer menor que o tamanho informado à função C.
Ponteiros e byref()
byref() passa um objeto por referência sem criar um ponteiro Python completo. pointer() cria um objeto de ponteiro reutilizável.
from ctypes import c_int, byref
resultado = c_int()
# status = biblioteca.calcular(10, byref(resultado))
# print(status, resultado.value)
ctypes detecta ponteiro NULL ao desreferenciar, mas não consegue validar um endereço não nulo incorreto. Indexar além de um array pode ler ou alterar memória arbitrária.
Estruturas e alinhamento
from ctypes import Structure, c_int
class Point(Structure):
_fields_ = [
("x", c_int),
("y", c_int),
]
O layout depende da ABI. Use sizeof(), offsets dos campos e testes contra um programa C de referência. _pack_, _align_ e _layout_ alteram o layout e não devem ser ajustados por tentativa.
Bit fields e unions variam entre compiladores. A documentação recomenda passar estruturas com bit fields e unions por ponteiro, não por valor.
Ownership de memória
Uma função C pode retornar memória estática, um ponteiro que deve ser liberado, ou um buffer pertencente a outro objeto. O wrapper precisa documentar quem é responsável pela liberação e qual função deve ser usada.
Nunca libere memória alocada por uma biblioteca com um allocator diferente. Em especial no Windows, runtimes C diferentes podem ter heaps incompatíveis.
lib.criar_buffer.restype = c_void_p
lib.liberar_buffer.argtypes = [c_void_p]
ponteiro = lib.criar_buffer()
if not ponteiro:
raise MemoryError("Falha ao alocar buffer nativo")
try:
pass # use o ponteiro com limites conhecidos
finally:
lib.liberar_buffer(ponteiro)
Erros com errno
Carregue a biblioteca com use_errno=True quando a API usa errno. Depois da chamada, consulte get_errno().
import os
from ctypes import CDLL, get_errno
lib = CDLL("libexemplo.so", use_errno=True)
resultado = lib.operacao()
if resultado == -1:
codigo = get_errno()
raise OSError(codigo, os.strerror(codigo))
O próximo conjunto deste lote abordará o módulo errno em detalhes.
errcheck
O atributo errcheck centraliza a interpretação do retorno.
def verificar(resultado, funcao, argumentos):
if resultado == 0:
codigo = get_errno()
raise OSError(codigo, os.strerror(codigo))
return resultado
lib.operacao.errcheck = verificar
Confirme o contrato da API: algumas funções usam zero como sucesso, outras usam zero como falha ou retornam ponteiro NULL.
Callbacks
CFUNCTYPE e WINFUNCTYPE transformam uma função Python em ponteiro chamável por C.
from ctypes import CFUNCTYPE, c_int
COMPARADOR = CFUNCTYPE(c_int, c_int, c_int)
@COMPARADOR
def comparar(a, b):
return (a > b) - (a < b)
Mantenha uma referência Python ao callback enquanto o código C puder chamá-lo. Caso contrário, ele pode ser coletado e a próxima chamada causará crash.
Exceções não devem escapar de callbacks. Capture-as, registre um sinal de erro e retorne um valor definido pelo contrato C.
Threads e GIL
CDLL normalmente libera o GIL durante chamadas nativas. Isso não torna a biblioteca thread-safe. Proteja estados compartilhados conforme a documentação C.
Em builds free-threaded do Python, acessos concorrentes à mesma memória por objetos de ponteiro diferentes exigem sincronização explícita. Use threading.Lock.
Segfaults e diagnóstico
Um segfault não é uma exceção Python comum. Ative faulthandler, execute testes em subprocesso e use ferramentas como AddressSanitizer, Valgrind ou debugger nativo.
Para funções de alto risco, isole o wrapper em outro processo. Se a biblioteca falhar, o serviço principal pode reiniciar o worker sem perder o processo inteiro.
Validação de limites
Antes de chamar C, valide tamanho, intervalo e consistência. Não confie em casts que silenciosamente truncam inteiros. Verifique que contadores cabem no tipo C escolhido e que arrays têm ao menos o número de elementos informado.
Evite cast() sem necessidade
cast() reinterpreta o mesmo endereço como outro tipo. Ele não converte conteúdo nem valida alinhamento. Use somente quando o contrato C exige e o layout está comprovado.
Testes multiplataforma
Teste Windows, Linux e macOS quando suportados, em arquiteturas de 64 bits e versões reais da biblioteca. Verifique sizeof(), endianness, calling convention, símbolos, encoding e mensagens de erro.
O guia de inspect no Python pode ajudar a validar a camada Python, mas não inspeciona a ABI nativa. Testes contra headers e versões da biblioteca são indispensáveis.
Segurança da cadeia de fornecimento
Bibliotecas nativas executam com os privilégios do processo. Fixe versões, valide hashes, use fontes confiáveis e limite os diretórios de busca. Variáveis como LD_LIBRARY_PATH podem alterar qual arquivo é carregado.
Para verificar binários distribuídos, veja hashlib no Python.
Erros comuns
Os erros mais frequentes são omitir argtypes, aceitar o restype padrão, passar string imutável como buffer, usar calling convention errada, perder a referência do callback, liberar memória com allocator incorreto, indexar ponteiros sem tamanho e carregar DLL de caminho não confiável.
Conclusão
ctypes permite criar wrappers nativos sem compilar uma extensão Python, mas exige disciplina semelhante à programação C. Defina protótipos, valide ABI e tamanhos, documente ownership, mantenha callbacks vivos e trate crashes como possibilidade real.
Consulte a documentação oficial de ctypes e a documentação oficial sobre extensões e integração com C.







