1. Extendiendo Python con C o C++

Es muy fácil agregar nuevos módulos incorporados a Python, si sabe cómo programar en C. Tales como módulos de extensión pueden hacer dos cosas que no se pueden hacer directamente en Python: pueden implementar nuevos tipos objetos incorporados, y pueden llamar a funciones de biblioteca C y llamadas de sistema.

Para admitir extensiones, la API de Python (interfaz de programadores de aplicaciones) define un conjunto de funciones, macros y variables que proporcionan acceso a la mayoría de los aspectos del sistema de tiempo de ejecución de Python. La API de Python se incorpora en un archivo fuente C incluyendo el encabezado "Python.h".

La compilación de un módulo de extensión depende de su uso previsto, así como de la configuración de su sistema; los detalles se dan en capítulos posteriores.

Nota

La interfaz de extensión C es específica de CPython, y los módulos de extensión no funcionan en otras implementaciones de Python. En muchos casos, es posible evitar escribir extensiones C y preservar la portabilidad a otras implementaciones. Por ejemplo, si su caso de uso es llamar a funciones de biblioteca C o llamadas de sistema, debería considerar usar el módulo ctypes o la biblioteca cffi en lugar de escribir código personalizado C. Estos módulos le permiten escribir código Python para interactuar con el código C y son más portátiles entre las implementaciones de Python que escribir y compilar un módulo de extensión C.

1.1. Un ejemplo simple

Creemos un módulo de extensión llamado spam (la comida favorita de los fanáticos de Monty Python …) y digamos que queremos crear una interfaz de Python para la función de biblioteca C system() 1 . Esta función toma una cadena de caracteres con terminación nula como argumento y retorna un entero. Queremos que esta función se pueda llamar desde Python de la siguiente manera:

>>> import spam
>>> status = spam.system("ls -l")

Comience creando un archivo spammodule.c. (Históricamente, si un módulo se llama spam, el archivo C que contiene su implementación se llama spammodule.c; si el nombre del módulo es muy largo, como spammify, el nombre del módulo puede sea solo spammify.c.)

Las dos primeras líneas de nuestro archivo pueden ser:

#define PY_SSIZE_T_CLEAN
#include <Python.h>

que extrae la API de Python (puede agregar un comentario que describa el propósito del módulo y un aviso de copyright si lo desea).

Nota

Dado que Python puede definir algunas definiciones de preprocesador que afectan los encabezados estándar en algunos sistemas, debe incluir Python.h antes de incluir encabezados estándar.

Se recomienda definir siempre PY_SSIZE_T_CLEAN antes de incluir Python.h. Consulte Extracción de parámetros en funciones de extensión para obtener una descripción de esta macro.

Todos los símbolos visibles para el usuario definidos por Python.h tienen un prefijo Py o PY, excepto los definidos en los archivos de encabezado estándar. Por conveniencia, y dado que el intérprete de Python los usa ampliamente, "Python.h" incluye algunos archivos de encabezado estándar: <stdio.h>, <string.h>, <errno.h>, y <stdlib.h>. Si el último archivo de encabezado no existe en su sistema, declara las funciones malloc(), free() y realloc() directamente.

Lo siguiente que agregamos a nuestro archivo de módulo es la función C que se llamará cuando se evalúe la expresión Python spam.system(string) (veremos en breve cómo termina siendo llamado):

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

    if (!PyArg_ParseTuple(args, "s", &command))
        return NULL;
    sts = system(command);
    return PyLong_FromLong(sts);
}

Hay una traducción directa de la lista de argumentos en Python (por ejemplo, la única expresión "ls -l") a los argumentos pasados a la función C. La función C siempre tiene dos argumentos, llamados convencionalmente self y args.

El argumento self apunta al objeto del módulo para funciones a nivel de módulo; para un método apuntaría a la instancia del objeto.

El argumento args será un puntero a un objeto de 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 — para hacer algo con ellos en nuestra función C tenemos que convertirlos a valores C. La función PyArg_ParseTuple() en la API de Python verifica los tipos de argumento y los convierte a valores C. Utiliza una cadena de plantilla para determinar los tipos requeridos de los argumentos, así como los tipos de las variables C en las que almacenar los valores convertidos. Más sobre esto más tarde.

PyArg_ParseTuple() retorna verdadero (distinto de cero) si todos los argumentos tienen el tipo correcto y sus componentes se han almacenado en las variables cuyas direcciones se pasan. Retorna falso (cero) si se pasó una lista de argumentos no válidos. En el último caso, también genera una excepción apropiada para que la función de llamada pueda retornar NULL inmediatamente (como vimos en el ejemplo).

1.2. Intermezzo: errores y excepciones

An important convention throughout the Python interpreter is the following: when a function fails, it should set an exception condition and return an error value (usually -1 or a NULL pointer). Exception information is stored in three members of the interpreter’s thread state. These are NULL if there is no exception. Otherwise they are the C equivalents of the members of the Python tuple returned by sys.exc_info(). These are the exception type, exception instance, and a traceback object. It is important to know about them to understand how errors are passed around.

La API de Python define una serie de funciones para establecer varios tipos de excepciones.

El más común es PyErr_SetString(). Sus argumentos son un objeto de excepción y una cadena C. El objeto de excepción suele ser un objeto predefinido como PyExc_ZeroDivisionError. La cadena C indica la causa del error y se convierte en un objeto de cadena Python y se almacena como el «valor asociado» de la excepción.

Otra función útil es PyErr_SetFromErrno(), que solo toma un argumento de excepción y construye el valor asociado mediante la inspección de la variable global errno. La función más general es PyErr_SetObject(), que toma dos argumentos de objeto, la excepción y su valor asociado. No necesita Py_INCREF() los objetos pasados a cualquiera de estas funciones.

Puede probar de forma no destructiva si se ha establecido una excepción con PyErr_Occurred(). Esto retorna el objeto de excepción actual o NULL si no se ha producido ninguna excepción. Normalmente no necesita llamar a PyErr_Occurred() para ver si se produjo un error en una llamada a la función, ya que debería poder distinguir el valor de retorno.

When a function f that calls another function g detects that the latter fails, f should itself return an error value (usually NULL or -1). It should not call one of the PyErr_* functions — one has already been called by g. f’s caller is then supposed to also return an error indication to its caller, again without calling PyErr_*, and so on — the most detailed cause of the error was already reported by the function that first detected it. Once the error reaches the Python interpreter’s main loop, this aborts the currently executing Python code and tries to find an exception handler specified by the Python programmer.

