FAQ sobre Extensão/Incorporação¶
Sumário
FAQ sobre Extensão/Incorporação
Como posso executar instruções arbitrárias de Python a partir de C?
Como posso executar e obter o resultado de uma expressão Python arbitrária a partir de C?
Como posso utilizar Py_BuildValue() para criar uma tupla de comprimento arbitrário?
Como faço para acessar a partir do C um módulo escrito em Python?
Adicionei um módulo usando o arquivo de Setup e o make falha; por quê?
Quero compilar um módulo Python no meu sistema Linux, mas alguns arquivos estão faltando. Por quê?
Como posso distinguir “entrada incompleta” de “entrada inválida”?
Como encontro os símbolos __builtin_new ou __pure_virtual não-definidos no g++?
Posso criar minhas próprias funções em C?¶
Sim, você pode construir módulos embutidos contendo funções, variáveis, exceções e até mesmo novos tipos em C. Isso é explicado no documento Estendendo e Incorporando o Interpretador Python.
A maioria dos livros intermediários ou avançados em Python também abordará esse tópico.
Posso criar minhas próprias funções em C++?¶
Sim, usando recursos de compatibilidade encontrados em C++. Coloque extern "C" { ... }
em torno dos arquivos de inclusão do Python e coloque extern "C"
antes de cada função que será chamada pelo interpretador do Python. Objetos globais ou estáticos em C++ com construtores provavelmente não são uma boa ideia.
Escrever C é difícil; há alguma alternativa?¶
Há um número de alternativas para escrever suas próprias extensões em C, dependendo daquilo que você está tentando fazer.
Cython and its relative Pyrex are compilers that accept a slightly modified form of Python and generate the corresponding C code. Cython and Pyrex make it possible to write an extension without having to learn Python’s C API.
If you need to interface to some C or C++ library for which no Python extension currently exists, you can try wrapping the library’s data types and functions with a tool such as SWIG. SIP, CXX Boost, or Weave are also alternatives for wrapping C++ libraries.
Como posso executar instruções arbitrárias de Python a partir de C?¶
A função mais alto-nível para isso é a PyRun_SimpleString()
, que recebe como único argumento uma string a ser executada no contexto do módulo __main__
e retorna 0
para sucesso e -1
quando uma exceção ocorrer (incluindo SyntaxError
). Se quiser mais controle, use PyRun_String()
; veja o código-fonte de PyRun_SimpleString()
em Python/pythonrun.c
.
Como posso executar e obter o resultado de uma expressão Python arbitrária a partir de C?¶
Chame a função PyRun_String()
da pergunta anterior passando Py_eval_input
como o símbolo de início; ela faz a análise sintática de uma expressão, a executa, e retorna o seu valor.
Como extraio valores em C a partir de um objeto Python?¶
That depends on the object’s type. If it’s a tuple, PyTuple_Size()
returns its length and PyTuple_GetItem()
returns the item at a specified
index. Lists have similar functions, PyListSize()
and
PyList_GetItem()
.
For bytes, PyBytes_Size()
returns its length and
PyBytes_AsStringAndSize()
provides a pointer to its value and its
length. Note that Python bytes objects may contain null bytes so C’s
strlen()
should not be used.
Para testar o tipo de um objeto, primeiramente se certifique de que ele não é NULL
, e então use PyBytes_Check()
, PyTuple_Check()
, PyList_Check()
, etc.
Também existe uma API alto-nível para objetos Python fornecida pela chamada interface “abstrata” – leia Include/abstract.h
para mais detalhes. Ela permite interagir com qualquer tipo de sequência Python usando chamadas como PySequence_Length()
, PySequence_GetItem()
, etc, além de vários outros protocolos úteis tais como números (PyNumber_Index()
e outros) e mapeamentos nas APIs PyMapping.
Como posso utilizar Py_BuildValue() para criar uma tupla de comprimento arbitrário?¶
Não é possível. Use a função PyTuple_Pack()
para isso.
Como eu chamo um método de um objeto a partir do C?¶
A função PyObject_CallMethod()
pode ser usada para chamar um método arbitrário de um objeto. Os parâmetros são o objeto, o nome do método a ser chamado, uma string de formato como a usada em Py_BuildValue()
, e os valores dos argumentos:
PyObject *
PyObject_CallMethod(PyObject *object, const char *method_name,
const char *arg_format, ...);
Isso funciona para qualquer objeto que tenha métodos – sejam eles embutidos ou definidos por usuário. Você fica então responsável por chamar Py_DECREF()
no valor de retorno.
Para chamar, por exemplo, o método “seek” de um objeto arquivo com argumentos 10, 0 (presumindo que “f” é o ponteiro para o objeto arquivo):
res = PyObject_CallMethod(f, "seek", "(ii)", 10, 0);
if (res == NULL) {
... an exception occurred ...
}
else {
Py_DECREF(res);
}
Note que a função PyObject_CallObject()
sempre recebe os argumentos da chamada como uma tupla, de forma que para chamar uma função sem argumentos deve-se passar “()” como formato, e para chamar uma função com 1 argumento, coloque-o entre parênteses, por exemplo “(i)”.
Como posso capturar a saída da função PyErr_Print() (ou qualquer outra coisa que escreva para stdout/stderr)?¶
Com código Python, defina um objeto que suporte o método write()
. Atribua esse objeto a sys.stdout
e sys.stderr
. Chame print_error, or simplesmente deixe o mecanismo padrão de traceback acontecer. Assim, a saída irá para onde quer que o seu método write()
a envie.
O jeito mais fácil de fazer isso é usar a classe io.StringIO
:
>>> import io, sys
>>> sys.stdout = io.StringIO()
>>> print('foo')
>>> print('hello world!')
>>> sys.stderr.write(sys.stdout.getvalue())
foo
hello world!
Um objeto personalizado para fazer a mesma coisa seria esse:
>>> import io, sys
>>> class StdoutCatcher(io.TextIOBase):
... def __init__(self):
... self.data = []
... def write(self, stuff):
... self.data.append(stuff)
...
>>> import sys
>>> sys.stdout = StdoutCatcher()
>>> print('foo')
>>> print('hello world!')
>>> sys.stderr.write(''.join(sys.stdout.data))
foo
hello world!
Como faço para acessar a partir do C um módulo escrito em Python?¶
Você pode obter um pointeiro para o objeto de módulo da seguinte maneira:
module = PyImport_ImportModule("<modulename>");
Se o módulo ainda não foi importado (isto é, ainda não aparece no sys.modules
), essa função vai inicializar o módulo; caso contrário, ela vai simplesmente retornar o valor de sys.modules["<modulename>"]
. Note que ela não adiciona o módulo a nenhum espaço de nomes – ela simplesmente garante que ele foi inicializado e colocado no sys.modules
.
Você pode então acessar os atributos do módulo (isto é, qualquer nome definido no módulo) assim:
attr = PyObject_GetAttrString(module, "<attrname>");
Chamar PyObject_SetAttrString()
para definir variáveis no módulo também funciona.
Como posso interagir com objetos C++ a partir do Python?¶
Dependendo das suas necessidades, há diversas abordagens. Para fazer isso manualmente, comece lendo o documento “Estendendo e Incorporando”. Note que, para o sistema Python em tempo de execução, não há muita diferença entre C e C++ – de forma que a estratégia de construir um novo tipo Python ao redor de uma estrutura C (ou ponteiro para uma) também funciona para objetos C++.
Para bibliotecas C++, veja Escrever C é difícil; há alguma alternativa?.
Adicionei um módulo usando o arquivo de Setup e o make falha; por quê?¶
O Setup deve terminar com uma quebra de linha; se não houver uma quebra de linha no final, o processo de compilação falha. (Consertar isso requer umas gambiarras feias em shell script, e esse bug é tão pequeno que o esforço não parece valer a pena.)
Como eu depuro uma extensão?¶
Ao usar o GDB com extensões carregadas dinamicamente, você não consegue definir um ponto de interrupção antes da sua extensão ser carregada.
No seu arquivo .gdbinit
(ou então interativamente), adicione o comando:
br _PyImport_LoadDynamicModule
Então, ao executar o GDB:
$ gdb /local/bin/python
gdb) run myscript.py
gdb) continue # repeat until your extension is loaded
gdb) finish # so that your extension is loaded
gdb) br myfunction.c:50
gdb) continue
Quero compilar um módulo Python no meu sistema Linux, mas alguns arquivos estão faltando. Por quê?¶
Most packaged versions of Python don’t include the
/usr/lib/python2.x/config/
directory, which contains various files
required for compiling Python extensions.
For Red Hat, install the python-devel RPM to get the necessary files.
For Debian, run apt-get install python-dev
.
Como posso distinguir “entrada incompleta” de “entrada inválida”?¶
Às vezes você quer emular o comportamento do interpretador interativo do Python, que te dá um prompt de continuação quando a entrada está incompleta (por exemplo, você digitou o início de uma instrução “if”, ou então não fechou os parênteses ou aspas triplas), mas que te dá um mensagem de erro de sintaxe imediatamente se a entrada for inválida.
Em Python você pode usar o módulo codeop
, que aproxima suficientemente o comportamento do analisador sintático. Por exemplo, o IDLE o usa.
Em C, a forma mais fácil de fazer isso é chamar PyRun_InteractiveLoop()
(talvez em uma thread separada) e deixar o interpretador do Python tratar a entrada para você. Você tambémm pode apontar o PyOS_ReadlineFunctionPointer()
para a sua função de entrada personalizada. Consulte Modules/readline.c
e Parser/myreadline.c
para mais dicas.
However sometimes you have to run the embedded Python interpreter in the same
thread as your rest application and you can’t allow the
PyRun_InteractiveLoop()
to stop while waiting for user input. The one
solution then is to call PyParser_ParseString()
and test for e.error
equal to E_EOF
, which means the input is incomplete. Here’s a sample code
fragment, untested, inspired by code from Alex Farber:
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <node.h>
#include <errcode.h>
#include <grammar.h>
#include <parsetok.h>
#include <compile.h>
int testcomplete(char *code)
/* code should end in \n */
/* return -1 for error, 0 for incomplete, 1 for complete */
{
node *n;
perrdetail e;
n = PyParser_ParseString(code, &_PyParser_Grammar,
Py_file_input, &e);
if (n == NULL) {
if (e.error == E_EOF)
return 0;
return -1;
}
PyNode_Free(n);
return 1;
}
Another solution is trying to compile the received string with
Py_CompileString()
. If it compiles without errors, try to execute the
returned code object by calling PyEval_EvalCode()
. Otherwise save the
input for later. If the compilation fails, find out if it’s an error or just
more input is required - by extracting the message string from the exception
tuple and comparing it to the string “unexpected EOF while parsing”. Here is a
complete example using the GNU readline library (you may want to ignore
SIGINT while calling readline()):
#include <stdio.h>
#include <readline.h>
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <object.h>
#include <compile.h>
#include <eval.h>
int main (int argc, char* argv[])
{
int i, j, done = 0; /* lengths of line, code */
char ps1[] = ">>> ";
char ps2[] = "... ";
char *prompt = ps1;
char *msg, *line, *code = NULL;
PyObject *src, *glb, *loc;
PyObject *exc, *val, *trb, *obj, *dum;
Py_Initialize ();
loc = PyDict_New ();
glb = PyDict_New ();
PyDict_SetItemString (glb, "__builtins__", PyEval_GetBuiltins ());
while (!done)
{
line = readline (prompt);
if (NULL == line) /* Ctrl-D pressed */
{
done = 1;
}
else
{
i = strlen (line);
if (i > 0)
add_history (line); /* save non-empty lines */
if (NULL == code) /* nothing in code yet */
j = 0;
else
j = strlen (code);
code = realloc (code, i + j + 2);
if (NULL == code) /* out of memory */
exit (1);
if (0 == j) /* code was empty, so */
code[0] = '\0'; /* keep strncat happy */
strncat (code, line, i); /* append line to code */
code[i + j] = '\n'; /* append '\n' to code */
code[i + j + 1] = '\0';
src = Py_CompileString (code, "<stdin>", Py_single_input);
if (NULL != src) /* compiled just fine - */
{
if (ps1 == prompt || /* ">>> " or */
'\n' == code[i + j - 1]) /* "... " and double '\n' */
{ /* so execute it */
dum = PyEval_EvalCode (src, glb, loc);
Py_XDECREF (dum);
Py_XDECREF (src);
free (code);
code = NULL;
if (PyErr_Occurred ())
PyErr_Print ();
prompt = ps1;
}
} /* syntax error or E_EOF? */
else if (PyErr_ExceptionMatches (PyExc_SyntaxError))
{
PyErr_Fetch (&exc, &val, &trb); /* clears exception! */
if (PyArg_ParseTuple (val, "sO", &msg, &obj) &&
!strcmp (msg, "unexpected EOF while parsing")) /* E_EOF */
{
Py_XDECREF (exc);
Py_XDECREF (val);
Py_XDECREF (trb);
prompt = ps2;
}
else /* some other syntax error */
{
PyErr_Restore (exc, val, trb);
PyErr_Print ();
free (code);
code = NULL;
prompt = ps1;
}
}
else /* some non-syntax error */
{
PyErr_Print ();
free (code);
code = NULL;
prompt = ps1;
}
free (line);
}
}
Py_XDECREF(glb);
Py_XDECREF(loc);
Py_Finalize();
exit(0);
}
Como encontro os símbolos __builtin_new ou __pure_virtual não-definidos no g++?¶
Para carregar dinamicamente módulos de extensão feitos com g++, você precisa recompilar o Python, usando o g++ como ligador (mude a constante LINKCC no Makefile dos módulos de extensão do Python), e use o g++ também como ligador do seu módulo (por exemplo, g++ -shared -o mymodule.so mymodule.o
).
Posso criar uma classe de objetos com alguns métodos implementados em C e outros em Python (por exemplo, via herança)?¶
Sim, você pode herdar de classes embutidas como int
, list
, dict
etc.
The Boost Python Library (BPL, http://www.boost.org/libs/python/doc/index.html) provides a way of doing this from C++ (i.e. you can inherit from an extension class written in C++ using the BPL).