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 llamada 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 _PyImport_Inittab. El modo más sencillo de gestionar esto es inicializar estáticamente los módulos enlazados estáticamente llamando directamente a initspam() tras llamar a Py_Initialize() o PyMac_Initialize():

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 reload() 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 Unix, .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.


Ver Sobre este documento... para obtener información sobre sugerencias.