(There are situations where a module can actually give a more detailed error message by calling another PyErr_* function, and in such cases it is fine to do so. As a general rule, however, this is not necessary, and can cause information about the cause of the error to be lost: most operations can fail for a variety of reasons.)

Para ignorar una excepción establecida por una llamada de función que falló, la condición de excepción debe borrarse explícitamente llamando a PyErr_Clear(). La única vez que el código C debe llamar PyErr_Clear() es si no quiere pasar el error al intérprete pero quiere manejarlo completamente por sí mismo (posiblemente probando algo más o pretendiendo que nada salió mal) )

Cada llamada fallida a malloc() debe convertirse en una excepción — la persona que llama directamente de malloc() (o realloc()) debe llamar PyErr_NoMemory() y retorna un indicador de falla en sí mismo. Todas las funciones de creación de objetos (por ejemplo, PyLong_FromLong()) ya hacen esto, por lo que esta nota solo es relevante para aquellos que llaman malloc() directamente.

También tenga en cuenta que, con la importante excepción de PyArg_ParseTuple() y sus amigos, las funciones que retornan un estado entero generalmente retornan un valor positivo o cero para el éxito y -1 para el fracaso, como las llamadas al sistema Unix.

Finalmente, tenga cuidado de limpiar la basura (haciendo Py_XDECREF() o Py_DECREF() requiere objetos que ya ha creado) cuando retorna un indicador de error!

La elección de qué excepción lanzar es totalmente suya. Hay objetos C declarados previamente que corresponden a todas las excepciones de Python incorporadas, como PyExc_ZeroDivisionError, que puede usar directamente. Por supuesto, debe elegir sabiamente las excepciones — no use PyExc_TypeError para significar que no se puede abrir un archivo (probablemente debería ser PyExc_IOError). Si algo anda mal con la lista de argumentos, la función PyArg_ParseTuple() generalmente genera PyExc_TypeError. Si tiene un argumento cuyo valor debe estar en un rango particular o debe satisfacer otras condiciones, PyExc_ValueError es apropiado.

También puede definir una nueva excepción que sea exclusiva de su módulo. Para esto, generalmente declara una variable de objeto estático al comienzo de su archivo:

static PyObject *SpamError;

y lo inicializa en la función de inicialización de su módulo (PyInit_spam()) con un objeto de excepción:

