Extendiendo/Embebiendo FAQ

¿Puedo crear mis propias funciones en C?

Si, puedes crear módulos incorporados que contengan funciones, variables, excepciones y incluso nuevos tipos en C. Esto esta explicado en el documento Ampliación e incrustación del intérprete de Python.

La mayoría de los libros intermedios o avanzados de Python también tratan este tema.

¿Puedo crear mis propias funciones en C++?

Si, utilizando las características de compatibilidad encontradas en C++. Coloca extern "C" { ... } alrededor los archivos incluidos Python y pon extern "C" antes de cada función que será llamada por el interprete Python. Objetos globales o estáticos C++ con constructores no son una buena idea seguramente.

Escribir en C es difícil; ¿no hay otra alternativa?

Hay un número de alternativas a escribir tus propias extensiones C, dependiendo en que estés tratando de hacer.

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.

¿Cómo puedo ejecutar declaraciones arbitrarias de Python desde C?

La función de más alto nivel para hacer esto es PyRun_SimpleString() que toma un solo argumento de cadena de caracteres para ser ejecutado en el contexto del módulo __main__ y retorna 0 si tiene éxito y -1 cuando ocurre una excepción (incluyendo SyntaxError). Si quieres mas control, usa PyRun_String(); mira la fuente para PyRun_SimpleString() en Python/pythonrun.c.

¿Cómo puedo evaluar una expresión arbitraria de Python desde C?

Llama a la función PyRun_String() de la pregunta anterior con el símbolo de comienzo Py_eval_input; analiza una expresión, evalúa y retorna su valor.

¿Cómo extraigo valores C de un objeto Python?

Eso depende del tipo de objeto. Si es una tupla, PyTuple_Size() retorna su tamaño, y PyTuple_GetItem() retorna el ítem del índice especificado. Las listas tienen funciones similares, PyListSize() and PyList_GetItem().

Para bytes PyBytes_Size() retorna su tamaño, y PyBytes_AsStringAndSize() proporciona un puntero a su valor y tamaño. Nota que los objetos byte de Python pueden contener bytes null por lo que la función de C strlen() no debe ser utilizada.

Para testear el tipo de un objeto, primero debes estar seguro que no es NULL, y luego usa PyBytes_Check(), PyTuple_Check(), PyList_Check(), etc.

También hay una API de alto nivel para objetos Python que son provistos por la supuestamente llamada interfaz “abstracta” – lee Include/abstract.h para mas detalles. Permite realizar una interfaz con cualquier tipo de secuencia Python usando llamadas como PySequence_Length(), PySequence_GetItem(), etc. así como otros protocolos útiles como números (PyNumber_Index() et al.) y mapeos en las PyMapping APIs.

¿Cómo utilizo Py_BuildValue() para crear una tupla de un tamaño arbitrario?

No puedes hacerlo. Utiliza a cambio PyTuple_Pack().

¿Cómo puedo llamar un método de un objeto desde C?

Se puede utilizar la función PyObject_CallMethod() para llamar a un método arbitrario de un objeto. Los parámetros son el objeto, el nombre del método a llamar, una cadena de caracteres de formato como las usadas con Py_BuildValue(), y los valores de argumento

PyObject *
PyObject_CallMethod(PyObject *object, const char *method_name,
                    const char *arg_format, ...);

Esto funciona para cualquier objeto que tenga métodos – sean estos incorporados o definidos por el usuario. Eres responsable si eventualmente usas Py_DECREF() en el valor de retorno.

Para llamar, por ejemplo, un método «seek» de un objeto archivo con argumentos 10, 0 (considerando que puntero del objeto archivo es «f»):

res = PyObject_CallMethod(f, "seek", "(ii)", 10, 0);
if (res == NULL) {
        ... an exception occurred ...
}
else {
        Py_DECREF(res);
}

Note que debido a PyObject_CallObject() siempre necesita una tupla para la lista de argumento, para llamar una función sin argumentos, deberás pasar «()» para el formato, y para llamar a una función con un solo argumento, encierra el argumento entre paréntesis, por ejemplo «(i)».

¿Cómo obtengo la salida de PyErr_Print() (o cualquier cosa que se imprime a stdout/stderr)?

