FAQ sobre Extensão/Incorporação

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).