PyMODINIT_FUNC
PyInit_spam(void)
{
    PyObject *m;

    m = PyModule_Create(&spammodule);
    if (m == NULL)
        return NULL;

    SpamError = PyErr_NewException("spam.error", NULL, NULL);
    Py_XINCREF(SpamError);
    if (PyModule_AddObject(m, "error", SpamError) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}

Tenga en cuenta que el nombre de Python para el objeto de excepción es spam.error. La función PyErr_NewException() puede crear una clase con la clase base siendo Exception (a menos que se pase otra clase en lugar de NULL), descrita en Excepciones incorporadas.

Tenga en cuenta también que la variable SpamError retiene una referencia a la clase de excepción recién creada; esto es intencional! Como la excepción podría eliminarse del módulo mediante un código externo, se necesita una referencia propia de la clase para garantizar que no se descarte, lo que hace que SpamError se convierta en un puntero colgante. Si se convierte en un puntero colgante, el código C que genera la excepción podría causar un volcado del núcleo u otros efectos secundarios no deseados.

Discutimos el uso de PyMODINIT_FUNC como un tipo de retorno de función más adelante en esta muestra.

La excepción spam.error se puede generar en su módulo de extensión mediante una llamada a PyErr_SetString() como se muestra a continuación:

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

    if (!PyArg_ParseTuple(args, "s", &command))
        return NULL;
    sts = system(command);
    if (sts < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}

1.3. De vuelta al ejemplo

Volviendo a nuestra función de ejemplo, ahora debería poder comprender esta declaración:

if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;

Retorna NULL (el indicador de error para las funciones que retornan punteros de objeto) si se detecta un error en la lista de argumentos, basándose en la excepción establecida por PyArg_ParseTuple(). De lo contrario, el valor de cadena del argumento se ha copiado en la variable local command. Esta es una asignación de puntero y no se supone que modifique la cadena a la que apunta (por lo tanto, en el Estándar C, la variable command debería declararse correctamente como const char * command).

La siguiente declaración es una llamada a la función Unix system(), pasándole la cadena que acabamos de obtener de PyArg_ParseTuple():

sts = system(command);

Nuestra función spam.system() debe retornar el valor de sts como un objeto Python. Esto se hace usando la función PyLong_FromLong().

return PyLong_FromLong(sts);

En este caso, retornará un objeto entero. (Sí, ¡incluso los enteros son objetos en el montículo (heap) en Python!)

If you have a C function that returns no useful argument (a function returning void), the corresponding Python function must return None. You need this idiom to do so (which is implemented by the Py_RETURN_NONE macro):

Py_INCREF(Py_None);
return Py_None;

Py_None es el nombre C para el objeto especial de Python None. Es un objeto genuino de Python en lugar de un puntero NULL, que significa «error» en la mayoría de los contextos, como hemos visto.

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

Prometí mostrar cómo spam_system() se llama desde los programas de Python. Primero, necesitamos enumerar su nombre y dirección en una «tabla de métodos»:

static PyMethodDef SpamMethods[] = {
    ...
    {"system",  spam_system, METH_VARARGS,
     "Execute a shell command."},
    ...
    {NULL, NULL, 0, NULL}        /* Sentinel */
};

Tenga en cuenta la tercera entrada (METH_VARARGS). Esta es una bandera que le dice al intérprete la convención de llamada que se utilizará para la función C. Normalmente debería ser siempre METH_VARARGS o METH_VARARGS | METH_KEYWORDS; un valor de 0 significa que se usa una variante obsoleta de PyArg_ParseTuple().

Cuando se usa solo METH_VARARGS, la función debe esperar que los parámetros a nivel de Python se pasen como una tupla aceptable para el análisis mediante PyArg_ParseTuple(); A continuación se proporciona más información sobre esta función.

El bit METH_KEYWORDS se puede establecer en el tercer campo si se deben pasar argumentos de palabras clave a la función. En este caso, la función C debería aceptar un tercer parámetro PyObject * que será un diccionario de palabras clave. Use PyArg_ParseTupleAndKeywords() para analizar los argumentos de dicha función.

La tabla de métodos debe ser referenciada en la estructura de definición del módulo:

static struct PyModuleDef spammodule = {
    PyModuleDef_HEAD_INIT,
    "spam",   /* name of module */
    spam_doc, /* module documentation, may be NULL */
    -1,       /* size of per-interpreter state of the module,
                 or -1 if the module keeps state in global variables. */
    SpamMethods
};

Esta estructura, a su vez, debe pasarse al intérprete en la función de inicialización del módulo. La función de inicialización debe llamarse PyInit_name(), donde name es el nombre del módulo y debe ser el único elemento no static definido en el archivo del módulo:

PyMODINIT_FUNC
PyInit_spam(void)
{
    return PyModule_Create(&spammodule);
}

Tenga en cuenta que PyMODINIT_FUNC declara la función como PyObject * tipo de retorno, declara cualquier declaración de vinculación especial requerida por la plataforma, y para C++ declara la función como extern "C".

Cuando el programa Python importa el módulo spam por primera vez, se llama PyInit_spam(). (Consulte a continuación los comentarios sobre la incorporación de Python). Llama a PyModule_Create(), que retorna un objeto de módulo e inserta objetos de función incorporados en el módulo recién creado en función de la tabla (un arreglo de estructuras PyMethodDef) encontradas en la definición del módulo. PyModule_Create() retorna un puntero al objeto del módulo que crea. Puede abortar con un error fatal para ciertos errores, o retornar NULL si el módulo no se pudo inicializar satisfactoriamente. La función init debe retornar el objeto del módulo a su llamador, para que luego se inserte en sys.modules.

Al incrustar Python, la función PyInit_spam() no se llama automáticamente a menos que haya una entrada en la tabla PyImport_Inittab. Para agregar el módulo a la tabla de inicialización, use PyImport_AppendInittab(), seguido opcionalmente por una importación del módulo:

int
main(int argc, char *argv[])
{
    wchar_t *program = Py_DecodeLocale(argv[0], NULL);
    if (program == NULL) {
        fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
        exit(1);
    }

    /* Add a built-in module, before Py_Initialize */
    if (PyImport_AppendInittab("spam", PyInit_spam) == -1) {
        fprintf(stderr, "Error: could not extend in-built modules table\n");
        exit(1);
    }

    /* Pass argv[0] to the Python interpreter */
    Py_SetProgramName(program);

    /* Initialize the Python interpreter.  Required.
       If this step fails, it will be a fatal error. */
    Py_Initialize();

    /* Optionally import the module; alternatively,
       import can be deferred until the embedded script
       imports it. */
    PyObject *pmodule = PyImport_ImportModule("spam");
    if (!pmodule) {
        PyErr_Print();
        fprintf(stderr, "Error: could not import module 'spam'\n");
    }

    ...

    PyMem_RawFree(program);
    return 0;
}

Nota

Eliminar entradas de sys.modules o importar módulos compilados en múltiples intérpretes dentro de un proceso (o seguir un fork() sin una intervención exec()) puede crear problemas para algunas extensiones de módulos. Los autores de módulos de extensiones deben tener precaución al inicializar estructuras de datos internas.

Se incluye un módulo de ejemplo más sustancial en la distribución fuente de Python como Modules/xxmodule.c. Este archivo puede usarse como plantilla o simplemente leerse como ejemplo.

Nota

A diferencia de nuestro ejemplo de spam, xxmodule usa inicialización de múltiples fases (nuevo en Python 3.5), donde se retorna una estructura PyModuleDef de PyInit_spam, y la creación del módulo se deja al maquinaria de importación. Para obtener detalles sobre la inicialización múltiples fases, consulte PEP 489.

1.5. Compilación y Enlazamiento

Hay dos cosas más que hacer antes de que pueda usar su nueva extensión: compilarla y vincularla con el sistema Python. Si usa carga dinámica, los detalles pueden depender del estilo de carga dinámica que usa su sistema; Para obtener más información al respecto, consulte los capítulos sobre la creación de módulos de extensión (capítulo Construyendo Extensiones C y C++) e información adicional que se refiere solo a la construcción en Windows (capítulo Creación de extensiones C y C++ en Windows).

Si no puede utilizar la carga dinámica, o si desea que su módulo sea una parte permanente del intérprete de Python, tendrá que cambiar la configuración (setup) y reconstruir el intérprete. Afortunadamente, esto es muy simple en Unix: simplemente coloque su archivo (spammodule.c por ejemplo) en el directorio Modules/ ` de una distribución fuente desempaquetada, agregue una línea al archivo :file:`Modules/Setup.local que describe su archivo:

spam spammodule.o

y reconstruya el intérprete ejecutando make en el directorio de nivel superior. También puede ejecutar make en el subdirectorio Modules/, pero primero debe reconstruir Makefile ejecutando “make Makefile”. (Esto es necesario cada vez que cambia el archivo Configuración).

Si su módulo requiere bibliotecas adicionales para vincular, también se pueden enumerar en la línea del archivo de configuración, por ejemplo:

spam spammodule.o -lX11

1.6. Llamando funciones Python desde C

Hasta ahora nos hemos concentrado en hacer que las funciones de C puedan llamarse desde Python. Lo contrario también es útil: llamar a las funciones de Python desde C. Este es especialmente el caso de las bibliotecas que admiten las llamadas funciones de «retrollamada». Si una interfaz C utiliza retrollamadas, el Python equivalente a menudo necesita proporcionar un mecanismo de retrollamada al programador de Python; la implementación requerirá llamar a las funciones de retrollamada de Python desde una retrollamada en C. Otros usos también son imaginables.

Afortunadamente, el intérprete de Python se llama fácilmente de forma recursiva, y hay una interfaz estándar para llamar a una función de Python. (No me detendré en cómo llamar al analizador Python con una cadena particular como entrada — si está interesado, eche un vistazo a la implementación de la opción de línea de comando -c en Modules/main.c del código fuente de Python.)

Llamar a una función de Python es fácil. Primero, el programa Python debe de alguna manera pasar el objeto de función Python. Debe proporcionar una función (o alguna otra interfaz) para hacer esto. Cuando se llama a esta función, guarde un puntero en el objeto de la función Python (tenga cuidado de usar Py_INCREF()) En una variable global — o donde mejor le parezca. 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(PyObject *dummy, PyObject *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 registrarse con el intérprete utilizando el indicador METH_VARARGS; esto se describe en la sección La tabla de métodos del módulo y la función de inicialización. La función PyArg_ParseTuple() y sus argumentos están documentados en la sección Extracción de parámetros en funciones de extensión.

Las macros Py_XINCREF() y Py_XDECREF() incrementan/disminuyen el recuento de referencia de un objeto y son seguros en presencia de punteros NULL (pero tenga en cuenta que temp no lo hará ser NULL en este contexto). Más información sobre ellos en la sección Conteo de Referencias.

Más tarde, cuando es hora de llamar a la función, llama a la función C PyObject_CallObject(). Esta función tiene dos argumentos, ambos punteros a objetos arbitrarios de Python: la función Python y la lista de argumentos. La lista de argumentos siempre debe ser un objeto de tupla, cuya longitud es el número de argumentos. Para llamar a la función Python sin argumentos, pase NULL o una tupla vacía; para llamarlo con un argumento, pasa una tupla singleton. Py_BuildValue() retorna una tupla cuando su cadena de formato consta de cero o más códigos de formato entre paréntesis. Por ejemplo:

int arg;
PyObject *arglist;
PyObject *result;
...
arg = 123;
...
/* Time to call the callback */
arglist = Py_BuildValue("(i)", arg);
result = PyObject_CallObject(my_callback, arglist);
Py_DECREF(arglist);

PyObject_CallObject() retorna un puntero de objeto Python: este es el valor de retorno de la función Python. PyObject_CallObject() es «recuento-referencia-neutral» con respecto a sus argumentos. En el ejemplo, se creó una nueva tupla para servir como lista de argumentos, a la cual se le llama Py_DECREF() inmediatamente después de la llamada PyObject_CallObject().

El valor de retorno de PyObject_CallObject() es «nuevo»: o bien es un objeto nuevo o es un objeto existente cuyo recuento de referencias se ha incrementado. Por lo tanto, a menos que desee guardarlo en una variable global, debería de alguna manera Py_DECREF() el resultado, incluso (¡especialmente!) Si no está interesado en su valor.

Sin embargo, antes de hacer esto, es importante verificar que el valor de retorno no sea NULL. Si es así, la función de Python terminó generando una excepción. Si el código C que llamó PyObject_CallObject() se llama desde Python, ahora debería retornar una indicación de error a su llamador de Python, para que el intérprete pueda imprimir un seguimiento de la pila, o el código de Python que llama puede manejar la excepción. Si esto no es posible o deseable, la excepción se debe eliminar llamando a PyErr_Clear(). Por ejemplo:

if (result == NULL)
    return NULL; /* Pass error back */
...use result...
Py_DECREF(result);

Dependiendo de la interfaz deseada para la función de retrollamada de Python, es posible que también deba proporcionar una lista de argumentos para PyObject_CallObject(). En algunos casos, el programa Python también proporciona la lista de argumentos, a través de la misma interfaz que especificó la función de retrollamada. Luego se puede guardar y usar de la misma manera que el objeto de función. En otros casos, puede que tenga que construir una nueva tupla para pasarla como lista de argumentos. La forma más sencilla de hacer esto es llamar a Py_BuildValue(). Por ejemplo, si desea pasar un código de evento integral, puede usar el siguiente código:

PyObject *arglist;
...
arglist = Py_BuildValue("(l)", eventcode);
result = PyObject_CallObject(my_callback, arglist);
Py_DECREF(arglist);
if (result == NULL)
    return NULL; /* Pass error back */
/* Here maybe use the result */
Py_DECREF(result);

¡Observe la ubicación de Py_DECREF(arglist) inmediatamente después de la llamada, antes de la verificación de errores! También tenga en cuenta que, estrictamente hablando, este código no está completo: Py_BuildValue() puede quedarse sin memoria, y esto debe verificarse.

También puede llamar a una función con argumentos de palabras clave utilizando PyObject_Call(), que admite argumentos y argumentos de palabras clave. Como en el ejemplo anterior, usamos Py_BuildValue() para construir el diccionario.

PyObject *dict;
...
dict = Py_BuildValue("{s:i}", "name", val);
result = PyObject_Call(my_callback, NULL, dict);
Py_DECREF(dict);
if (result == NULL)
    return NULL; /* Pass error back */
/* Here maybe use the result */
Py_DECREF(result);

1.7. Extracción de parámetros en funciones de extensión

La función PyArg_ParseTuple() se declara de la siguiente manera:

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

El argumento arg debe ser un objeto de 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 en Analizando argumentos y construyendo valores en el Manual de referencia de la API de Python/C. Los argumentos restantes deben ser direcciones de variables cuyo tipo está determinado por la cadena de formato.

Tenga en cuenta que si bien PyArg_ParseTuple() verifica que los argumentos de Python tengan los tipos requeridos, no puede verificar la validez de las direcciones de las variables C pasadas a la llamada: si comete errores allí, su código probablemente se bloqueará o al menos sobrescribir bits aleatorios en la memoria. ¡Así que ten cuidado!

Tenga en cuenta que las referencias de objetos de Python que se proporcionan a quien llama son referencias prestadas (borrowed); ¡no disminuya su recuento de referencias!

Algunas llamadas de ejemplo:

#define PY_SSIZE_T_CLEAN  /* Make "s#" use Py_ssize_t rather than int. */
#include <Python.h>
int ok;
int i, j;
long k, l;
const char *s;
Py_ssize_t size;

ok = PyArg_ParseTuple(args, ""); /* No arguments */
    /* Python call: f() */
ok = PyArg_ParseTuple(args, "s", &s); /* A string */
    /* Possible Python call: f('whoops!') */
ok = PyArg_ParseTuple(args, "lls", &k, &l, &s); /* Two longs and a string */
    /* Possible Python call: f(1, 2, 'three') */
ok = PyArg_ParseTuple(args, "(ii)s#", &i, &j, &s, &size);
    /* A pair of ints and a string, whose size is also returned */
    /* Possible Python call: f((1, 2), 'three') */
{
    const char *file;
    const char *mode = "r";
    int bufsize = 0;
    ok = PyArg_ParseTuple(args, "s|si", &file, &mode, &bufsize);
    /* A string, and optionally another string and an integer */
    /* Possible Python calls:
       f('spam')
       f('spam', 'w')
       f('spam', 'wb', 100000) */
}
{
    int left, top, right, bottom, h, v;
    ok = PyArg_ParseTuple(args, "((ii)(ii))(ii)",
             &left, &top, &right, &bottom, &h, &v);
    /* A rectangle and a point */
    /* Possible Python call:
       f(((0, 0), (400, 300)), (10, 10)) */
}
{
    Py_complex c;
    ok = PyArg_ParseTuple(args, "D:myfunction", &c);
    /* a complex, also providing a function name for errors */
    /* Possible Python call: myfunction(1+2j) */
}

1.8. Parámetros de palabras clave para funciones de extensión

La función PyArg_ParseTupleAndKeywords() se declara de la siguiente manera:

int PyArg_ParseTupleAndKeywords(PyObject *arg, PyObject *kwdict,
                                const 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 el diccionario de palabras clave recibidas como tercer parámetro del tiempo 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 corresponden con la información de tipo de format de izquierda a derecha. En caso de éxito, PyArg_ParseTupleAndKeywords() retorna verdadero; de lo contrario, retorna falso y genera una excepción apropiada.

Nota

¡Las tuplas anidadas no se pueden analizar al usar argumentos de palabras clave! Los parámetros de palabras clave pasados que no están presentes en la kwlist provocarán que se genere TypeError.

Aquí hay un módulo de ejemplo que usa palabras clave, basado en un ejemplo de Geoff Philbrick (philbrick@hks.com):

#define PY_SSIZE_T_CLEAN  /* Make "s#" use Py_ssize_t rather than int. */
#include <Python.h>

static PyObject *
keywdarg_parrot(PyObject *self, PyObject *args, PyObject *keywds)
{
    int voltage;
    const char *state = "a stiff";
    const char *action = "voom";
    const char *type = "Norwegian Blue";

    static char *kwlist[] = {"voltage", "state", "action", "type", NULL};

    if (!PyArg_ParseTupleAndKeywords(args, keywds, "i|sss", kwlist,
                                     &voltage, &state, &action, &type))
        return NULL;

    printf("-- This parrot wouldn't %s if you put %i Volts through it.\n",
           action, voltage);
    printf("-- Lovely plumage, the %s -- It's %s!\n", type, state);

    Py_RETURN_NONE;
}

static PyMethodDef keywdarg_methods[] = {
    /* The cast of the function is necessary since PyCFunction values
     * only take two PyObject* parameters, and keywdarg_parrot() takes
     * three.
     */
    {"parrot", (PyCFunction)(void(*)(void))keywdarg_parrot, METH_VARARGS | METH_KEYWORDS,
     "Print a lovely skit to standard output."},
    {NULL, NULL, 0, NULL}   /* sentinel */
};

static struct PyModuleDef keywdargmodule = {
    PyModuleDef_HEAD_INIT,
    "keywdarg",
    NULL,
    -1,
    keywdarg_methods
};

PyMODINIT_FUNC
PyInit_keywdarg(void)
{
    return PyModule_Create(&keywdargmodule);
}

1.9. Construyendo Valores Arbitrarios

Esta función es la contraparte de PyArg_ParseTuple(). Se declara de la siguiente manera:

PyObject *Py_BuildValue(const char *format, ...);

Reconoce un conjunto de unidades de formato similares a las reconocidas por PyArg_ParseTuple(), pero los argumentos (que son de entrada a la función, no de salida) no deben ser punteros, solo valores. Retorna un nuevo objeto Python, adecuado para regresar de una función C llamada desde Python.

Una diferencia con PyArg_ParseTuple(): mientras que este último requiere que su primer argumento sea una tupla (ya que las listas de argumentos de Python siempre se representan como tuplas internamente), Py_BuildValue() no siempre construye una tupla . Construye una tupla solo si su cadena de formato contiene dos o más unidades de formato. Si la cadena de formato está vacía, retorna None; si contiene exactamente una unidad de formato, retorna el objeto que describa esa unidad de formato. Para forzarlo a retornar una tupla de tamaño 0 o uno, agregar paréntesis a la cadena de formato.

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", "hello")              'hello'
Py_BuildValue("y", "hello")              b'hello'
Py_BuildValue("ss", "hello", "world")    ('hello', 'world')
Py_BuildValue("s#", "hello", 4)          'hell'
Py_BuildValue("y#", "hello", 4)          b'hell'
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. Conteo de Referencias

En lenguajes como C o C++, el programador es responsable de la asignación dinámica y la desasignación de memoria en el montón. En C, esto se hace usando las funciones malloc() y free(). En C++, los operadores new y delete se usan esencialmente con el mismo significado y restringiremos la siguiente discusión al caso C.

Cada bloque de memoria asignado con malloc() eventualmente debería ser retorna al grupo de memoria disponible exactamente por una llamada a free(). Es importante llamar a free() en el momento adecuado. Si se olvida la dirección de un bloque pero free() no se solicita, la memoria que ocupa no se puede reutilizar hasta que finalice el programa. Esto se llama fuga de memoria. Por otro lado, si un programa llama free() para un bloque y luego continúa usando el bloque, crea un conflicto con la reutilización del bloque a través de otra llamada a malloc(). Esto se llama usando memoria liberada. Tiene las mismas malas consecuencias que hacer referencia a datos no inicializados: volcados de núcleos, resultados incorrectos, bloqueos misteriosos.

Las causas comunes de pérdidas de memoria son rutas inusuales a través del código. Por ejemplo, una función puede asignar un bloque de memoria, hacer algunos cálculos y luego liberar el bloque nuevamente. Ahora, un cambio en los requisitos de la función puede agregar una prueba al cálculo que detecta una condición de error y puede regresar prematuramente de la función. Es fácil olvidar liberar el bloque de memoria asignado al tomar esta salida prematura, especialmente cuando se agrega más tarde al código. Tales filtraciones, una vez introducidas, a menudo pasan desapercibidas durante mucho tiempo: la salida del error se toma solo en una pequeña fracción de todas las llamadas, y la mayoría de las máquinas modernas tienen mucha memoria virtual, por lo que la filtración solo se hace evidente en un proceso de larga ejecución que usa la función de fugas con frecuencia. Por lo tanto, es importante evitar que se produzcan fugas mediante una convención o estrategia de codificación que minimice este tipo de errores.

Dado que Python hace un uso intensivo de malloc() y free(), necesita una estrategia para evitar pérdidas de memoria, así como el uso de memoria liberada. El método elegido se llama recuento de referencias. El principio es simple: cada objeto contiene un contador, que se incrementa cuando se almacena una referencia al objeto en algún lugar, y que se reduce cuando se elimina una referencia al mismo. Cuando el contador llega a cero, la última referencia al objeto se ha eliminado y el objeto se libera.

Una estrategia alternativa se llama recolección automática de basura. (A veces, el recuento de referencias también se conoce como una estrategia de recolección de basura, de ahí mi uso de «automático» para distinguir los dos). La gran ventaja de la recolección automática de basura es que el usuario no necesita llamar a free() explícitamente. (Otra ventaja afirmada es una mejora en la velocidad o el uso de la memoria; sin embargo, esto no es un hecho difícil). La desventaja es que para C, no hay un recolector de basura automático verdaderamente portátil, mientras que el conteo de referencias se puede implementar de forma portátil (siempre que las funciones malloc() y free() están disponibles — que garantiza el estándar C). Tal vez algún día un recolector de basura automático lo suficientemente portátil estará disponible para C. Hasta entonces, tendremos que vivir con recuentos de referencia.

Si bien Python utiliza la implementación tradicional de conteo de referencias, también ofrece un detector de ciclos que funciona para detectar ciclos de referencia. Esto permite que las aplicaciones no se preocupen por crear referencias circulares directas o indirectas; Estas son las debilidades de la recolección de basura implementada utilizando solo el conteo de referencias. Los ciclos de referencia consisten en objetos que contienen referencias (posiblemente indirectas) a sí mismos, de modo que cada objeto en el ciclo tiene un recuento de referencias que no es cero. Las implementaciones típicas de recuento de referencias no pueden recuperar la memoria que pertenece a algún objeto en un ciclo de referencia, o referenciada a partir de los objetos en el ciclo, a pesar de que no hay más referencias al ciclo en sí.

El detector de ciclos es capaz de detectar ciclos basura y puede recuperarlos. El módulo gc expone una forma de ejecutar el detector (la función collect()), así como interfaces de configuración y la posibilidad de desactivar el detector en tiempo de ejecución.

1.10.1. Conteo de Referencias en Python

Hay dos macros, Py_INCREF(x) y Py_DECREF(x), que manejan el incremento y la disminución del recuento de referencias. Py_DECREF() también libera el objeto cuando el recuento llega a cero. Por flexibilidad, no llama a free() directamente — más bien, realiza una llamada a través de un puntero de función en el objeto type object. Para este propósito (y otros), cada objeto también contiene un puntero a su objeto de tipo.

La gran pregunta ahora permanece: ¿cuándo usar Py_INCREF(x) y Py_DECREF(x)? Primero introduzcamos algunos términos. Nadie «posee» un objeto; sin embargo, puede poseer una referencia a un objeto. El recuento de referencias de un objeto ahora se define como el número de referencias que posee. El propietario de una referencia es responsable de llamar a Py_DECREF() cuando la referencia ya no es necesaria. La propiedad de una referencia puede ser transferida. Hay tres formas de deshacerse de una referencia de propiedad: pasarla, almacenarla o llamar a Py_DECREF(). Olvidar deshacerse de una referencia de propiedad crea una pérdida de memoria.

También es posible tomar prestada 2 una referencia a un objeto. El prestatario de una referencia no debe llamar a Py_DECREF(). El prestatario no debe retener el objeto por más tiempo que el propietario del cual fue prestado. El uso de una referencia prestada después de que el propietario la haya eliminado corre el riesgo de usar memoria liberada y debe evitarse por completo 3.

La ventaja de pedir prestado sobre tener una referencia es que no necesita ocuparse de disponer de la referencia en todas las rutas posibles a través del código — en otras palabras, con una referencia prestada no corre el riesgo de fugas cuando se toma una salida prematura. La desventaja de pedir prestado sobre la posesión es que hay algunas situaciones sutiles en las que, en un código aparentemente correcto, una referencia prestada se puede usar después de que el propietario del que se tomó prestado la haya eliminado.

Una referencia prestada se puede cambiar en una referencia de propiedad llamando a Py_INCREF(). Esto no afecta el estado del propietario del cual se tomó prestada la referencia: crea una nueva referencia de propiedad y otorga responsabilidades completas al propietario (el nuevo propietario debe disponer de la referencia correctamente, así como el propietario anterior).

1.10.2. Reglas de Propiedad

Cuando una referencia de objeto se pasa dentro o fuera de una función, es parte de la especificación de la interfaz de la función si la propiedad se transfiere con la referencia o no.

La mayoría de las funciones que retornan una referencia a un objeto pasan de propiedad con la referencia. En particular, todas las funciones cuya función es crear un nuevo objeto, como PyLong_FromLong() y Py_BuildValue(), pasan la propiedad al receptor. Incluso si el objeto no es realmente nuevo, aún recibirá la propiedad de una nueva referencia a ese objeto. Por ejemplo, PyLong_FromLong() mantiene un caché de valores populares y puede retornar una referencia a un elemento en caché.

Muchas funciones que extraen objetos de otros objetos también transfieren la propiedad con la referencia, por ejemplo PyObject_GetAttrString(). Sin embargo, la imagen es menos clara aquí, ya que algunas rutinas comunes son excepciones: PyTuple_GetItem(), PyList_GetItem(), PyDict_GetItem(), y PyDict_GetItemString() todas las referencias retornadas que tomaste prestadas de la tupla, lista o diccionario.

La función PyImport_AddModule() también retorna una referencia prestada, aunque en realidad puede crear el objeto que retorna: esto es posible porque una referencia de propiedad del objeto se almacena en sys.modules.

Cuando pasa una referencia de objeto a otra función, en general, la función toma prestada la referencia de usted — si necesita almacenarla, usará Py_INCREF() para convertirse en un propietario independiente. Hay exactamente dos excepciones importantes a esta regla: PyTuple_SetItem() y PyList_SetItem(). Estas funciones se hacen cargo de la propiedad del artículo que se les pasa, ¡incluso si fallan! (Tenga en cuenta que PyDict_SetItem() y sus amigos no se hacen cargo de la propiedad — son «normales»)

Cuando se llama a una función C desde Python, toma de la persona que llama referencias a sus argumentos. Quien llama posee una referencia al objeto, por lo que la vida útil de la referencia prestada está garantizada hasta que la función regrese. Solo cuando dicha referencia prestada debe almacenarse o transmitirse, debe convertirse en una referencia propia llamando a Py_INCREF().

La referencia de objeto retornada desde una función C que se llama desde Python debe ser una referencia de propiedad: la propiedad se transfiere de la función a su llamador.

1.10.3. Hielo delgado

Hay algunas situaciones en las que el uso aparentemente inofensivo de una referencia prestada puede generar problemas. Todo esto tiene que ver con invocaciones implícitas del intérprete, lo que puede hacer que el propietario de una referencia se deshaga de él.

El primer y más importante caso que debe conocer es el uso de Py_DECREF() en un objeto no relacionado mientras toma prestada una referencia a un elemento de la lista. Por ejemplo:

void
bug(PyObject *list)
{
    PyObject *item = PyList_GetItem(list, 0);

    PyList_SetItem(list, 1, PyLong_FromLong(0L));
    PyObject_Print(item, stdout, 0); /* BUG! */
}

Esta función primero toma prestada una referencia a list[0], luego reemplaza list[1] con el valor 0, y finalmente imprime la referencia prestada. Parece inofensivo, ¿verdad? ¡Pero no lo es!

Sigamos el flujo de control en PyList_SetItem(). La lista posee referencias a todos sus elementos, por lo que cuando se reemplaza el elemento 1, debe deshacerse del elemento original 1. Ahora supongamos que el elemento original 1 era una instancia de una clase definida por el usuario, y supongamos además que la clase definió un método __del__(). Si esta instancia de clase tiene un recuento de referencia de 1, al eliminarla llamará a su método __del__().

Como está escrito en Python, el método __del__() puede ejecutar código arbitrario de Python. ¿Podría hacer algo para invalidar la referencia a item en error()? ¡Tenlo por seguro! Suponiendo que la lista pasado a bug() es accesible para el método __del__(), podría ejecutar una declaración en el sentido de del list[0], y suponiendo que este fuera el última referencia a ese objeto, liberaría la memoria asociada con él, invalidando así el elemento.

La solución, una vez que conoce el origen del problema, es fácil: incremente temporalmente el recuento de referencia. La versión correcta de la función dice:

void
no_bug(PyObject *list)
{
    PyObject *item = PyList_GetItem(list, 0);

    Py_INCREF(item);
    PyList_SetItem(list, 1, PyLong_FromLong(0L));
    PyObject_Print(item, stdout, 0);
    Py_DECREF(item);
}

Esta es una historia real. Una versión anterior de Python contenía variantes de este error y alguien pasó una cantidad considerable de tiempo en un depurador C para descubrir por qué sus métodos __del__() fallaban …

El segundo caso de problemas con una referencia prestada es una variante que involucra hilos. Normalmente, varios hilos en el intérprete de Python no pueden interponerse entre sí, porque hay un bloqueo global que protege todo el espacio de objetos de Python. Sin embargo, es posible liberar temporalmente este bloqueo usando la macro Py_BEGIN_ALLOW_THREADS, y volver a adquirirlo usando Py_END_ALLOW_THREADS. Esto es común al bloquear las llamadas de E/S, para permitir que otros subprocesos usen el procesador mientras esperan que se complete la E/S. Obviamente, la siguiente función tiene el mismo problema que la anterior:

void
bug(PyObject *list)
{
    PyObject *item = PyList_GetItem(list, 0);
    Py_BEGIN_ALLOW_THREADS
    ...some blocking I/O call...
    Py_END_ALLOW_THREADS
    PyObject_Print(item, stdout, 0); /* BUG! */
}

1.10.4. Punteros NULL

En general, las funciones que toman referencias de objetos como argumentos no esperan que les pase los punteros NULL, y volcará el núcleo (o causará volcados de núcleo posteriores) si lo hace. Las funciones que retornan referencias a objetos generalmente retornan NULL solo para indicar que ocurrió una excepción. La razón para no probar los argumentos NULL es que las funciones a menudo pasan los objetos que reciben a otra función — si cada función probara NULL, habría muchas pruebas redundantes y el código correría más lentamente.

Es mejor probar NULL solo en «source:» cuando se recibe un puntero que puede ser NULL, por ejemplo, de malloc() o de una función que puede plantear una excepción.

Las macros Py_INCREF() y Py_DECREF() no comprueban los punteros NULL — sin embargo, sus variantes Py_XINCREF() y Py_XDECREF() lo hacen.

Las macros para verificar un tipo de objeto en particular (Pytype_Check()) no verifican los punteros NULL — nuevamente, hay mucho código que llama a varios de estos en una fila para probar un objeto contra varios tipos esperados diferentes, y esto generaría pruebas redundantes. No hay variantes con comprobación NULL.

El mecanismo de llamada a la función C garantiza que la lista de argumentos pasada a las funciones C (args en los ejemplos) nunca sea NULL — de hecho, garantiza que siempre sea una tupla 4.

Es un error grave dejar que un puntero NULL «escape» al usuario de Python.

1.11. Escribiendo Extensiones en C++

Es posible escribir módulos de extensión en C++. Se aplican algunas restricciones. Si el compilador de C compila y vincula el programa principal (el intérprete de Python), no se pueden usar objetos globales o estáticos con constructores. Esto no es un problema si el programa principal está vinculado por el compilador de C++. Las funciones que serán llamadas por el intérprete de Python (en particular, las funciones de inicialización del módulo) deben declararse usando extern "C". No es necesario encerrar los archivos de encabezado de Python en extern "C" {...} — ya usan este formulario si el símbolo __cplusplus está definido (todos los compiladores recientes de C++ definen este símbolo) .

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

Muchos módulos de extensión solo proporcionan nuevas funciones y tipos para ser utilizados desde Python, pero a veces el código en un módulo de extensión puede ser útil para otros módulos de extensión. Por ejemplo, un módulo de extensión podría implementar un tipo de «colección» que funciona como listas sin orden. Al igual que el tipo de lista Python estándar tiene una API C que permite a los módulos de extensión crear y manipular listas, este nuevo tipo de colección debe tener un conjunto de funciones C para la manipulación directa desde otros módulos de extensión.

A primera vista, esto parece fácil: simplemente escriba las funciones (sin declararlas static, por supuesto), proporcione un archivo de encabezado apropiado y documente la API de C. Y, de hecho, esto funcionaría si todos los módulos de extensión siempre estuvieran vinculados estáticamente con el intérprete de Python. Sin embargo, cuando los módulos se usan como bibliotecas compartidas, los símbolos definidos en un módulo pueden no ser visibles para otro módulo. Los detalles de visibilidad dependen del sistema operativo; algunos sistemas usan un espacio de nombres global para el intérprete de Python y todos los módulos de extensión (Windows, por ejemplo), mientras que otros requieren una lista explícita de símbolos importados en el momento del enlace del módulo (AIX es un ejemplo) u ofrecen una variedad de estrategias diferentes (la mayoría Unices). E incluso si los símbolos son visibles a nivel mundial, ¡el módulo cuyas funciones uno desea llamar podría no haberse cargado todavía!

Por lo tanto, la portabilidad requiere no hacer suposiciones sobre la visibilidad del símbolo. Esto significa que todos los símbolos en los módulos de extensión deben declararse static, excepto la función de inicialización del módulo, para evitar conflictos de nombres con otros módulos de extensión (como se discutió en la sección La tabla de métodos del módulo y la función de inicialización). Y significa que los símbolos que deberían ser accesibles desde otros módulos de extensión deben exportarse de una manera diferente.

Python provides a special mechanism to pass C-level information (pointers) from one extension module to another one: Capsules. A Capsule is a Python data type which stores a pointer (void*). Capsules can only be created and accessed via their C API, but they can be passed around like any other Python object. In particular, they can be assigned to a name in an extension module’s namespace. Other extension modules can then import this module, retrieve the value of this name, and then retrieve the pointer from the Capsule.

Hay muchas formas en que las Cápsulas se pueden usar para exportar la API de C de un módulo de extensión. Cada función podría tener su propia cápsula, o todos los punteros de API C podrían almacenarse en una matriz cuya dirección se publica en una cápsula. Y las diversas tareas de almacenamiento y recuperación de los punteros se pueden distribuir de diferentes maneras entre el módulo que proporciona el código y los módulos del cliente.

Whichever method you choose, it’s important to name your Capsules properly. The function PyCapsule_New() takes a name parameter (const char*); you’re permitted to pass in a NULL name, but we strongly encourage you to specify a name. Properly named Capsules provide a degree of runtime type-safety; there is no feasible way to tell one unnamed Capsule from another.

En particular, las cápsulas utilizadas para exponer las API de C deben recibir un nombre siguiendo esta convención:

modulename.attributename

La función de conveniencia PyCapsule_Import() facilita la carga de una API C proporcionada a través de una cápsula, pero solo si el nombre de la cápsula coincide con esta convención. Este comportamiento brinda a los usuarios de C API un alto grado de certeza de que la Cápsula que cargan contiene la API de C correcta.

The following example demonstrates an approach that puts most of the burden on the writer of the exporting module, which is appropriate for commonly used library modules. It stores all C API pointers (just one in the example!) in an array of void pointers which becomes the value of a Capsule. The header file corresponding to the module provides a macro that takes care of importing the module and retrieving its C API pointers; client modules only have to call this macro before accessing the C API.

El módulo de exportación es una modificación del módulo spam de la sección Un ejemplo simple. La función spam.system() no llama a la función de la biblioteca C system() directamente, sino una función PySpam_System(), que por supuesto haría algo más complicado en realidad (como agregar «spam» a cada comando). Esta función PySpam_System() también se exporta a otros módulos de extensión.

La función PySpam_System() es una función C simple, declarada static como todo lo demás:

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

La función spam_system() se modifica de manera trivial:

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

    if (!PyArg_ParseTuple(args, "s", &command))
        return NULL;
    sts = PySpam_System(command);
    return PyLong_FromLong(sts);
}

Al comienzo del módulo, justo después de la línea:

#include <Python.h>

se deben agregar dos líneas más:

#define SPAM_MODULE
#include "spammodule.h"

El #define se usa para decirle al archivo de encabezado que se está incluyendo en el módulo de exportación, no en un módulo de cliente. Finalmente, la función de inicialización del módulo debe encargarse de inicializar la matriz de punteros de API C:

PyMODINIT_FUNC
PyInit_spam(void)
{
    PyObject *m;
    static void *PySpam_API[PySpam_API_pointers];
    PyObject *c_api_object;

    m = PyModule_Create(&spammodule);
    if (m == NULL)
        return NULL;

    /* Initialize the C API pointer array */
    PySpam_API[PySpam_System_NUM] = (void *)PySpam_System;

    /* Create a Capsule containing the API pointer array's address */
    c_api_object = PyCapsule_New((void *)PySpam_API, "spam._C_API", NULL);

    if (PyModule_AddObject(m, "_C_API", c_api_object) < 0) {
        Py_XDECREF(c_api_object);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}

Tenga en cuenta que PySpam_API se declara static; de lo contrario, la matriz de punteros desaparecería cuando PyInit_spam() finalice!

La mayor parte del trabajo está en el archivo de encabezado spammodule.h, que se ve así:

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

/* Header file for spammodule */

/* C API functions */
#define PySpam_System_NUM 0
#define PySpam_System_RETURN int
#define PySpam_System_PROTO (const char *command)

/* Total number of C API pointers */
#define PySpam_API_pointers 1


#ifdef SPAM_MODULE
/* This section is used when compiling spammodule.c */

static PySpam_System_RETURN PySpam_System PySpam_System_PROTO;

#else
/* This section is used in modules that use spammodule's API */

static void **PySpam_API;

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

/* Return -1 on error, 0 on success.
 * PyCapsule_Import will set an exception if there's an error.
 */
static int
import_spam(void)
{
    PySpam_API = (void **)PyCapsule_Import("spam._C_API", 0);
    return (PySpam_API != NULL) ? 0 : -1;
}

#endif

#ifdef __cplusplus
}
#endif

#endif /* !defined(Py_SPAMMODULE_H) */

Todo lo que un módulo cliente debe hacer para tener acceso a la función PySpam_System() es llamar a la función (o más bien macro) import_spam() en su función de inicialización:

PyMODINIT_FUNC
PyInit_client(void)
{
    PyObject *m;

    m = PyModule_Create(&clientmodule);
    if (m == NULL)
        return NULL;
    if (import_spam() < 0)
        return NULL;
    /* additional initialization can happen here */
    return m;
}

La principal desventaja de este enfoque es que el archivo spammodule.h es bastante complicado. Sin embargo, la estructura básica es la misma para cada función que se exporta, por lo que solo se debe aprender una vez.

Finalmente, debe mencionarse que las cápsulas ofrecen una funcionalidad adicional, que es especialmente útil para la asignación de memoria y la desasignación del puntero almacenado en una cápsula. Los detalles se describen en el Manual de referencia de Python/C API en la sección Cápsulas y en la implementación de Capsules (archivos Include/pycapsule.h y Objects/pycapsule.c en la distribución del código fuente de Python).

Notas al pie de página

1

Ya existe una interfaz para esta función en el módulo estándar os — se eligió como un ejemplo simple y directo.

2

La metáfora de «pedir prestado» una referencia no es completamente correcta: el propietario todavía tiene una copia de la referencia.

3

¡Comprobar que el recuento de referencia es al menos 1 no funciona — el recuento de referencia en sí podría estar en la memoria liberada y, por lo tanto, puede reutilizarse para otro objeto!

4

Estas garantías no se cumplen cuando utiliza la convención de llamadas de estilo «antiguo», que todavía se encuentra en muchos códigos existentes.