En código Python, define un objeto que soporta el método write(). Asigna este objeto a sys.stdout y sys.stderr. Llama a print_error, o solo permite que el mecanismo estándar de rastreo funcione. Luego, la salida se hará cuando invoques write().

La manera mas fácil de hacer esto es usar la clase io.StringIO:

>>> import io, sys
>>> sys.stdout = io.StringIO()
>>> print('foo')
>>> print('hello world!')
>>> sys.stderr.write(sys.stdout.getvalue())
foo
hello world!

Un objeto personalizado para hacer lo mismo se vería así:

>>> 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!

¿Cómo accedo al módulo escrito en Python desde C?

Puedes obtener un puntero al módulo objeto de esta manera:

module = PyImport_ImportModule("<modulename>");

Si el módulo todavía no se importó, (por ejemplo aún no esta presente en sys.modules), esto inicializa el módulo, de otra forma simplemente retorna el valor de sys.modules["<modulename>"]. Nota que no entra el módulo a ningún espacio de nombres (namespace) –solo asegura que fue inicializado y guardado en sys.modules.

Puedes acceder luego a los atributos del módulo (por ejemplo a cualquier nombre definido en el módulo) de esta forma:

attr = PyObject_GetAttrString(module, "<attrname>");

También funciona llamar a PyObject_SetAttrString() para asignar a variables en el módulo.

¿Cómo hago una interface a objetos C++ desde Python?

Dependiendo de lo que necesites, hay varias maneras de hacerlo. Para hacerlo manualmente empieza por leer the «Extending and Embedding» document.Fíjate que para el sistema de tiempo de ejecución Python, no hay una gran diferencia entre C y C++, por lo que la estrategia de construir un nuevo tipo Python alrededor de una estructura de C de tipo puntero, también funcionará para objetos C++.

Para bibliotecas C++, mira Escribir en C es difícil; ¿no hay otra alternativa?.

He agregado un módulo usando el archivo de configuración y el make falla. ¿Porque?

La configuración debe terminar en una nueva linea, si esta no está, entonces el proceso build falla. (reparar esto requiere algún truco de linea de comandos que puede ser no muy prolijo, y seguramente el error es tan pequeño que no valdrá el esfuerzo.)

¿Cómo puedo depurar una extensión?

Cuando se usa GDB con extensiones cargadas de forma dinámica, puedes configurar un punto de verificación (breakpoint) en tu extensión hasta que esta se cargue.

En tu archivo .gdbinit (o interactivamente), agrega el comando:

br _PyImport_LoadDynamicModule

Luego, cuando corras 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

Quiero compilar un módulo Python en mi sistema Linux, pero me faltan algunos archivos . ¿Por qué?

La mayoría de las versiones empaquetadas de Python no incluyen el directorio /usr/lib/python2.x/config/, que contiene varios archivos que son necesarios para compilar las extensiones Python.

Para Red Hat, instala el python-devel RPM para obtener los archivos necesarios.

Para Debian, corre apt-get install python-dev.

¿Cómo digo «entrada incompleta» desde «entrada inválida»?

A veces quieres emular el comportamiento del interprete interactivo de Python, que te da una continuación del prompt cuando la entrada esta incompleta (por ejemplo si comenzaste tu instrucción «if» o no cerraste un paréntesis o triples comillas), pero te da un mensaje de error de sintaxis inmediatamente cuando la entrada es invalida.

En Python puedes usar el módulo codeop, que aproxima el comportamiento del analizador gramatical (parser) . IDLE usa esto, por ejemplo.

La manera mas fácil de hacerlo en C es llamar a PyRun_InteractiveLoop() (quizá en un hilo separado) y dejar que el interprete Python gestione la entrada por ti. Puedes también configurar PyOS_ReadlineFunctionPointer() para apuntar a tu función de entrada personalizada.

¿Cómo encuentro símbolos g++ __builtin_new o __pure_virtual?

Para cargar dinámicamente módulos de extensión g++, debes recompilar Python, hacer un nuevo link usando g++ (cambia LINKCC en el Python Modules Makefile) y enlaza link tu extensión usando g++ (por ejemplo g++ -shared -o mymodule.so mymodule.o`).

¿Puedo crear una clase objeto con algunos métodos implementado en C y otros en Python (por ejemplo a través de la herencia)?

Si, puedes heredar de clases integradas como int, list, dict, etc.

The Boost Python Library (BPL, https://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).