Sunteți pe pagina 1din 43

Extensión y empotrado del intérprete de

Python
Versión 2.0

Guido van Rossum


Fred L. Drake, Jr., editor

16 de octubre de 2000

BeOpen PythonLabs
Correo electrónico: python-docs@python.org
BEOPEN.COM TERMS AND CONDITIONS FOR PYTHON 2.0
BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1

1. This LICENSE AGREEMENT is between BeOpen.com (“BeOpen”), having an office at 160 Saratoga Avenue,
Santa Clara, CA 95051, and the Individual or Organization (“Licensee”) accessing and otherwise using this
software in source or binary form and its associated documentation (“the Software”).
2. Subject to the terms and conditions of this BeOpen Python License Agreement, BeOpen hereby grants Licensee
a non-exclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly,
prepare derivative works, distribute, and otherwise use the Software alone or in any derivative version, provided,
however, that the BeOpen Python License is retained in the Software, alone or in any derivative version prepared
by Licensee.
3. BeOpen is making the Software available to Licensee on an “AS IS” basis. BEOPEN MAKES NO REPRESEN-
TATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION,
BEOPEN MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTA-
BILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE
WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE SOFTWARE FOR
ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF USING,
MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY DERIVATIVE THEREOF, EVEN IF ADVI-
SED OF THE POSSIBILITY THEREOF.
5. This License Agreement will automatically terminate upon a material breach of its terms and conditions.
6. This License Agreement shall be governed by and interpreted in all respects by the law of the State of Cali-
fornia, excluding conflict of law provisions. Nothing in this License Agreement shall be deemed to create any
relationship of agency, partnership, or joint venture between BeOpen and Licensee. This License Agreement
does not grant permission to use BeOpen trademarks or trade names in a trademark sense to endorse or promote
products or services of Licensee, or any third party. As an exception, the “BeOpen Python” logos available at
http://www.pythonlabs.com/logos.html may be used according to the permissions granted on that web page.
7. By copying, installing or otherwise using the software, Licensee agrees to be bound by the terms and conditions
of this License Agreement.

CNRI OPEN SOURCE LICENSE AGREEMENT


Python 1.6 is made available subject to the terms and conditions in CNRI’s License Agreement. This Agreement
together with Python 1.6 may be located on the Internet using the following unique, persistent identifier (known as a
handle): 1895.22/1012. This Agreement may also be obtained from a proxy server on the Internet using the following
URL: http://hdl.handle.net/1895.22/1012.
CWI PERMISSIONS STATEMENT AND DISCLAIMER
Copyright
c 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, The Netherlands. All rights reserved.
Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is
hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and
this permission notice appear in supporting documentation, and that the name of Stichting Mathematisch Centrum or
CWI not be used in advertising or publicity pertaining to distribution of the software without specific, written prior
permission.
STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFT-
WARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT
SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CON-
SEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Agradecimientos
Las siguientes personas han contribuido secciones a este documento: Jim Fulton, Konrad Hinsen, Chris Phoenix y Neil
Schemenauer.
Resumen

Python es un lenguaje de programación interpretado y orientado a objetos. Este documento describe cómo escribir
módulos en C o C++ para extender el intérprete de Python con nuevos módulos. Estos módulos pueden definir nuevas
funciones y también nuevos tipos de objetos con sus métodos. También se describe cómo empotrar el intérprete de
python en otra aplicación, para usarlo como lenguaje de extensión (como los lenguajes de macro). Al final, explica
cómo compilar y enlazar los módulos de extensión para que se carguen dinámicamente (en tiempo de ejecución) en el
intérprete, siempre que el sistema operativo tenga esta posibilidad.
Este documento supone un conocimiento básico de Python. Hay una introducción informal al lenguaje en la Guía de
aprendizaje de Python. El Manual de referencia de Python proporciona una definición más formal del lenguaje. La
Referencia de la biblioteca de Python documenta los tipos de objetos existentes, funciones y módulos (tanto internos
como escritos en Python) que dan al lenguaje su amplio rango de aplicación.
Si se desea una descripción detallada del API de Python/C, se debe consultar el Manual de referencia del API Python/C.
ÍNDICE GENERAL

1 Extensión de Python con C o C++ 1


1.1 Un ejemplo sencillo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Intermezzo: Errores y excepciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.3 Volviendo al ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.4 La tabla de métodos y función de inicialización del módulo . . . . . . . . . . . . . . . . . . . . . . 5
1.5 Compilación y enlazado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.6 Llamar a funciones de Python desde C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.7 Cadenas de formato para PyArg_ParseTuple() . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.8 Análisis de claves con PyArg_ParseTupleAndKeywords() . . . . . . . . . . . . . . . . . . 13
1.9 La función Py_BuildValue() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.10 Saldo de referencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.11 Escritura de extensiones en C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
1.12 Proporcionar una API de C para un módulo de extensión . . . . . . . . . . . . . . . . . . . . . . . 20

2 Construcción de extensiones en C y C++ en U NIX 25


2.1 Construcción de intérpretes a medida . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
2.2 Opciones de definición del módulo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
2.3 Ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.4 Distribución de los módulos de extensión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

3 Construcción de extensiones en C y C++ en Windows 29


3.1 La receta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
3.2 Diferencias entre U NIX y Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.3 Uso de DLLs en la práctica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

4 Empotrado de Python en otra aplicación 33


4.1 Empotrado de Python en C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
4.2 Requisitos de enlazado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

A Informes de error 35

i
ii
CAPÍTULO

UNO

Extensión de Python con C o C++

Es bastante fácil añadir nuevos módulos internos a Python si se sabe programar en C. Tales módulos de extensión
pueden hacer cosas que no se pueden hacer directamente en Python: implementan nuevos tipos de objetos internos y
son capaces de llamara a funciones de C y hacer llamadas al sistema.
Para dar soporte a las extensiones, el API (Interfaz de aplicación para programadores) de Python define un conjunto
de funciones, macros y variables que proporcionan acceso a la mayoría de los aspectos del sistema de ejecución de
Python. El API de Python se incorpora en un fichero fuente C incluyendo la cabecera "Python.h".
La compilación de un módulo de extensión depende de su futuro uso y de la configuración del sistema, sobre lo que
se habla en capítulos posteriores.

1.1 Un ejemplo sencillo


Vamos a crear un módulo de extensión denominado ‘spam’1 (la comida favorita de los fans de Monty Python. . . ) y
queremos crear una interfaz de Python con la función de biblioteca C system()2 . Esta función toma como argumento
una cadena de caracteres terminada en nulo y devuelve un entero. Queremos que la función se pueda llamar desde
Python de este modo:

>>> import spam


>>> status = spam.system("ls -l") #Orden POSIX que presenta el contenido de un directorio

Se empieza por crear un fichero ‘spammodule.c’. Por tradición, si un módulo se llama ‘spam’, el fichero C que
contiene su implementación se llama ‘spammodule.c’. Si el nombre del módulo es largo, como ‘spammify’, el
módulo se puede llamar sólo ‘spammify.c’.
La primera línea del fichero será:

#include <Python.h>

Lo que pone disponible el API de Python (se puede añadir comentarios que describan el propósito del módulo y una
nota de derechos de copia).
Todos los símbolos visibles por el usuario definidos por "Python.h" tienen un prefijo ‘Py’ o ‘PY’, excepto aquéllos
definidos en ficheros de cabeceras estándar. Por comodidad y, ya que se utilizan extensivamente en el intérprete de
Python, "Python.h" incluye unos cuantos ficheros de cabecera: <stdio.h>, <string.h>, <errno.h> y
1 N. del T.: “Spam” se puede traducir como magro. Aparece tanto en ejemplos, etc. que me ha parecido mejor dejarlo tal cual, lo que además

evita el riesgo de cargarme algún ejemplo.


2 Ya hay una interfaz de esta función en el módulo estándar os. Se ha elegido como ejemplo simple y directo.

1
<stdlib.h>. Si en el sistema no existe este último fichero, se declaran las funciones malloc(), free() y
realloc() directamente.
Lo siguiente que se añade al módulo de extensión es la función C que se invocará al evaluar la expresión de Python
‘spam.system(string)’ (pronto se verá como se acaba invocando):

static PyObject *
spam_system(self, args)
PyObject *self;
PyObject *args;
{
char *orden;
int sts;

if (!PyArg_ParseTuple(args, "s", &orden))


return NULL;
sts = system(orden);
return Py_BuildValue("i", sts);
}

Hay una traducción directa desde la lista de argumentos de Python (es decir, la expresión simple "ls -l") a los
argumentos que se pasan a la función de C. La función de C siempre tiene dos argumentos, por convención self y args.
El argumento self sólo se utiliza cuando la función C implementa un método interno, no una función. En el ejemplo,
self siempre será un puntero NULL, porque se está definiendo una función y no un método (se hace así para que el
intérprete no se tenga que entender con dos tipos diferentes de funciones C).
El argumento args será un puntero a un objeto tupla de Python que contiene los argumentos. Cada elemento de la tupla
corresponde a un argumento en la lista de argumentos de la llamada. Los argumentos son objetos de Python, por lo que
para hacer cualquier cosa útil con ellos habrá que convertirlos a valores de C. La función PyArg_ParseTuple()
del API de Python comprueba los tipos de argumento y los convierte a valores de C. Usa una cadena de plantilla para
determinar los tipos de los argumentos y los tipos C de las variables en las que almacenar los valores convertidos. Ya
se hablará de esto más tarde.
PyArg_ParseTuple() devuelve verdadero (no cero) si todos los argumentos son del tipo correcto y sus compo-
nentes han sido almacenados en las variables cuyas direcciones se pasaron. Devuelve falso (cero) si se pasa una lista
de argumentos incorrecta. En este caso, también lanza una excepción adecuada, por lo que la función llamante debe
devolver NULL de inmediato (como se vio en el ejemplo).

1.2 Intermezzo: Errores y excepciones


Hay una importante convención en todo el intérprete de Python: cuando una función fracasa, debe establecer una
condición de excepción y devolver un valor de error (normalmente, un puntero NULL). Las excepciones se alma-
cenan en una variable global estática dentro del intérprete: si esta variable es NULL es que no ha ocurrido ninguna
excepción. Una segunda variable global almacena el “valor asociado” a la excepción (el segundo argumento de rai-
se). Una tercera variable almacena el trazado de la pila en el caso en que el error se generase en el código Python.
Estas tres variables son las equivalentes en C de las variables de Python sys.exc_type, sys.exc_value y
sys.exc_traceback (consultar la sección del módulo sys en la Referencia de la biblioteca de Python). Es im-
portante conocerlas para comprender como se transmiten los errores.
El API de Python define varias funciones para establecer los diversos tipos de excepciones.
La más común es PyErr_SetString(). Sus argumentos son un objeto excepción y una cadena C. El objeto
excepción suele ser un objeto predefinido, como PyExc_ZeroDivisionError. La cadena C indica la causa del
error. Se convierte a objeto cadena Python y se almacena como “valor asociado” a la excepción.

2 Capítulo 1. Extensión de Python con C o C++


Otra función útil es PyErr_SetFromErrno(), que sólo toma un argumento excepción y construye el valor asocia-
do mediante inspección de la variable global errno. La función más general es PyErr_SetObject(), que toma
dos argumentos objeto, la excepción y su valor asociado. No hay que aplicar Py_INCREF() a los objetos pasados a
ninguna de estas funciones.
Se puede hacer una comprobación no destructiva de si se ha establecido una excepción con PyErr_Occurred().
Ésta devuelve el objeto excepción en curso o NULL si no ha habido excepciones. No se suele tener que llamar a
PyErr_Occurred() para ver si hubo un error en una llamada a función, ya que se debería saber por el valor
devuelto.
Cuando una función f que llama a otra función g detecta que ésta falla, la misma f debería devolver un valor de error (es
decir, NULL o -1). No debería llamar a ninguna de las funciones PyErr_*(), g ya ha llamado a alguna. Se supone
que el que llamó a f también devolverá una indicación de error a su llamante, de nuevo sin llamar a PyErr_*() y así
sucesivamente. La causa de error más detallada ya fue explicada por la función que la detectó primero. Al llegar el error
al bucle principal de Python, interrumpe la ejecución del código Python e intenta encontrar un gestor de excepciones
especificado por el programador en Python.
Hay situaciones en las que un módulo realmente puede dar un mensaje de error más detallado llamando a otra función
PyErr_*() y, en dichos casos, es adecuado hacerlo. De forma general, no obstante, no es necesario y puede causar
pérdida de información de la causa del error: la mayoría de las operaciones pueden fracasar por multitud de razones.
Para hacer caso omiso de una excepción establecida por una llamada a función fracasada, la condición de excepción
debe restablecerse explícitamente llamando a PyErr_Clear(). El único momento en que el código C debería llamar
a PyErr_Clear() es si no desea transmitir el error sino gestionarlo él completamente (por ejemplo, intentando otra
cosa o fingir que no ha ocurrido nada).
Toda llamada sin éxito a malloc() debe convertirse en una excepción. El que llamó a malloc() (o a realloc())
debe llamar a PyErr_NoMemory() y devolver un indicador de fracaso. Todas las funciones de creación de objetos
(por ejemplo, PyInt_FromLong()) ya lo hacen, por lo que esta nota sólo afecta a aquéllos que llamen a malloc()
directamente.
Hay que destacar también que , con la importante excepción de PyArg_ParseTuple() y similares, las funciones
que devuelven un entero de estado suelen devolver un valor positivo o cero para éxito y -1 para fracaso, como las
llamadas al sistema en U NIX.
Finalmente, ¡hay que tener cuidado de limpiar la basura (llamando a Py_XDECREF() o a Py_DECREF() para los
objetos que ya han sido creados) al devolver un indicador de error!
La excepción que se lanza queda a elección del programador. Hay objetos C predeclarados correspondientes a todas las
excepciones internas de Python, por ejemplo PyExc_ZeroDivisionError, directamente utilizables. Por supues-
to, se deben usar las excepciones sabiamente, no se vaya a usar PyExc_TypeError para indicar que un fichero no
se ha podido abrir (lo que se debería indicar probablemente con PyExc_IOError). Si hay algo incorrecto en la lista
de argumentos, la función PyArg_ParseTuple() suele lanzar PyExc_TypeError. Si hay un argumento cuyo
valor está restringido a un rango o debe satisfacer ciertas condiciones, lo apropiado es lanzar PyExc_ValueError.
También se puede definir una excepción exclusiva del módulo. Para ello, se suele declarar una variable objeto estática
al inicio del fichero. Por ejemplo:
static PyObject *SpamError;

y se inicializa en la función de inicialización del módulo (initspam()) con un objeto excepción, por ejemplo, y
dejando aparte la comprobación de errores de momento:

1.2. Intermezzo: Errores y excepciones 3


void
initspam()
{
PyObject *m, *d;

m = Py_InitModule("spam", SpamMethods);
d = PyModule_GetDict(m);
SpamError = PyErr_NewException("spam.error", NULL, NULL);
PyDict_SetItemString(d, "error", SpamError);
}

Es importante hacer ver que el nombre en Python del objeto excepción es spam.error. La función
PyErr_NewException() puede crear tanto una cadena como una clase, dependiendo de si se pasó el indica-
dor -X al intérprete. Si se puso -X, SpamError será un objeto cadena; en caso contrario será un objeto de clase cuya
clase base será Exception, descrita en la Referecia de la biblioteca de Python en “Excepciones internas”.

1.3 Volviendo al ejemplo


Volviendo a nuestra función de ejemplo, este ejemplo debería ser comprensible ahora:
if (!PyArg_ParseTuple(args, "s", &orden))
return NULL;

Devuelve NULL (el indicador de error de las funciones que devuelven punteros a objetos) si se detecta un error en la
lista de argumentos, delegando en la excepción establecida por PyArg_ParseTuple(). En caso contrario, el valor
de cadena del argumento se copia a la variable local orden. Se trata de una asignación de punteros, se supone que
no se debe modificar la cadena a la que apunta (por lo que en C estándar, se debería declarar la variable orden como
‘const char *orden’).
La sentencia siguiente es una llamada a la función de U NIX system(), pasándole la cadena que acabamos de obtener
de PyArg_ParseTuple():

sts = system(orden);

Nuestra función spam.system() debe devolver el valor de sts como objeto de Python. Esto se logra utilizando
la función Py_BuildValue(), que es de algún modo la inversa de PyArg_ParseTuple(): toma una cadena de
formato y un número arbitrario de valores C y devuelve un nuevo objeto Python. Se proporcionará más información
acerca de Py_BuildValue() más tarde.

return Py_BuildValue("i", sts);

En este caso, devolverá un objeto entero (¡sí, hasta los enteros son objetos en el montículo (heap) en Python!).
Si se trata de una función C que no devuelve un argumento útil (una función que devuelve void), la función corres-
pondiente en Python debe devolver None. Por convención, se utiliza:
Py_INCREF(Py_None);
return Py_None;

4 Capítulo 1. Extensión de Python con C o C++


Py_None es el nombre en C del objeto especial de Python None. Es un objeto Python genuino, no un puntero a
NULL, que indica una condición de error en la mayoría de los contextos, como se ha visto.

1.4 La tabla de métodos y función de inicialización del módulo


Prometí mostrar cómo se llama a spam_system() desde los programas en Python. Para empezar, necesitamos tener
su nombre y dirección en una “tabla de métodos”:

static PyMethodDef SpamMethods[] = {


...
{"system", spam_system, METH_VARARGS},
...
{NULL, NULL} /* centinela */
};

El tercer elemento es importante (‘METH_VARARGS’). Es un indicador que establece la convención de llama-


da a usar para la función C. Lo más común es que siempre sea ‘METH_VARARGS’ o ‘METH_VARARGS |
METH_KEYWORDS’. Un valor de 0 significa que se usa una variante obsoleta de PyArg_ParseTuple().
Cuando se use sólo ‘METH_VARARGS’, la función debe esperar que se le pasen los parámetros del nivel de Python
como una tupla adecuada para sera analizada por PyArg_ParseTuple(). Más adelante se profundiza en este tema.
Si se han de pasar argumentos por clave a la función, se activará el bit de METH_KEYWORDS en el tercer campo. En
tal caso, la función C debería aceptar un tercer parámetro ‘PyObject *’ que será un diccionario de claves. Se debe
usar PyArg_ParseTupleAndKeywords() para analizar los argumentos de una función de este estilo.
Se debe pasar la tabla de métodos al intérprete en la función de inicialización del módulo. La función de inicialización
debe llamarse initnombre(), donde nombre es el nombre del módulo, y ser el único elemento no static definido
en el fichero del módulo:

void
initspam()
{
(void) Py_InitModule("spam", SpamMethods);
}

En C++, este método se ha de declarar con extern "C".


Cuando el programa Python importa el módulo spam por primera vez, se llama a la función initspam() (ver
los comentarios posteriores acerca del empotrado de Python). Llama a Py_InitModule(), que crea un “objeto
módulo” (que se inserta en el diccionario sys.modules con clave "spam") e inserta las funciones internas del
módulo en el módulo recién creado, basándose en la tabla (una matriz de estructuras PyMethodDef) pasada como
segundo argumento. Py_InitModule() devuelve un puntero (que no se usa aquí) al objeto módulo. Si no se
inicializa correctamente el módulo se interrumpe con un error fatal, por lo que no es necesario que el que llama
verifique la existencia de errores.
Al empotrar Python, no se llama automáticamente a initspam() a menos que exista una entrada en la tabla _PyIm-
port_Inittab. El modo más sencillo de gestionar esto es inicializar estáticamente los módulos enlazados estática-
mente llamando directamente a initspam() tras llamar a Py_Initialize() o PyMac_Initialize():

1.4. La tabla de métodos y función de inicialización del módulo 5


int main(int argc, char **argv)
{
/* Pasar argv[0] al intérprete de Python */
Py_SetProgramName(argv[0]);

/* Inicializar el intérprete de Python. Requerido. */


Py_Initialize();

/* Agregar un módulo estático */


initspam();

Se puede encontrar un ejemplo en el fichero ‘Demo/embed/demo.c’ en la distribución de fuentes de Python.


Nota: Eliminar elementos de sys.modules o importar módulos compilados a múltiples intérpretes dentro de un
proceso (o tras fork() sin una exec()) puede causar problemas en ciertos módulos de extensión. Los autores de
módulos de extensión deberían obrar con prudencia al inicializar estructuras de datos internas. Además, la función re-
load() se puede usar con módulos de extensión y llamará a la función de inicialización del módulo (initspam()
en el ejemplo), pero no recargará el módulo si se obtuvo de un objeto de carga dinámica (‘.so’ en U NIX, ‘.dll’ en
Windows).
Se incluye un ejemplo más sustancial en la distribución de fuentes de Python, en ‘Modules/xxmodule.c’. Se puede usar
este fichero como plantilla o leerlo como ejemplo. El guion modulator.py incluido en la distribución de fuentes o en
la instalación de Windows proporciona una sencilla interfaz gráfica para declarar las funciones y objetos que debería
implementar un módulo y puede generar una plantilla para rellenar. El guion reside en el directorio ‘Tools/modulator/’.
En el fichero ‘README’ de dicho directorio hay más información.

1.5 Compilación y enlazado


Quedan dos cosas antes de poder usar la nueva extensión: compilarla y enlazarla al sistema Python. Si se usa carga
dinámica, los detalles dependen del estilo de carga dinámica que utilice el sistema. En los capítulos relativos a la
construcción de módulos de extensión en U NIX (capítulo 2) y Windows (capítulo 3) hay más información.
Si no es posible la carga dinámica o se desea hacer el módulo parte del intérprete de Python, hay que cambiar la
configuración y reconstruir el intérprete. Por fortuna, resulta sencillo: basta con colocar el fichero (‘spammodule.c’
por ejemplo) en el directorio ‘Modules/’ de una distribución de fuentes descomprimida, añadir una línea en el fichero
‘Modules/Setup.local’ describiendo el fichero.

spam spammodule.o

y reconstruir el intérprete ejecutando make en el directorio base. También se puede ejecutar make en el subdirectorio
de ‘Modules/’, pero entonces hay que reconstruir primero el ‘Makefile’ ejecutando ‘make Makefile’ (es necesario cada
vez que se cambia el fichero ‘Setup’).
Si el módulo utiliza más bibliotecas de enlace, se pueden enumerara en la línea correspondiente del fichero de confi-
guración, por ejemplo:

spam spammodule.o -lX11

6 Capítulo 1. Extensión de Python con C o C++


1.6 Llamar a funciones de Python desde C
Hasta ahora, nos hemos concentrado en hacer las funciones en C accesibles desde Python. También lo contrario, llamar
a las funciones de Python desde C, es útil. Esto es el caso concreto de las bibliotecas con el concepto de llamadas de
“respuesta”. Si una interfaz de C utiliza respuestas, el Python equivalente suele tener que proporcionar un mecanismo
de respuesta para el programador de Python. La implementación requerirá llamar a las funciones de repuesta desde las
funciones de respuesta en C. Existen otros usos.
Afortunadamente, es sencillo llamar al intérprete de Python recursivamente y existe una interfaz estándar para lla-
mar a una función de Python. No voy a insistir en cómo llamar al analizador de Python con una cadena concreta
como entrada; en caso de interés, consultar la implementación de la opción de línea de órdenes -c en el fichero
‘Python/pythonmain.c’ del código fuente de Python.
Es fácil llamar a una función de Python. Para empezar, el programa Python debe pasar de algún modo el objeto
función de Python. Se debe proporcionar una función (u otra interfaz) para lograrlo. En dicha función se debe guardar
un puntero al objeto función de Python (ojo, hay que hacer Py_INCREF()) en una variable global, u otro lugar
adecuado. Por ejemplo, la siguiente función podría ser parte de una definición de módulo:

static PyObject *my_callback = NULL;

static PyObject *
my_set_callback(dummy, args)
PyObject *dummy, *args;
{
PyObject *result = NULL;
PyObject *temp;

if (PyArg_ParseTuple(args, "O:set_callback", &temp)) {


if (!PyCallable_Check(temp)) {
PyErr_SetString(PyExc_TypeError, "parameter must be callable");
return NULL;
}
Py_XINCREF(temp); /* Add a reference to new callback */
Py_XDECREF(my_callback); /* Dispose of previous callback */
my_callback = temp; /* Remember new callback */
/* Boilerplate to return "None" */
Py_INCREF(Py_None);
result = Py_None;
}
return result;
}

Esta función debe estar registrada en el intérprete mediante el indicador METH_VARARGS, lo que se describe en la
sección 1.4, “La tabla de métodos y la función de inicialización del módulo ”. La función PyArg_ParseTuple()
y sus argumentos están documentados en la sección 1.7 “Cadenas de formato de PyArg_ParseTuple()”.
Las macros Py_XINCREF() y Py_XDECREF() incrementan/decrementan el saldo de referencias de un objeto y son
seguras en la presencia de punteros NULL (obsérvese que temp no será NULL en este contexto). Hay más información
en la sección 1.10, “Saldos de referencias.”
Más tarde cuando toque evaluar la función, se debe llamar a la función C PyEval_CallObject(). Esta función
toma dos argumentos, los dos punteros a objetos de Python arbitrarios: la función Python y la lista de argumentos.
La lista de argumentos siempre debe ser un objeto tupla cuya longitud sea el número de argumentos. Para llamar a la
función de Python si argumentos, se debe pasar una tupla vacía. Para pasar un solo argumento, se debe pasar una tupla
de un solo elemento. Py_BuildValue() devuelve una tupla cuando su cadena de formato consta de cero o más
códigos de formato entre paréntesis. Por ejemplo:

1.6. Llamar a funciones de Python desde C 7


int arg;
PyObject *arglist;
PyObject *result;
...
arg = 123;
...
/* Hora de la llamada de respuesta */
arglist = Py_BuildValue("(i)", arg);
result = PyEval_CallObject(my_callback, arglist);
Py_DECREF(arglist);

PyEval_CallObject() devuelve un puntero a objeto Python: el valor devuelto por la función de Python. PyE-
val_CallObject() es neutro respecto al saldo de referencias de sus argumentos. En el ejemplo, se crea una nueva
tupla para servir de lista de argumentos, que sufre una llamada a Py_DECREF() inmediatamente tras la llamada.
El valor devuelto por PyEval_CallObject() es “nuevo”: bien es un objeto completamente nuevo o es un objeto
existente cuyo saldo de referencias se ha incrementado. Por ello, salvo que se vaya a guardar el objeto en una variable
global, se debe someter el resultado a Py_DECREF(), incluso (¡más aún!) si no se está interesado en el valor.
Antes de hacerlo, sin embargo, es importante comprobar que el valor no es NULL. De serlo, la función termino su eje-
cución con una excepción. Si el código C que llamó a PyEval_CallObject() fue llamado desde Python, debería
devolver una indicación de error a su llamante Python, para que el intérprete sea capaz de presentar la información
de la excepción o el código Python sea capaz de gestionar la excepción. Si esto no es posible o conveniente, debería
limpiarse la excepción llamando a PyErr_Clear(). Por ejemplo:
if (result == NULL)
return NULL; /* Remitir el error */
...use result...
Py_DECREF(result);

Dependiendo de la interfaz con la función de respuesta de Python deseada, puede que se tenga que proporcionar una
lista de argumentos para PyEval_CallObject(). En ciertos casos, también el programa en Python proporciona
la lista de argumentos, a través de la misma interfaz especificada por la función de respuesta. Así, puede guardarse y
utilizarse del mismo modo que el objeto función. En otros casos, puede que haya que construir una nueva tupla para
pasar la lista de argumentos.El modo más sencillo de hacerlo es llamar a Py_BuildValue(). Por ejemplo, si se
desea pasar un código de suceso entero, puede usarse el siguiente código:
PyObject *arglist;
...
arglist = Py_BuildValue("(l)", eventcode);
result = PyEval_CallObject(my_callback, arglist);
Py_DECREF(arglist);
if (result == NULL)
return NULL; /* Devolver el error */
/* Aquí se puede usar el resultado */
Py_DECREF(result);

Obsérvese la colocación de ‘Py_DECREF(arglist)’ inmediatamente tras la llamada y antes de la comprobación


de errores. También debe destacarse que el código no está completo estrictamente: puede agotarse la memoria en
Py_BuildValue(), lo que debería comprobase.

8 Capítulo 1. Extensión de Python con C o C++


1.7 Cadenas de formato para PyArg_ParseTuple()
La función PyArg_ParseTuple() se declara así:

int PyArg_ParseTuple(PyObject *arg, char *format, ...);

El argumento arg debe ser un objeto tupla que contenga una lista de argumentos pasada de Python a una función C. El
argumento format debe ser una cadena de formato, cuya sintaxis se explica más adelante. El resto de los argumentos
deben ser direcciones de variables cuyo tipo queda determinado por la cadena de formato. Para que tenga éxito la
conversión, el objeto arg debe concordar con el formato y utilizarlo por completo.
Aunque PyArg_ParseTuple() comprueba que los argumentos de Python tienen los tipos indicados, no puede
comprobar la validez de las direcciones de las variables de C que se pasen a la llamada: si se cometen errores en este
punto, el código efectuará operaciones no válidas o, al menos, sobreescribirá porciones aleatorias de la memoria. ¡Hay
que tener cuidado!
Una cadena de formato consta de cero o más “unidades de formato”. Una unidad de formato describe un objeto
Python. Suele ser un solo carácter o una secuencia de unidades de formato entre paréntesis. Salvo algunas excepciones,
una unidad de formato que no es una secuencia entre paréntesis corresponde a un solo argumento de dirección para
PyArg_ParseTuple(). En la siguiente descripción, la forma entrecomillada es la unidad de formato, la entrada
entre paréntesis es el tipo de objeto Python que concuerda con la unidad de formato y la entrada entre corchetes es el
tipo de la/s variable/s C cuyas direcciones se han de pasar (se debe usar el operador ‘&’ para pasar la dirección de una
variable).
Todas las referencias a objetos de Python proporcionadas al llamante son referencias prestadas; no se debe decrementar
su saldo de referencias.

‘s’ (cadena u objeto Unicode) [char *] Convierte una cadena u objeto Unicode de Python a puntero a cadena de
caracteres de C. No hay que proporcionar almacenamiento a la propia cadena, ya que se guarda un puntero a una
cadena existente en la variable puntero a carácter cuya dirección se pasa. La cadena C está terminada por nulo.
La cadena de Python no debe contener bytes nulos, en caso contrario, se lanza una excepción TypeError. Los
objetos Unicode se convierten a cadenas de C utilizando la codificación predeterminada. Si falla la conversión,
se lanza una excepción UnicodeError.
‘s#’ (objeto cadena, Unicode o cualquier objeto legible con read) [char *, int] Esta variación sobre ‘s’ almace-
na en dos variables de C, la primera un puntero a una cadena de caracteres y la segunda su longitud. En este
caso, la cadena Python puede contener bytes cero en medio. Los objetos Unicode devuelven un puntero a la
versión codificada por defecto de la cadena si tal conversión es posible. Los demás objetos legibles devuelven
una referencia a la representación de los datos internos en crudo.
‘z’ (cadena o None) [char *] Como ‘s’, pero el objeto Python puede ser también None, en cuyo caso el puntero C
se pone a NULL.
‘z#’ (cadena o None o cualquier objeto legible con read) [char *, int] Esto es a ‘s#’ lo que ‘z’ es a ‘s’.
‘u’ (objeto Unicode) [Py_UNICODE *] Convierte un objeto Unicode de Python a un puntero C que apunta a una
cadena terminada en nulo de datos Unicode (UTF-16). Como con ‘s’, no es necesario proporcionar espacio
para la cadena de datos Unicode, se guarda un puntero a los datos Unicode existentes en la variable puntero
Py_UNICODE cuya dirección se pasa.
‘u#’ (objeto Unicode) [Py_UNICODE *, int] Esta variación sobre ‘u’ almacena en dos variables de C, la primera
un puntero a una cadena de datos Unicode y la segunda su longitud.
‘es’ (objeto cadena, Unicode o cualquier objeto legible con read) [const char *encoding, char **buffer] Esta
variación sobre ‘s’ se usa para codificar objetos Unicode y objetos convertibles en Unicode en una cadena de
caracteres. Sólo funciona para datos codificados sin caracteres NULL en medio.

1.7. Cadenas de formato para PyArg_ParseTuple() 9


Lee una variable de C y almacena en dos. La variable leída es un puntero a una cadena de nombre de codifica-
ción (encoding). La primera variable destino es un puntero a un puntero a una cadena (**buffer, utilizada para
almacenar los datos codificados y la otra un puntero a un entero (*buffer_length, la longitud de la cadena).
El nombre de la codificación debe ser uno de los códec registrados. De ser NULL, se utilizará la codificación
predeterminada.
PyArg_ParseTuple() reservará un bloque del tamaño adecuado mediante PyMem_NEW(), copiará los
datos codificados a este bloque y ajustará *buffer para que apunte al nuevo almacenamiento. El que llama es
responsable de llamar a PyMem_Free() para liberar el bloque reservado tras su uso.
‘es#’ (cadena, objeto Unicode o cualquier objeto legible con read) [const char *encoding, char **buffer, int *buffer_length]
Esta variación sobre ‘s#’ se utiliza para codificar objetos Unicode y compatibles a cadena de caracteres. Lee de
una variable de C y almacena en dos. La variable leída es un puntero a una cadena de nombre de codificación
(encoding), la primera escrita es un puntero a un puntero a una cadena de caracteres (**buffer, que se utiliza
para almacenar los datos codificados y la última un puntero a un entero *buffer_length, la longitud de la cadena.
El nombre de la codificación debe ser uno de los códec registrados. De ser NULL, se utilizará la codificación
predeterminada.
Hay dos modos de funcionamiento:
Si *buffer apunta a un puntero NULL, PyArg_ParseTuple() reservará un bloque del tamaño necesario
con PyMem_NEW(), copiará los datos codificados a este bloque y ajustará *buffer para que haga referencia al
almacenamiento recién reservado. El que llama a esta función es responsable de llamara a PyMem_Free()
para liberar el bloque reservado, tras su uso.
Si *buffer apunta a un puntero no NULL (un bloque ya reservado), PyArg_ParseTuple() utilizará es-
ta ubicación e interpretará que *buffer_length es el tamaño del bloque utilizable. Entonces, copiará los datos
codificados al bloque y les dará una terminación cero. Se señala el desbordamiento del bloque mediante una
excepción.
En los dos casos, se actualiza *buffer_length a la longitud de los datos codificados, excluyendo el byte cero final.
‘b’ (integer) [char] Convierte un entero de Python a un entero pequeño, almacenado en un char de C.
‘h’ (integer) [short int] Convierte un entero de Python a un short int de C.
‘i’ (integer) [int] Convierte un entero de Python a un int de C.
‘l’ (integer) [long int] Convierte un entero de Python a un long int de C.
‘c’ (string of length 1) [char] Convierte un carácter de Python, es decir, una cadena de caracteres de longitud uno, a
un char de C.
‘f’ (float) [float] Convierte un número de coma flotante de Python a un float de C.
‘d’ (float) [double] Convierte un número de coma flotante de Python a un double de C.

‘D’ (complex) [Py_complex] Convierte un número complejo de Python a una estructura Py_complex de C.
‘O’ (objeto) [PyObject *] Almacena un objeto de Python (sin conversión ninguna) a un puntero a objeto de C. El
programa de C recibe el objeto real traspasado. No se incrementa el saldo de referencias del puntero. El puntero
almacenado no es NULL.

‘O!’ (objeto) [typeobject, PyObject *] Almacena un objeto de Python en un puntero a objeto de C. Se parece a ‘O’,
pero toma dos argumentos: el primero es la dirección de un objeto tipo de Python, el segundo es la dirección de
la variable C (de tipo PyObject *) en la que se almacena el puntero a objeto. Si el objeto de Python no tiene
el tipo exigido, se lanza TypeError.

10 Capítulo 1. Extensión de Python con C o C++


‘O&’ (objeto) [conversión, anything] Convierte un objeto de Python a una variable C mediante una función converter
de conversión, que debe tomar dos argumentos: el primero es una función, el segundo es la dirección de una
variable C (de tipo arbitrario), convertida a void *. Se invoca la función converter de este modo:
status = conversión(objeto, dirección);
donde objeto es el objeto de Python a convertir y dirección es el argumento void * pasado a
PyArg_ConvertTuple(). El estado devuelto debería ser 1 para la conversión con éxito 0 si fracasó la
conversión. Si la conversión falla, la función conversión debería lanzar una excepción.
‘S’ (cadena) [PyStringObject *] Como ‘O’, pero necesita que el objeto Python sea un objeto cadena. Lanza Ty-
peError si no lo es. También se puede declarar la variable de C como PyObject *.
‘U’ (cadena Unicode) [PyUnicodeObject *] Como ‘O’, pero necesita que el objeto Python sea un objeto Unicode.
Lanza TypeError si no lo es. También se puede declarar la variable de C como PyObject *.
‘t#’ (bloque de caracteres de sólo lectura) [char *, int] Como ‘s#’, pero acepta la interfaz de sólo lectura median-
te read. La variable char * se hace apuntar al primer byte del bloque y se asigna al int la longitud del bloque.
Sólo se aceptan bloques de un sólo segmento, se lanza TypeError en otros casos.
‘w’ (bloque de caracteres de lectura/escritura) [char *] Como ‘s’, pero acepta la interfaz de lectura-escritura me-
diante read/write. El que llama a la función debe determinar la longitud del bloque por otros medios, o usar ‘w#’
en lugar de ésta. Sólo se aceptan bloques de un sólo segmento, se lanza TypeError en otros casos.
‘w#’ (bloque de caracteres de lectura/escritura) [char *, int] Como ‘s#’, pero acepta la interfaz de lectura-
escritura mediante read/write. La variable char * se hace apuntar al primer byte del bloque y se asigna al
int la longitud del bloque. Sólo se aceptan bloques de un sólo segmento, se lanza TypeError en otros casos.
‘(elementos)’ (tupla) [mismo no elementos] El objeto debe se una secuencia de Python cuya longitud es el núme-
ro de unidades de formato de elementos. Los argumentos C deben corresponder con las unidades de formato
individuales de elementos. Se puede anidar las unidades de formato.
Nota: En las versiones de Python anteriores a 1.5.2, este especificador de formato sólo aceptaba una tupla
que contenía los parámetros individuales, no una secuencia arbitraria. El código que lanzara anteriormente
TypeError puede proseguir ahora sin lanzar excepciones. No se espera que esto sea un problema para el
código existente.

Es posible pasar enteros largos de Python cuando se requieran enteros. Sin embargo, no se hace una verificación de
rangos, truncándose los bits más significativos silenciosamente si el campo receptor es demasiado pequeño para recibir
el valor (en realidad, la semántica se hereda de las conversiones de C, por lo que puede ocurrir cualquier cosa).
Hay otros caracteres que tienen significado dentro de una cadena de formato. No pueden darse dentro de paréntesis
anidados. Son:

‘|’ Indica que el resto de los argumentos de la lista de argumentos de Python es opcional. Las variable de C corres-
pondientes a los argumentos opcionales deberían inicializarse a su valor por omisión. Cuando no se da valor a
un argumento opcional, PyArg_ParseTuple() no toca el contenido de las variables C correspondientes.

‘:’ La lista de unidades de formato termina aquí: la cadena que sigue a los dos puntos se utiliza como nombre de la
función en mensajes de error (el “valor asociado” de la excepción que lanza PyArg_ParseTuple()).
‘;’ La lista de unidades de formato termina aquí: la cadena que sigue a los dos puntos se usa como mensaje de error
en lugar del mensaje de error predeterminado. Obviamente, ‘:’ y ‘;’ son mutuamente excluyentes.

Llamadas de ejemplo:

1.7. Cadenas de formato para PyArg_ParseTuple() 11


int vale;
int i, j;
long k, l;
char *s;
int tamanho;

vale = PyArg_ParseTuple(args, ""); /* Sin argumentos */


/* Llamada desde Python: f() */

vale = PyArg_ParseTuple(args, "s", &s); /* Una cadena */


/* Posible llamada desde Python: f(’¡ahí va!’) */

vale = PyArg_ParseTuple(args, "lls", &k, &l, &s); /* Dos enteros y una cadena */
/* Posible llamada desde Python: f(1, 2, ’tres’) */

vale = PyArg_ParseTuple(args, "(ii)s#", &i, &j, &s, &tamanho);


/* Dos enteros y una cadena, cuyo tamaño se devuelve también */
/* Posible llamada desde Python: f(1, 2, ’tres’) */

{
char *fichero;
char *modo = "r";
int tamanhobloque = 0;
ok = PyArg_ParseTuple(args, "s|si", &fichero, &modo, &tamanhobloque);
/* Una cadena y, opcionalmente, otra cadena y un entero */
/* Posibles llamadas desde Python:
f(’spam’)
f(’spam’, ’w’)
f(’spam’, ’wb’, 100000) */
}

{
int izquierda, tope, derecha, base, h, v;
ok = PyArg_ParseTuple(args, "((ii)(ii))(ii)",
&izquierda, &tope, &derecha, &base, &h, &v);
/* Un rectángulo y un punto */
/* Posible llamada desde Python:
f(((0, 0), (400, 300)), (10, 10)) */
}

{
Py_complex c;
ok = PyArg_ParseTuple(args, "D:mifuncion", &c);
/* un complejo, más un nombre de función para los errores */
/* Posible llamada desde Python: mifuncion(1+2j) */
}

12 Capítulo 1. Extensión de Python con C o C++


1.8 Análisis de claves con PyArg_ParseTupleAndKeywords()
La función PyArg_ParseTupleAndKeywords() se declara así:

int PyArg_ParseTupleAndKeywords(PyObject *arg, PyObject *kwdict,


char *format, char **kwlist, ...);

Los parámetros arg y format son idénticos a los de la función PyArg_ParseTuple(). El parámetro kwdict es un
diccionario de claves recibidas como tercer parámetro del entorno de ejecución de Python. El parámetro kwlist es una
lista de cadenas terminadas en NULL que identifican los parámetros. Los nombres se casan con la información de tipos
de format de izquierda a derecha.
Nota: No es posible analizar tuplas anidadas si se usan argumentos clave. Los parámetros clave no presentes en kwlist
lanzarán TypeError.
He aquí un módulo de ejemplo que toma parámetros clave, basado en un ejemplo de Geoff Philbrick
(philbrick@hks.com):

1.8. Análisis de claves con PyArg_ParseTupleAndKeywords() 13


#include <stdio.h>
#include "Python.h"

static PyObject *
argsclave_loro(self, args, keywds)
PyObject *self;
PyObject *args;
PyObject *keywds;
{
int tension;
char *estado= "un fiambre";
char *accion = "despegaría";
char *tipo = "Azul noruego";

static char *kwlist[] = {"tension", "estado", "accion", "tipo", NULL};

if (!PyArg_ParseTupleAndKeywords(args, keywds, "i|sss", kwlist,


&tension, &estado, &accion, &tipo))
return NULL;

printf("-- Este loro no %s aunque le aplicaras %i voltios.\n",


accion, tension);
printf("-- Bello plumaje, el %s -- ¡Es %s!\n", tipo, estado);

Py_INCREF(Py_None);

return Py_None;
}

static PyMethodDef metodos_argsclave[] = {


/* La conversión de tipo de la función es necesaria porque PyCFunction
sólo toma dos parámetros PyObject* y argsclave_loro() toma tres */
{"loro", (PyCFunction)argsclave_loro, METH_VARARGS|METH_KEYWORDS},
{NULL, NULL} /* hoja roja */
};

void
initargsclave()
{
/* Crear el módulo y añadir las funciones */
Py_InitModule("argsclave", metodos_argsclave);
}

1.9 La función Py_BuildValue()


Esta función es la contrapartida de PyArg_ParseTuple(). Se declara así:
PyObject *Py_BuildValue(char *format, ...);

Reconoce un conjunto de unidades de formato similares a las reconocidas por PyArg_ParseTuple(), pero los
argumentos (que son entradas a la función y no salidas) no deben ser punteros, sino simples valores. Devuelve un
objeto Python nuevo, susceptible de ser devuelto por una función C invocada desde Python.
Una diferencia con PyArg_ParseTuple(): mientras ésta exige que su primer argumento sea una tupla (ya que
las listas de argumentos en Python siempre se representan mediante tuplas internamente), Py_BuildValue() no

14 Capítulo 1. Extensión de Python con C o C++


siempre construye una tupla. Sólo construye una tupla si su cadena de formato contiene dos o más unidades. Si la
cadena de formato está vacía, devuelve None; si contiene exactamente una unidad de formato, devuelve un objeto del
tipo correspondiente a dicho formato. Para forzar la construcción de una tupla, en los casos de cero o un elementos,
encerrar entre paréntesis la cadena de formato.
Cuando se pasan bloques de memoria como parámetros para proporcionar datos para formar los objetos, como en
los formatos ‘s’ y ‘s#’, se copian los datos necesarios. Los objetos creados por Py_BuildValue() nunca hacen
referencia a los bloques de memoria del usuario. En otras palabras, si el código de usuario llama a malloc() y
pasa la memoria reservada a Py_BuildValue(), es el código del usuario el responsable de llamar a free() para
liberar la memoria a la vuelta de Py_BuildValue().
En la siguiente descripción, la forma entrecomillada es la unidad de formato, la entrada entre paréntesis es el tipo de
objeto Python que devolverá la unidad de formato y la entrada entre corchetes es el tipo de valor(es) C que hay que
pasar.
Los caracteres espacio, tabulador, dos puntos y coma son ignorados en las cadenas de formato (pero no dentro de las
unidades de formato como ‘s#’). Se puede utilizar este efecto para hacer que las cadenas de formato sean un poco
más legibles.

‘s’ (string) [char *] Convierte una cadena C terminada en nulo a un objeto Python. Si la cadena C es NULL, se
devuelve None.
‘s#’ (string) [char *, int] Convierte una cadena C y su longitud a un objeto Python. Si la cadena C es NULL, se usa
None, haciendo caso omiso de la longitud.
‘z’ (cadena o None) [char *] Como ‘s’.
‘z#’ (cadena o None) [char *, int] Como ‘s#’.
‘u’ (cadena Unicode) [Py_UNICODE *] Convierte un bloque de datos Unicode (UCS-2) terminado en nulo a un
objeto Unicode de Python. Si el puntero al bloque es NULL, se devuelve None.
‘u#’ (cadena Unicode) [Py_UNICODE *, int] Convierte un bloque de datos Unicode (UCS-2) y su longitud a un
objeto Unicode de Python. Si el puntero al bloque es NULL, se devuelve None, haciendo caso omiso de la
longitud.
‘i’ (entero) [int] Convierte un int de C a un objeto entero de Python.
‘b’ (entero) [char] Como ‘i’.
‘h’ (entero) [short int] Como ‘i’.
‘l’ (entero) [long int] Convierte un long int de C a un objeto entero de Python.
‘c’ (cadena de longitud 1) [char] Convierte un int que representa un carácter a una cadena de Python de longitud
1.
‘d’ (float) [double] Convierte un double de C a un número en coma flotante de Python.
‘f’ (float) [float] Como ‘d’.

‘O’ (objeto) [PyObject *] Pasa un objeto Python intacto (salvo su saldo de referencias, incrementado en uno). Si el
objeto es un puntero NULL, se asume que la causa es que la llamada que obtuvo el argumento encontró un error
y activó una excepción. Por ello, Py_BuildValue() devolverá NULL pero no lanzará una excepción. Si no
hay una excepción activa, se activa PyExc_SystemError.
‘S’ (objeto) [PyObject *] Como ‘O’.

‘U’ (objeto) [PyObject *] Como ‘O’.

1.9. La función Py_BuildValue() 15


‘N’ (objeto) [PyObject *] Como ‘O’, pero sin incrementar el saldo de referencias del objeto. Es útil cuando el objeto
es creado por una llamada al constructor en la propia lista de argumentos.
‘O&’ (objeto) [convertidora, cualquiera] Convierte cualquiera a un objeto Python mediante una función convertido-
ra. Se llama a la función con cualquiera como parámetro (debe ser compatible con void *). Debe devolver un
objeto Python “nuevo” o NULL si hay algún error.
‘(elementos)’ (tupla) [elementos] Convierte una secuencia de valores C a una tupla de Python con el mismo número
de elementos.
‘[elementos]’ (lista) [elementos] Convierte una secuencia de valores C a una lista de Python con el mismo número
de elementos.
‘{items}’ (diccionario) [elementos] Convierte una secuencia de valores C a un diccionario de Python. Cada pareja
de valores C consecutivos (clave y valor, respectivamente) añade un elemento al diccionario.

Si hay un error en la cadena de formato, se activa la excepción PyExc_SystemError y se devuelve NULL.


Ejemplos (a la izquierda la llamada, a la derecha el valor de Python resultante):

Py_BuildValue("") None
Py_BuildValue("i", 123) 123
Py_BuildValue("iii", 123, 456, 789) (123, 456, 789)
Py_BuildValue("s", "hola") ’hola’
Py_BuildValue("ss", "hola", "mundo") (’hola’, ’mundo’)
Py_BuildValue("s#", "hola", 3) ’hol’
Py_BuildValue("()") ()
Py_BuildValue("(i)", 123) (123,)
Py_BuildValue("(ii)", 123, 456) (123, 456)
Py_BuildValue("(i,i)", 123, 456) (123, 456)
Py_BuildValue("[i,i]", 123, 456) [123, 456]
Py_BuildValue("{s:i,s:i}",
"abc", 123, "def", 456) {’abc’: 123, ’def’: 456}
Py_BuildValue("((ii)(ii)) (ii)",
1, 2, 3, 4, 5, 6) (((1, 2), (3, 4)), (5, 6))

1.10 Saldo de referencias


En lenguajes como C o C++, el programador es responsable de reservar y liberar la memoria del montículo. En
C, esto se realiza mediante las funciones malloc() y free(). En C++, los operadores new y delete se usan
esencialmente con el mismo significado; se implementan mediante malloc() y free(), por lo que la siguiente
discusión se restringirá a éstas.
Cada bloque de memoria reservado con malloc() debería, con el tiempo, devolverse al conjunto de memoria dispo-
nible mediante exactamente una llamada a free(). Es importante llamar a free() en el momento adecuado. Si se
olvida la dirección de un bloque pero no se llama a free() para esa dirección, la memoria del bloque queda inutili-
zable hasta la terminación del programa. Esto se denomina pérdida de memoria. Por otra parte, si un programa llama a
free() para un bloque de memoria y sigue utilizando la memoria, crea un conflicto de utilización con subsiguientes
usos de malloc() que juzguen disponible para nuevas reservas el mismo bloque de memoria. Esto se denomina uso
de memoria libre. Tiene las mismas graves consecuencias que hacer referencia a datos no inicializados: finalizaciones
no deseadas del programa, resultados inesperados y casques misteriosos.
Una causa común de pérdida de memoria son los recorridos poco comunes del código. Por ejemplo, una función
puede reservar un bloque de memoria, hacer unos cálculos y liberarla. Puede que un cambio de requisitos añada una
comprobación en los cálculos que detecte una condición de error que fuerce la salida prematura de la función. Es fácil

16 Capítulo 1. Extensión de Python con C o C++


olvidarse de liberar el bloque de memoria si se toma esta salida prematura, especialmente si se añade mucho después.
Estas pérdidas suelen pasar inadvertidas mucho tiempo. El error sólo se da en un pequeño número de llamadas y las
máquinas modernas suelen tener grandes cantidades de memoria virtual, por lo que la pérdida sólo se hace evidente
en un proceso largo que utilice la función con pérdidas frecuentemente. Por ello, es importante evitar las pérdidas
mediante una convención del código o una estrategia que minimice este tipo de errores.
Como Python hace un uso intensivo de malloc() y free(), necesita una estrategia para evitar las pérdidas de
memoria y el uso de memoria liberada. El método utilizado se denomina saldo de referencias. El principio es simple:
cada objeto contiene un contador, que se incrementa cada vez que se almacena una referencia al objeto en cualquier
parte y se decrementa cada vez que se borra la referencia. Cuando el saldo del contador es cero, significa que se ha
borrado la última referencia al objeto, por lo que se puede liberar su memoria.
Una estrategia alternativa se denomina recolección automática de basura (a veces, el saldo de referencias se considera
también una estrategia de recolección de basura, de ahí el uso de “automática” para distinguirlas). La gran ventaja de
la recolección automática de basura es que no es necesario que el usuario llame a free() explícitamente (se dice
que hay una mejora de velocidad o uso de memoria, pero no hay datos concluyentes). La desventaja es que para C no
existe ningún recolector automático de basura auténticamente transportable, mientras que el saldo de referencias se
puede implementar transportablemente (siempre que estén disponibles las funciones malloc() y free(), lo que
garantiza el estándar de C). Quizás algún día se haga disponible algún recolector automático de basura para C. Hasta
entonces, habrá que vivir contando referencias.

1.10.1 Saldo de referencias en Python

Hay dos macros, Py_INCREF(x) y Py_DECREF(x), que gestionan el incremento y decremento del saldo de re-
ferencias. Py_DECREF() también libera el objeto cuando el saldo llega a cero. Para ser más flexible, no llama a
free() directamente, sino que realiza una llamada mediante un puntero a función del tipo de objeto del objeto. Por
esto y otros motivos, cada objeto también contiene un puntero a su tipo de objeto.
Queda la gran cuestión: cuándo usar Py_INCREF(x) y Py_DECREF(x). Vamos a presentar algunos términos.
Nadie “posee” un objeto, pero puede poseer una referencia al objeto. El saldo de referencias del objeto se define
ahora como el número de referencias a él que tengan dueño. El dueño de una referencia tiene la responsabilidad de
llamara a Py_DECREF() cuando no se necesite más la referencia. La propiedad de una referencia es transferible. Hay
tres modos de deshacerse de una referencia propia: Transferirla, almacenarla o llamar a Py_DECREF(). Si se olvida
deshacerse de una referencia propia se causará una pérdida de memoria.
También es posible tomar prestada3 una referencia al objeto. El que toma prestada la referencia no debe utilizar el
objeto más tiempo que su dueño. Si se usa una referencia prestada después de que el dueño se haya deshecho de ella,
hay un riesgo de que se utilice memoria liberada que se debe evitar por completo4 .
La ventaja de tomar prestada una referencia en lugar de poseerla estriba en que no hay que ocuparse de deshacerse
de la referencia en todo recorrido posible del código. Dicho con otras palabras, con una referencia prestada no hay
peligro de pérdidas en el caso de salidas prematuras. La desventaja es que hay situaciones muy sutiles en que código
aparentemente correcto utiliza una referencia prestada cuando su dueño ya se había deshecho de ella.
Se puede tomar posesión de una referencia prestada llamando a Py_INCREF(). Esto no afecta al estado del dueño
original de la referencia, sólo crea una nueva propiedad de la referencia y carga al nuevo propietario con las mismas
responsabilidades de dueño (es decir, el nuevo propietario debe deshacerse de la referencia correctamente, como
también debe el antiguo propietario).
3 Lametáfora de “tomar prestada” una referencia no es completamente fiel: el dueño mantiene una copia de la referencia.
4 Comprobar que el saldo de referencia es positivo no funciona el propio contador puede estar en memoria liberada y reasignada, por lo que
puede pertenecer a otro objeto.

1.10. Saldo de referencias 17


1.10.2 Reglas de propiedad

Siempre que se mete o saca una referencia a un objeto de una función, es parte de la especificación de la interfaz de la
función si se transfiere la propiedad (en el sentido de posesión, no de característica) con la referencia o no.
La mayoría de las funciones que devuelven una referencia a un objeto traspasan la propiedad con la referencia. En
particular, todas las funciones cuyo propósito es la creación de un nuevo objeto, por ejemplo PyInt_FromLong()
y Py_BuildValue(), traspasan la propiedad al receptor. Aun en el caso en que, de hecho, el receptor no reciba
una referencia a un objeto nuevo del todo, se recibe la propiedad. Por ejemplo, PyInt_FromLong() mantiene una
caché de valores comunes y puede devolver una referencia a un elemento de la caché.
Muchas funciones que extraen objetos de otros objetos también traspasan la propiedad con la referencia, por
ejemplo PyObject_GetAttrString(). La cosa no está tan clara aquí, sin embargo, pues algunas ruti-
nas comunes no cumplen esto: PyTuple_GetItem(), PyList_GetItem(), PyDict_GetItem() y Py-
Dict_GetItemString() devuelven referencias prestadas de la tupla, lista o diccionario.
La función PyImport_AddModule() también devuelve una referencia prestada, aunque haya generado el objeto
devuelto: Esto es posible porque se almacena una referencia propia al objeto en sys.modules.
Al pasar una referencia a objeto a otra función, en general, la función toma prestada la referencia. Si necesita almace-
narla, utilizará Py_INCREF() para hacerse propietario independiente. Hay exactamente dos importantes excepciones
a esta regla: PyTuple_SetItem() y PyList_SetItem(). Estas funciones toman la propiedad del elemento que
reciben ¡aunque fracasen! PyDict_SetItem() y similares, sin embargo, no adquieren la propiedad, son “norma-
les”.
Cuando se llama a una función de C desde Python, toma prestadas las referencias de sus argumentos del llamante. El
llamante posee referencias a los objetos, por lo que la vida de la referencia prestada está garantizada hasta el retorno
de la función. Sólo cuando una de estas referencias prestadas se deba almacenar o pasar a su vez se debe convertir en
referencia propia llamando a Py_INCREF().
La referencia a objeto devuelta desde una función C llamada desde Python debe ser una referencia propia; se traspasa
la propiedad de la función a su llamante.

1.10.3 En la cuerda floja

Hay unas cuantas situaciones en las la que el uso aparentemente inocua de una referencia prestada puede causar
quebraderos de cabeza. Todas tienen que ver con llamadas implícitas al intérprete, que pueden causar que el propietario
de una referencia se deshaga de ella.
La primera y más importante causa que hay que conocer es el uso de Py_DECREF() sobre un objeto independiente
mientras se toma prestada una referencia a un elemento de lista. Por ejemplo:

bug(PyObject *lista) {
PyObject *elemento = PyList_GetItem(list, 0);

PyList_SetItem(lista, 1, PyInt_FromLong(0L));
PyObject_Print(elemento, stdout, 0); /* ¡BUG! */
}

Esta función toma prestada una referencia a list[0], luego reemplaza list[1] con el valor 0 y, por último,
presenta la referencia prestada. Parece inofensivo, ¿no? Pues no.
Sigamos el flujo de control de PyList_SetItem(). La lista posee referencias a todos sus elementos, por lo que
cuando se reemplaza el elemento 1, se ha de deshacer del elemento 1 original. Supongamos ahora que dicho elemen-
to fuera una instancia de una clase definida por el usuario y que dicha clase definiera un método __del__(). Si
esta instancia de clase tuviera un saldo de referencias igual a 1, deshacerse de ella hará que se llame a su método

18 Capítulo 1. Extensión de Python con C o C++


__del__().
Al estar escrito en Python, el método __del__() puede ejecutar código Python arbitrario. ¿Podría hacer algo para
invalidar la referencia a elemento en bug()? ¡Por supuesto! Suponiendo que la lista pasada a bug() es accesible
por el método __del__() podría ejecutar una sentencia análoga a ‘del list[0]’. Suponiendo que ésta fuera la
última referencia al objeto, se liberaría la memoria asociada, invalidando elemento.
La solución, conocida la fuente del problema, es sencilla: incrementar temporalmente el saldo de referencias. La
versión correcta de la función es:
no_bug(PyObject *lista) {
PyObject *elemento = PyList_GetItem(lista, 0);

Py_INCREF(elemento);
PyList_SetItem(lista, 1, PyInt_FromLong(0L));
PyObject_Print(elemento, stdout, 0);
Py_DECREF(elemento);
}

Esto está basado en hechos reales. Una versión anterior de Python contenía variantes de este bug y alguien se pasó una
cantidad considerable de tiempo para descubrir por qué fracasaban sus métodos __del__(). . .
El segundo caso de problemas de referencias prestadas afecta a las hebras de ejecución. Normalmente, las múltiples
hebras de ejecución no se estorban, porque hay un bloque global que protege el espacio nominal de Python comple-
to. Sin embargo, es posible liberar este bloqueo usando la macro Py_BEGIN_ALLOW_THREADS y reactivarlo con
Py_END_ALLOW_THREADS. Esto es común en los bloques de llamadas E/S bloqueantes, para permitir que otras
hebras usen la CPU mientras se espera que se complete la E/S. Obviamente, la función siguiente presenta el mismo
problema que la anterior:
bug(PyObject *lista) {
PyObject *elemento = PyList_GetItem(lista, 0);
Py_BEGIN_ALLOW_THREADS
...llamada a E/S bloqueante...
Py_END_ALLOW_THREADS
PyObject_Print(elemento, stdout, 0); /* ¡BUG! */
}

1.10.4 Punteros NULL

En general, las funciones que toman referencias a objetos como argumentos no esperan que se les pasen punteros
NULL, y harán que el intérprete casque si se hace. Las funciones que devuelven referencias a objetos suelen devolver
NULL sólo si quieren indicar que ha ocurrido una excepción. La razón para no comprobar los argumentos NULL es
que las funciones pasan a menudo los objetos que reciben a otra función. Si cada función tuviera que comprobar los
NULL, habría una gran cantidad de comprobaciones redundantes, que ralentizarían el código.
Es mejor comprobar los NULL sólo en el “origen”, es decir, cuando se recibe un puntero que pudiera ser nulo, en
malloc() o desde una función que pueda lanzar una excepción.
Las macros Py_INCREF() y Py_DECREF() no comprueban los punteros NULL. Sin embargo, sí lo hacen sus
hermanas Py_XINCREF() y Py_XDECREF().
Las macros que verifican el tipo de objeto (Pytype_Check()) no comprueban si los punteros son NULL. De nuevo,
hay mucho código que llama a éstas en fila para comprobar diferentes tipos esperados, lo que causaría comprobaciones
redundantes. No hay versiones con comprobación de NULL.
El mecanismo de llamada a función de C garantiza que la lista de argumentos pasados a funciones C (args en los

1.10. Saldo de referencias 19


ejemplos) nunca es NULL y, de hecho, garantiza que siempre es una tupla5 .
Es un error grave dejar que un puntero NULL se “escape” hasta el usuario de Python.

1.11 Escritura de extensiones en C++


Es posible escribir módulos de extensión en C++, con restricciones. Si el programa principal (el intérprete de Python)
se compila y enlaza con el compilador de C, no se pueden utilizar objetos globales o estáticos con constructores. Este
problema no se da si el programa principal está compilado con el compilador de C++. Las funciones a las que llamará
el intérprete de Python (en particular, las de inicialización del módulo) se han de declarar con extern "C". No es
necesario encerrar los ficheros de cabecera de Python entre extern "C"{...}. Ya usan esta construcción si está
definido el símbolo ‘__cplusplus’ (todos los compiladores de C++ recientes definen este símbolo).

1.12 Proporcionar una API de C para un módulo de extensión


Muchos módulos de extensión se limitan a proporcionar nuevas funciones y tipos para usarlos desde Python, pero a
veces el código de un módulo de extensión puede resultar útil a otros módulos de extensión. Por ejemplo, un módulo
de extensión puede proporcionar un tipo “colección” que funcione como una lista desordenada. Del mismo modo que
la lista estándar de Python tiene una API de C que permite que los módulos de extensión creen y manipulen listas,
este nuevo tipo colección debería estar dotado de un conjunto de funciones C para su manipulación directa desde otros
módulos de extensión.
A primera vista parece fácil: sólo hay que escribir las funciones (sin declararlas, static, claro), proporcionar un
fichero de cabecera apropiado y documentar la API de C. Y, de hecho, esto funcionaría si todos los módulos de exten-
sión se enlazaran siempre estáticamente contra el intérprete de Python. Cuando se usan los módulos como bibliotecas
compartidas, no obstante, los símbolos definidos en un módulo pueden no ser visibles desde otro módulo. Los detalles
de visibilidad dependen del sistema operativo: algunos sistemas usan un espacio nominal global para el intérprete
de Python y todos los módulos de extensión (por ejemplo, Windows), mientras otros requieren una lista explícita de
símbolos importados en tiempo de compilación (por ejemplo, AIX) u ofrecen una opción de estrategias (la mayoría de
los Unix). E incluso si los símbolos tienen visibilidad global, ¡puede que el módulo cuyas funciones desea llamar no
esté todavía cargado!
La portabilidad, por consiguiente, requiere no hacer ninguna suposición sobre la visibilidad de los símbolos. Esto su-
pone que todos los símbolos de los módulos de extensión deben declararse static, salvo la función de inicialización
del módulo, para evitar conflictos con nombres de otros módulos de extensión (como se describió en la sección 1.4).
También significa que todos los símbolos que deberían ser accesibles desde otros módulos de extensión deben expor-
tarse de un modo diferente.
Python proporciona un mecanismo especial para pasar la información a nivel de C (es decir, punteros) de un módulo
de extensión a otro: Los CObjects. Un CObject es un tipo de dato de Python que almacena un puntero (void *). Los
CObjects sólo se pueden crear y acceder a través de su API de C, pero se pueden pasar y traspasar como cualquier otro
objeto de Python. En particular, se pueden asignar a un nombre del espacio nominal del módulo de extensión. Otros
módulos de extensión pueden importar el módulo, recuperar el valor del nombre y recuperar el puntero del CObject.
Existen muchos modos de usar los CObjects para exportar el API C de un módulo de extensión. Cada nombre podría
tener su propio CObject o podrían reunirse en una matriz todos los punteros del API de C y publicar su dirección en un
CObject. Las tareas de guardar y recuperar los punteros pueden repartirse entre el módulo que proporciona el código
y los módulos cliente.
El siguiente ejemplo muestra una solución que vuelca la mayor carga en el autor del módulo que exporta, lo que es
apropiado en los módulos de bibliotecas de uso común. Almacena todos los punteros del API de C (¡uno en el ejemplo!)
en una matriz de punteros void que pasa a ser el valor de un CObject. El fichero de cabecera correspondiente al
5 Estas garantías se pierden si se usa la convención de llamadas “antigua”, que se encuentra a menudo en código heredado.

20 Capítulo 1. Extensión de Python con C o C++


módulo proporciona una macro que se ocupa de importar el módulo y recuperar sus punteros del API C; los módulos
cliente sólo tienen que llamar a esta macro antes de acceder al API C.
El módulo servidor (servidor de código, no en el sentido estricto) es una modificación del módulo spam de la sec-
ción 1.1. La función spam.system() no llama a la función de biblioteca de C system() directamente, sino a una
función PySpam_System(), que sería por supuesto más complicada en realidad (por, ejemplo, añadiendo “spam”
a cada orden). Esta función, PySpam_System() también se exporta a otros módulos de extensión.
La función PySpam_System() es una simple función C, declarada static como todo el resto:

static int
PySpam_System(command)
char *command;
{
return system(command);
}

La función spam_system() tiene modificaciones triviales:

static PyObject *
spam_system(self, args)
PyObject *self;
PyObject *args;
{
char *command;
int sts;

if (!PyArg_ParseTuple(args, "s", &command))


return NULL;
sts = PySpam_System(command);
return Py_BuildValue("i", sts);
}

Al principio del módulo, justo tras la línea

#include "Python.h"

se deben añadir dos líneas:

#define SPAM_MODULE
#include "spammodule.h"

El #define se usa para indicar al fichero de cabecera que se está incluyendo en el módulo servidor, no en un módulo
cliente. Por último, la función de inicialización del módulo debe ocuparse de inicializar la matriz de punteros del API
de C:

1.12. Proporcionar una API de C para un módulo de extensión 21


void
initspam()
{
PyObject *m, *d;
static void *PySpam_API[PySpam_API_pointers];
PyObject *c_api_object;
m = Py_InitModule("spam", SpamMethods);

/* Inicializar la matriz de punteros del API de C */


PySpam_API[PySpam_System_NUM] = (void *)PySpam_System;

/* Crear un CObject que contenga la dirección de la matriz de punteros del API */


c_api_object = PyCObject_FromVoidPtr((void *)PySpam_API, NULL);

/* Crear un nombre para este objeto en el espacio nominal del módulo */


d = PyModule_GetDict(m);
PyDict_SetItemString(d, "_C_API", c_api_object);
}

Véase que PySpam_API se declara static; en caso contrario, ¡la matriz de punteros desaparecería al terminar la
ejecución de initspam!
El grueso del trabajo está en el fichero de cabecera ‘spammodule.h’, que tiene este aspecto:

22 Capítulo 1. Extensión de Python con C o C++


#ifndef Py_SPAMMODULE_H
#define Py_SPAMMODULE_H
#ifdef __cplusplus
extern "C" {
#endif

/* Fichero de cabecera de spammodule */

/* Funciones del API de C */


#define PySpam_System_NUM 0
#define PySpam_System_RETURN int
#define PySpam_System_PROTO (char *command)

/* No total de punteros del API de C */


#define PySpam_API_pointers 1

#ifdef SPAM_MODULE
/* Esta sección se utiliza al compilar spammodule.c */

static PySpam_System_RETURN PySpam_System PySpam_System_PROTO;

#else
/* Esta sección se utiliza en los módulos que utilizan el API de spammodule */

static void **PySpam_API;

#define PySpam_System \
(*(PySpam_System_RETURN (*)PySpam_System_PROTO) PySpam_API[PySpam_System_NUM])

#define import_spam() \
{ \
PyObject *module = PyImport_ImportModule("spam"); \
if (module != NULL) { \
PyObject *module_dict = PyModule_GetDict(module); \
PyObject *c_api_object = PyDict_GetItemString(module_dict, "_C_API"); \
if (PyCObject_Check(c_api_object)) { \
PySpam_API = (void **)PyCObject_AsVoidPtr(c_api_object); \
} \
} \
}

#endif

#ifdef __cplusplus
}
#endif

#endif /* !defined(Py_SPAMMODULE_H */

Todo cuanto tiene que hacer un cliente para acceder a la función PySpam_System() es llamara a la función (más
bien macro) import_spam() en su función de inicialización:

1.12. Proporcionar una API de C para un módulo de extensión 23


void
initclient()
{
PyObject *m;

Py_InitModule("client", ClientMethods);
import_spam();
}

La mayor pega de esta solución es que el fichero ‘spammodule.h’ es bastante complicado, Sin embargo, la estructura
básica es la misma para cada función a exportar, por lo que sólo hay que aprenderlo una vez.
Por último, hay que mencionar que los CObjects ofrecen funciones adicionales, especialmente para reserva y libe-
ración de memoria del puntero almacenado en un CObject. Los detalles están descritos en Manual de referencia
del API Python/C en la sección “CObjects” y en la implementación de los CObjects (ficheros ‘Include/cobject.h’ y
‘Objects/cobject.c’ de la distribución de fuentes de Python).

24 Capítulo 1. Extensión de Python con C o C++


CAPÍTULO

DOS

Construcción de extensiones en C y C++


en U NIX

Empezando por Python 1.4, Python proporciona un “makefile” especial para generar makefiles para generar extensio-
nes de carga dinámica e intérpretes a medida. Este fichero especial genera makefiles que reflejan diversas variables
del sistema determinadas mediante el programa “configure” en el momento de construir el intérprete de Python, para
que los que vayan a generar módulos no tenga que volver a proporcionar estos valores. Esto simplifica sobremanera el
proceso de generación de extensiones e intérpretes a medida en sistemas Unix.
El makefile especial se distribuye en el fichero ‘Misc/Makefile.pre.in’ en la distribución de fuentes de Python. El primer
paso en la generación de extensiones e intérpretes a medida es copiar este makefile al directorio de desarrollo que
contiene los fuentes del módulo de extensión.
El makefile especial, ‘Makefile.pre.in’ usa metadatos proporcionados por el fichero ‘Setup’. El formato del fichero
‘Setup’ es el mismo que el del fichero ‘Setup’ (o ‘Setup.in’) del directorio ‘Modules/’ de la distribución de fuentes de
Python. ‘Setup’ contiene definiciones de variables:

EC=/projects/ExtensionClass

y líneas de descripción del modulo. Puede contener también líneas en blanco y comentarios que empiecen por ‘#’.
Una línea de descripción de módulo incluye un nombre de módulo, ficheros fuente opciones, referencias a variables y
otros ficheros de entrada, como bibliotecas o ficheros objeto. Considérese el siguiente ejemplo mínimo:

ExtensionClass ExtensionClass.c

Ésta es la forma más simple de línea de definición de módulo. Define un módulo, ExtensionClass, con un solo
fichero fuente, ‘ExtensionClass.c’.
Este ejemplo, ligeramente más complejo, utiliza una opción -I para especificar un directorio de ficheros incluidos:
EC=/projects/ExtensionClass
cPersistence cPersistence.c -I$(EC)

Este ejemplo también ilustra el formato de las referencias a variables.


En los sistemas que tienen soporte de enlace dinámico, el fichero ‘Setup’ debería comenzar por
*shared*

25
para indicar que los módulos definidos en ‘Setup’ han de ser generados como módulos de enlace dinámico. Se puede
usar una línea que contenga sólo ‘*static*’ para indicar que los módulos que sigan deben compilarse estáticamente.
He aquí un fichero ‘Setup’ completo para construir el módulo cPersistent:

# Fichero de configuración para generar el módulo cPersistence.


# Es importante que el texto comience en la primera columna.
*shared*

# Necesitamos la ruta hasta el directorio que contiene los ficheros


# de inclusión de ExtensionClass.
EC=/projects/ExtensionClass
cPersistence cPersistence.c -I$(EC)

Tras crear el fichero ‘Setup’, se ejecuta ‘Makefile.pre.in’ con ‘boot’ como objetivo para generar el makefile:

make -f Makefile.pre.in boot

Con lo que se genera el fichero Makefile. Para generar las extensiones, basta con ejecutar make con el Makefile creado,
que se utiliza por omisión:

make

No es necesario volver a ejecutar ‘Makefile.pre.in’ si cambia ‘Setup’. El makefile se reconstruye automáticamente si


cambia el fichero ‘Setup’.

2.1 Construcción de intérpretes a medida


El fichero make generado por ‘Makefile.pre.in’ puede ejecutarse con el objetivo ‘static’ para construir un intérprete:

make static

Los módulos definidos en el fichero Setup antes de la línea ‘*shared*’ se enlazarán estáticamente al intérprete.
Típicamente, se omite la línea ‘*shared*’ en el fichero Setup si se desea obtener un intérprete a medida.

2.2 Opciones de definición del módulo


Hay soporte para varias opciones del compilador:

Opción Significado
-C Indicar al preprocesador de C que no descarte los comentarios
-Dname=value Definir una macro
-Idir Especificar un directorio de include, dir
-Ldir Especificar un directorio de bibliotecas de enlace estático, dir
-Rdir Especificar un directorio de bibliotecas de enlace dinámico, dir
-llib Enlazar una biblioteca, lib
-Uname Des-definir una macro

26 Capítulo 2. Construcción de extensiones en C y C++ en U NIX


Se pueden incluir (con calzador) otras opciones de compilación poniéndolas en variables.
Los ficheros fuente pueden incluir los ficheros con extensiones ‘.c’, ‘.C’, ‘.cc’, ‘.cpp’, ‘.cxx’ y ‘.c++’.
Otros ficheros de entrada incluyen los de extensiones ‘.a’, ‘.o’, ‘.sl’ y ‘.so’.

2.3 Ejemplo
He aquí un ejemplo más complicado de ‘Modules/Setup.in’:

GMP=/ufs/guido/src/gmp
mpz mpzmodule.c -I$(GMP) $(GMP)/libgmp.a

que podría escribirse también como:

mpz mpzmodule.c -I$(GMP) -L$(GMP) -lgmp

2.4 Distribución de los módulos de extensión


Al distribuir los módulos de extensión en forma de código fuente, hay que asegurarse de incluir un fichero
‘Setup’ file. El fichero ‘Setup’ debería llamarse ‘Setup.in’ en la distribución. El fichero de generación del makefi-
le, ‘Makefile.pre.in’, copiará ‘Setup.in’ a ‘Setup’. Distribuir un fichero ‘Setup.in’ facilita a la gente personalizar el
fichero ‘Setup’ a la vez que mantiene el original en ‘Setup.in’.
Es una buena idea incluir una copia de ‘Makefile.pre.in’ para la gente que no tiene una distribución de fuentes de
Python.
No se debe distribuir un makefile. Los que compilen los módulos deberían usar ‘Makefile.pre.in’ para generar su
propio makefile. Un fichero ‘README’ (léeme) incluido en el paquete debería proporcionar instrucciones sencillas
para generar el módulo binario.
Se está trabajando para facilitar la construcción e instalación de extensiones de Python en todas las plataformas.
Las nuevas soluciones sustituirán a la actual en algún momento. Si se desea obtener más información o colaborar,
consúltese http://www.python.org/sigs/distutils-sig/ en la web de Python.

2.3. Ejemplo 27
28
CAPÍTULO

TRES

Construcción de extensiones en C y C++


en Windows

Este capítulo explica brevemente cómo crear un módulo de extensión para Python utilizando Microsoft Visual C++
y sigue con información avanzada sobre su funcionamiento. El material explicativo es útil para el programador de
Windows que está aprendiendo a construir extensiones de Python y para el programador de U NIX interesado en
producir software capaz de compilar en U NIX y Windows.

3.1 La receta
Esta sección proporciona una receta para construir una extensión de Python en Windows.
Bajar el instalador binario de http://www.python.org/ e instalar Python. El instalador binario contiene todas las cabeceras
requeridas excepto la cabecera necesaria ‘config.h’.
Bajar la distribución de fuentes y extraerla en una ubicación adecuada. Copiar el ‘config.h’ del directorio ‘PC/’ al
directorio ‘include/’ creado por el instalador.
Crear un fichero ‘Setup’ para el futuro módulo de extensión, según se describe en el capítulo 2.
Obtener el guion ‘compile.py’ de David Ascher desde http://starship.python.net/crew/da/compile/. Ejecutar el guion
para crear los ficheros de proyecto de Microsoft Visual C++.
Abrir el fichero DSW con Visual C++ y seleccionar Build.
Si el módulo crea nuevos tipos, la siguiente línea puede dar problemas:

PyObject_HEAD_INIT(&PyType_Type)

Cambiarla a:

PyObject_HEAD_INIT(NULL)

y añadir lo siguiente a la función de inicialización del módulo:

MyObject_Type.ob_type = &PyType_Type;

Se pueden obtener más detalles sobre esto en la sección 3 del FAQ de Python (http://www.python.org/doc/FAQ.html).

29
3.2 Diferencias entre U NIX y Windows
U NIX y Windows parten de paradigmas completamente diferentes para la carga de código en tiempo de ejecución.
Antes de intentar construir un módulo con carga dinámica, se debe comprender cómo funciona el sistema final del
usuario.
En U NIX, un fichero objeto compartido (shared object, ‘.so’) contiene código que será utilizado por el programa junto
con los nombres de las funciones y datos que espera encontrar en el programa. Cuando el fichero se une al programa,
se cambian todas las referencias a dichas funciones y datos para que apunten a sus direcciones de memoria reales en
el programa. A grandes rasgos, se realiza una operación de enlace.
En Windows, un fichero de biblioteca de enlace dinámico, (dynamic-link library, ‘.dll’) no tiene referencias pendientes.
En lugar de ello, todo acceso a funciones y datos pasa por una tabla de consulta. Por ello, no hay que arreglar el código
de la DLL para que haga referencia a la memoria del programa. El programa ya utiliza la tabla de búsquedas, lo que
cambia en tiempo de ejecución es la tabla de búsquedas para apuntar a las funciones y datos finales.
En U NIX, sólo hay un tipo de fichero de biblioteca (‘.a’) que contiene código de varios ficheros objeto (‘.o’). En el
paso de enlace para crear un fichero objeto compartido (‘.so’), el enlazador puede encontrarse que desconoce dónde
se define un identificador. El enlazador lo buscará en los ficheros objeto y en las bibliotecas. Si lo encuentra, incluirá
todo el código del fichero objeto.
En Windows, existen dos tipos de biblioteca, una biblioteca estática y una biblioteca de importación (ambas llamadas
‘.lib’). Una biblioteca estática es como un fichero ‘.a’ de U NIX: contiene código que se incluirá si es necesario.
Una biblioteca de importación se usas sólo para asegurar al enlazador que un identificador concreto es legal y estará
presente en el programa cuando se cargue la DLL. Por ello, el enlazador utiliza la información de la biblioteca de
importación para construir la tabla de consulta para usar los identificadores no incluidos en la DLL. Cuando se enlaza
una aplicación o DLL, puede generarse una biblioteca de importación, que tendrá que usarse para futuras DLLs que
dependan de los símbolos de la aplicación o DLL.
Supóngase que se están construyendo dos módulos de carga dinámica, B y C, que han de compartir otro bloque de
código A. En U NIX, no se pasaría ‘A.a’ al enlazador para ‘B.so’ y ‘C.so’; eso causaría que se incluyera dos veces y
tanto B como C tendrían su propio ejemplar. En Windows, al construir ‘A.dll’ se construiría ‘A.lib’. Sí se pasaría ‘A.lib’
al enlazador tanto en B como en C. ‘A.lib’ no contiene código, sólo información que se usará en tiempo de ejecución
para acceder al código de A.
En Windows, usar una biblioteca de importación es análogo a usar ‘import spam’; proporciona acceso a los nom-
bres de spam, pero no genera una copia aparte. En U NIX, enlazar con una biblioteca es más como ‘from spam
import *’; sí genera una copia aparte1 .

3.3 Uso de DLLs en la práctica


Python para Windows se construye con Microsoft Visual C++; otros compiladores pueden funcionar o no (parece que
el de Borland sí). El resto de la sección es específico de MSVC++.
Al crear DLLs en Windows, se ha de pasar ‘python15.lib’ al enlazador. Para construir dos DLLs, spam y ni (que usa
funciones C de spam), se podrían usar las siguientes órdenes:

cl /LD /I/python/include spam.c ../libs/python15.lib


cl /LD /I/python/include ni.c spam.lib ../libs/python15.lib

La primera orden genera tres ficheros: ‘spam.obj’, ‘spam.dll’ y ‘spam.lib’. ‘Spam.dll’ no contiene ninguna función de
Python (del estilo de PyArg_ParseTuple()), pero sabe como acceder al código de Python gracias a ‘python15.lib’.
La segunda orden genera ‘ni.dll’ (además de ‘.obj’ y ‘.lib’), que dispone de información para acceder a las funciones
1 N. del T.: No estoy seguro de que haber comprendido la explicación, o la analogía no es muy buena.

30 Capítulo 3. Construcción de extensiones en C y C++ en Windows


relevantes de spam y del ejecutable de Python.
No se exportan todos los identificadores a la tabla de consulta. Si se desea que otros módulos (incluido Pyt-
hon) sean capaces de ver los identificadores, se debe declarar con ‘_declspec(dllexport)’, como en
‘void _declspec(dllexport) initspam(void)’ o ‘PyObject _declspec(dllexport) *Ni-
GetSpamData(void)’.
Developer Studio incluirá en el lote muchas bibliotecas de importación que no son en realidad necesarias, añadiendo
unos 100K al ejecutable. Para librarse del lastre, se debe usar el diálogo de Project Settings, carpeta Link, para es-
pecificar ignore default libraries (hacer caso omiso de las bibliotecas predeterminadas). Se debe agregar a la lista de
bibliotecas el ‘msvcrtxx.lib’ correcto.

3.3. Uso de DLLs en la práctica 31


32
CAPÍTULO

CUATRO

Empotrado de Python en otra aplicación

Empotrar Python es parecido a extenderlo, pero no igual. La diferencia radica en que al extender Python, el programa
principal de la aplicación sigue siendo el intérprete de Python, mientras que al empotrar Python, el programa principal
puede no tener nada que ver con Python, ocasionalmente alguna parte del programa invocará al intérprete para que
ejecute cierto código Python.
Por ello, al empotrar Python, se debe proporcionar el programa principal. Una de las cosas que debe hacer el programa
principal es inicializar el intérprete de Python. Como mínimo, hay que llamar a la función Py_Initialize() (en
MacOS, a la función PyMac_Initialize()). Hay llamadas opcionales para pasar argumentos de línea de órdenes
a Python. Más tarde, se puede llamar al intérprete desde cualquier lugar de la aplicación.
Hay varias maneras de llamar al intérprete: se puede pasar una cadena que contenga sentencias de Python a Py-
Run_SimpleString() o pasar un puntero al fichero stdio y un nombre de fichero (tan sólo para identificación
de los mensajes de error) a PyRun_SimpleFile(). También se puede llamar a las operaciones de nivel inferior
descritas en los anteriores capítulos para construir y usar objetos de Python.
Se puede encontrar una sencilla demostración de empotrado de Python en el directorio ‘Demo/embed/’ de la distribu-
ción de fuentes.

4.1 Empotrado de Python en C++


También es posible empotrar Python en un programa C++ ; la manera precisa de hacerlo dependerá de los detalles del
sistema C++ utilizado; en general habrá que escribir el programa principal en C++ y usar el compilador de C++ para
compilar y enlazar el programa. No es necesario recompilar el propio Python con C++.

4.2 Requisitos de enlazado


Aunque el guion configure que viene con las fuentes de Python construirá correctamente Python para que exporte
los símbolos necesarios para las extensiones de enlace dinámico, esto no es así de manera automática en las aplica-
ciones que empotran la biblioteca de Python estáticamente, al menos en U NIX. Esto supone un problema cuando la
aplicación está enlazada a la biblioteca de ejecución estática (‘libpython.a’) y necesita cargar extensiones dinámicas
(implementadas como ficheros ‘.so’).
El problema está en que algunos puntos de entrada están definidos en el entorno de ejecución de Python sólo para ser
utilizados por los módulos de extensión. Si la aplicación huésped no usa ninguno de estos puntos de entrada, algunos
enlazadores no incluirán estos puntos de entrada en la tabla de símbolos del ejecutable final. Es necesario proporcionar
ciertas opciones al enlazador para indicarle que no elimine esos símbolos.
Determinar las opciones correctas para una plataforma dada puede se difícil, pero afortunadamente la configuración
de Python ya tiene los valores necesarios. Para obtenerlos de un intérprete de Python instalado, se ha de arrancar una
sesión interactiva:

33
>>> import distutils.sysconfig
>>> distutils.sysconfig.LINKFORSHARED
’-Xlinker -export-dynamic’

El contenido de la cadena presentada será el bloque de opciones que se deben usar. Si la cadena aparece vacía, no es
necesario añadir opciones adicionales. La definición de LINKFORSHARED se corresponde con la variable del mismo
nombre del ‘Makefile’ principal de Python.

34 Capítulo 4. Empotrado de Python en otra aplicación


APÉNDICE

Informes de error

Python es un lenguaje de programación maduro que se ha ganado reputación por su estabilidad. Para mantener esta
reputación, los desarrolladores agradecerían recibir noticia de cualquier deficiencia que encuentres en Python o su
documentación.
Se deben enviar todos los informes de error mediante el Seguimiento de errores de Python (Python Bug Tracker),
ubicado en SourceForge (http://sourceforge.net/bugs/?group_id=5470). El seguimiento de errores propociona un for-
mulario Web que permite rellenar la información pertinente y enviarla a los desarrolladores.
Antes de enviar un informe, se ruega entrar en SourceForge si eres miembro. Esto permitirá a los desarrolladores po-
nerse en contacto contigo para obtener información adicional, si fuera necesario. Si no eres miembro de SourceForge,
pero no te importa que los desarrolladores se pongan en contacto contigo, puedes incluir tu dirección en la descripción
del error. En tal caso, sé consciente de que la información quedará disponible para el público.
El primer paso para rellenar un informe es determinar si el problema ya ha sido informado. La ventaja de hacer esto,
además de ahorrar el tiempo de los desarrolladores, es que puedes enterarte de lo que se ha hecho para solucionarlo.
Puede ser que ya esté resulelto en la siguiente versión o que haga falta información adicional (¡en tal caso, estás
invitado a proporcionarla si puedes!). Para hacerlo, busca en la base de datos de errores usando la caja de búsqueda
del final de la página.
Si el problema que estás enviando no está ya en el seguimiento de errores, vuelve al seguimiento
(http://sourceforge.net/bugs/?group_id=5470). Selecciona el enlace “Submit a Bug”, enviar un error, del principio de
la página para abrir el formulario de envío de errores.
El formulario de envío tiene varios campos. Los únicos exigidos son los de “Resumen” y “Detalles”. Para el resu-
men, introduce un descripción muy breve del problema; menos de diez palabras está bien. En el campo de detalles,
describe el problema en detalle, incluyendo el resultado esperado y obtenido. Asegúrate de incluir el número de ver-
sión de Python utilizado, si había módulos de extensión involucrados y la plataforma hardware y software (incluidas
versiones).
El otro campo que te podría interesar es el de “Categoría”, que permite clasificar el informe de error en una categoría
amplia (como “Documentación” or “Biblioteca”).
Cada informe de error se asignará a un desarrollador, que determinará qué hay que hacer para corregir el problema. Si
tienes una cuenta en Sourceforge y habías iniciado una sesión cuando diste de alta el informe de error, recibirás una
actualización cada vez que se tomen acciones para solucionar el problema.
See Also:
Cómo dar partes de error eficaces
(http://www-mice.cs.ucl.ac.uk/multimedia/software/documentation/ReportingBugs.html)
Artículo que entra en cierto detalle de cómo crear un parte de error útil. Describe qué tipo de información es útil
y por qué es útil.
Libro de estilo de errores
(http://www.mozilla.org/quality/bug-writing-guidelines.html)

35
Información sobre la escritura de buenos informes de error. Hay cosas específicas del proyecto Mozilla, pero
describe buenas normas generales.

36 Apéndice A. Informes de error

S-ar putea să vă placă și