Python para Desenvolvedores

2ª edição, revisada e ampliada

Apêndice F: Integração com outras linguagens


Existe hoje muito código legado desenvolvido em diversas linguagens que pode ser aproveitado pelo Python, através de várias formas de integração.

Uma forma genérica de fazer isso é gerar uma biblioteca compartilhada (shared library) através do compilador da outra linguagem e fazer chamadas a funções que estão definidas na biblioteca.

Como a implementação original do Python é usando Linguagem C, é possível integrar Python e C nos dois sentidos:

  • Python -> C (Python faz chamadas a um módulo compilado em C).
  • C -> Python (C evoca o interpretador Python em modo embedded).

Também é possível integrar o Python com Fortran usando o utilitário f2py, que faz parte do projeto NumPy.

Bibliotecas compartilhadas

A partir da versão 2.5, o Python incorporou o módulo ctypes, que implementa tipos compatíveis com os tipos usados pela linguagem C e permite evocar funções de bibliotecas compartilhadas.

O módulo provê várias formas de evocar funções. Funções que seguem a convenção de chamada stdcall, como a API do Windows, podem ser acessadas através da classe windll. Dynamic-link library (DLL) é a implementação de bibliotecas compartilhadas que são usadas no Windows.

Exemplo com windll:

In [ ]:
import ctypes

# Evocando a caixa de mensagens do Windows
# Os argumentos são: janela pai, mensagem,
# título da janela e o tipo da janela.
# A função retorna um inteiro, que
# corresponde a que botão foi pressionado
i = ctypes.windll.user32.MessageBoxA(None,
    'Teste de DLL!', 'Mensagem', 0)

# O resultado indica qual botão foi clicado
print i

Para funções que seguem a convenção de chamada cdecl, usada pela maioria dos compiladores C, existe a classe cdll. Para as passagens de argumentos por referência é preciso criar uma variável que funciona como um buffer para receber os resultados. Isso é necessário para receber strings, por exemplo.

Exemplo com cdll e buffer:

In [ ]:
import ctypes

# msvcrt é a biblioteca com a maioria das funções
# padrões da linguagens C no Windows
# O Windows coloca automaticamente
# a extensão do arquivo
clib = ctypes.cdll.msvcrt

# Cria um buffer para receber o resultado
# a referência para o buffer será passada para
# a função, que preenche o buffer com o resultado
s = ctypes.c_buffer('\000', 40)

# sscanf() é uma função que extrai valores
# de uma string conforme uma mascara
clib.sscanf('Testando sscanf!\n',
    'Testando %s!\n', s)

# Mostra o resultado
print s.value

É possível também evocar funções de bibliotecas compartilhadas no Linux:

In [ ]:
import ctypes

# Carrega a biblioteca padrão C no Linux
# A extensão do arquivo precisa passada
# para a função LoadLibrary()
clib = ctypes.cdll.LoadLibrary("libc.so.6")

# Cria um buffer para receber o resultado
s = ctypes.c_buffer('\000', 40)

# Evoca a função sprintf
clib.sprintf(s, 'Testando %s\n', 'sprintf!')

# Mostra o resultado
print s.value

Através de bibliotecas compartilhadas é possível usar código desenvolvido em outras linguagens de uma maneira simples.

Python -> C

O módulo escrito em C deve utilizar as estruturas do Python (que estão definidas na API de interface) para se comunicar com o interpretador Python.

Exemplo:

In [ ]:
// Arquivo: mymodule.c

// Python.h define as estruturas do Python em C
#include <Python.h>

// No Python, mesmo os erros sao objetos
static PyObject *MyModuleError;

// Chamando a funcao "system" em C
static PyObject *
mymodule_system(PyObject *self, PyObject *args)
{
  const char *command;
  int sts;

  // "PyArg_ParseTuple" desempacota a tupla de parametros
  // "s" significa que ele deve identificar uma string
  if (!PyArg_ParseTuple(args, "s", &command))
    // retornando NULL gera uma excessao
    // caso falte parametros
    return NULL;

  // chamando "system":
  sts = system(command);

  // "Py_BuildValue" gera objetos que o Python conhece
  // "i" significa inteiro
  return Py_BuildValue("i", sts);
}

// Tabela que o Python consulta para resolver
// os metodos do modulo e pode ser usado
// tambem para gerar a documentacao
// por instrospeccao: dir(), help(),...
static PyMethodDef MyModuleMethods[] = {
  {"system", mymodule_system, METH_VARARGS,
  "Executa comandos externos."},
  // Fim da tabela:
  {NULL, NULL, 0, NULL}
};

// inicializacao do modulo:
PyMODINIT_FUNC
initmymodule(void)
{
  // O modulo tambem e' um objeto
  PyObject *m;

  // "Py_InitModule" precisa do nome do modulo e da
  // tabela de metodos
  m = Py_InitModule("mymodule", MyModuleMethods);

  // Erros...
  MyModuleError = PyErr_NewException("mymodule.error",
    NULL, NULL);

  // "Py_INCREF" incrementa o numero de referencias do objeto
  Py_INCREF(MyModuleError);

  // "PyModule_AddObject" adiciona um objeto ao modulo
  PyModule_AddObject(m, "error", MyModuleError);
}

Ao invés de compilar o módulo manualmente, use o Python para automatizar o processo. Primeiro, crie o script:

In [ ]:
# Arquivo: setup.py

from distutils.core import setup, Extension

mymodule = Extension('mymodule', sources = ['mymodule.c'])
setup(name = 'MyPackage', version = '1.0',
    description = 'My Package',
    ext_modules = [mymodule])

E para compilar:

python setup.py build

O binário compilado será gerado dentro da pasta “build”. O módulo pode ser usado como qualquer outro módulo no Python (através de import).

C -> Python

O inverso também é possível. Um programa escrito em C pode evocar o interpretador Python seguindo três passos:

  • Inicializar o interpretador.
  • Interagir (que pode ser feito de diversas formas).
  • Finalizar o interpretador.

Exemplo:

In [ ]:
// Arquivo: py_call.c

// Python.h com as definicoes para
// interagir com o interpretador
#include <Python.h>

int main()
{
  // Inicializa interpretador Python
  Py_Initialize();

  // Executando codigo Python
  PyRun_SimpleString("import os\n"
    "for f in os.listdir('.'):\n"
    "  if os.path.isfile(f):\n"
    "    print f, ':', os.path.getsize(f)\n");

  // Finaliza interpretador Python
  Py_Finalize();
  return 0;
}

Para compilar, é preciso passar a localização das headers e libraries do Python para o compilador C:

gcc -I/usr/include/python2.5 \
  -L/usr/lib/python2.5/config \
  -lpython2.5 -opy_call py_call.c

Observações:

  • Esta API faz parte do CPython (porte do Python escrito em C).
  • Existem ferramentas para automatizar o processo para gerar interfaces para sistemas maiores: SWIG, Boost.Python e SIP.
In [1]:
 
Out[1]: