Vamos a crear un módulo de extensión denominado "spam"1.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()1.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
<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 componentes 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).