Иными словами, в этом примере используются оба знакомых нам интерфейса — расширения и встраивания — для регистрации и вызова программного кода обработчика события на Python. Чтобы лучше узнать, как действует этот прием, внимательно изучите его реализацию в примере 20.30.
Пример 20.30. PP4E\Integrate\Embed\Regist\cregister.c
#include <Python.h> #include <stdlib.h> /***********************************************/
/* 1) передача событий объекту Python */
/* вместо этого можно было бы выполнять строки */ /***********************************************/
static PyObject *Handler = NULL; /* содержит объект Python в C */
void Route_Event(char *label, int count) {
char *cres;
PyObject *args, *pres;
/* вызов обработчика Python */
args = Py_BuildValue("(si)", label, count); /* создать список
аргументов */
pres = PyEval_CallObject(Handler, args); /* применение: выполнить вызов */
Py_DECREF(args); /* добавить контроль ошибок */
if (pres != NULL) {
/* использовать результат и уменьшить счетчик ссылок на него */ PyArg_Parse(pres, "s", &cres);
printf("%s\n", cres);
Py_DECREF(pres);
}
}
/*************************************************************/
/* 2) модуль расширения python для регистрации обработчиков */
/* python импортирует этот модуль для установки обработчиков */ /*************************************************************/
static PyObject *
Register_Handler(PyObject *self, PyObject *args) {
/* сохранить вызываемый объект Python */
Py_XDECREF(Handler); /* вызывался прежде? */
PyArg_Parse(args, "(O)", &Handler); /* один аргумент */
Py_XINCREF(Handler); /* добавить ссылку */
Py_INCREF(Py_None); /* вернуть ‘None’: успех */ return Py_None;
}
static PyObject *
Trigger_Event(PyObject *self, PyObject *args) {
/* позволить Python имитировать событие, перехваченное C */ static count = 0;
Route_Event("spam", count++);
Py_INCREF(Py_None); return Py_None;
}
static PyMethodDef cregister_methods[] = {
{"setHandler", Register_Handler, METH_VARARGS, ""}, /* имя, адр.
функ.,…*/ {"triggerEvent", Trigger_Event, METH_VARARGS, ""},
{NULL, NULL, 0, NULL} /* конец таблицы */
};
static struct PyModuleDef cregistermodule = {
PyModuleDef_HEAD_INIT, "cregister", /* имя модуля */ "cregister mod", /* описание модуля, может быть NULL */ -1, /* размер структуры для каждого экземпляра, -1=глоб. перем. */ cregister_methods /* ссылка на таблицу методов */
};
PyMODINIT_FUNC
PyInit_cregister() /* вызывается первой инструкцией импортирования */ {
return PyModule_Create(&cregistermodule);
}
Конечно, эта программа на C является модулем расширения Python, а не самостоятельной программой C со встроенным программным кодом Python (хотя программа на C могла бы быть верхним уровнем). Для компиляции ее в файл динамически загружаемого модуля выполните make-файл, представленный в примере 20.31 в Cygwin (или аналогичный ему make-файл на других платформах). Как мы узнали выше в этой главе, получающийся файл cregister.dll будет загружен при первом импорте в сценарии Python, если поместить его в каталог, находящийся в пути поиска модулей Python (например, . или содержащийся в PYTHONPATH).
Пример 20.31. PP4E\Integrate\Embed\Regist\makefile.regist
######################################################################
# make—файл для Cygwin, выполняющий сборку cregister.dll, динамически
# загружаемого модуля расширения на C (разделяемого), который
# будет импортироваться сценарием register.py
######################################################################
PYLIB = /usr/local/bin
PYINC = /usr/local/include/python3.1
CMODS = cregister.dll
all: $(CMODS)
cregister.dll: cregister.c
gcc cregister.c -g -I$(PYINC) -shared -L$(PYLIB) -lpython3.1 -o $@
clean:
rm -f *.pyc $(CMODS)
Теперь, когда у нас есть модуль расширения C для регистрации и вызова обработчиков Python, нам нужны лишь какие-нибудь обработчики Python. Модуль Python, представленный в примере 20.32, определяет две функции обработчиков обратного вызова, импортирует модуль расширения C для регистрации обработчиков и генерирует события.
Пример 20.32. PP4E\Integrate\Embed\Regist\register.py
######################################################################### в Python, регистрирует обработчики событий, которые будут вызываться из C; скомпилируйте и скомпонуйте программный код на C и запустите этот сценарий командой ‘python register.py‘
#########################################################################
# ############################################
# эти функции Python будут вызываться из C;
# обрабатывать события и возвращать результат
# ############################################
def callback1(label, count):
return ‘callback1 => %s number %i’ % (label, count)
def callback2(label, count):
return ‘callback2 => ‘ + label * count
# #################################################
# Python вызывает модуль расширения C
# для регистрации обработчиков, возбуждает события
##################################################
import cregister
print(‘\nTest1:’)
cregister.setHandler(callback1) # зарегистрировать функцию—обработчик
for i in range(3):
cregister.triggerEvent() # имитировать события, перехватываемые слоем C
print(‘\nTest2:’)
cregister.setHandler(callback2)
for i in range(3):
cregister.triggerEvent() # передаст события функции callback2
Вот и все — интеграция обработчиков обратного вызова Python/C готова к работе. Чтобы запустить систему, выполните сценарий Python. Он зарегистрирует одну функцию, сгенерирует три события, затем изменит обработчик событий и повторит все снова:
…/PP4E/Integrate/Embed/Regist$ make -f makefile.regist
gcc cregister.c -g -I/usr/local/include/python3.1 -shared -L/usr/local/bin -lpython3.1 -o cregister.dll
…/PP4E/Integrate/Embed/Regist$ python register.py
Test1:
callback1 => spam number 0
callback1 => spam number 1
callback1 => spam number 2
Test2:
callback2 => spamspamspam
callback2 => spamspamspamspam
callback2 => spamspamspamspamspam
Этот вывод производит функция в программе C маршрутизации событий, но в нем присутствуют значения, возвращаемые функциями обработчиков в модуле Python. В действительности здесь незаметно много чего происходит. Когда Python возбуждает событие, между двумя языками происходит такая передача управления:
1. Из Python в функцию маршрутизации событий C.
2. Из функции маршрутизации событий C в функцию-обработчик Python.
3. Обратно в функцию маршрутизации событий C (которая выводит полученные результаты).
4. Наконец, обратно в сценарий Python.
Таким образом, мы переходим из Python в C, оттуда в Python и еще раз обратно. Попутно управление передается через интерфейсы расширения и встраивания. Во время работы обработчика обратного вызова Python активными оказываются два уровня Python и один уровень C посередине. К счастью, это действует — API Python отличается реентерабельностью, поэтому не нужно беспокоиться по поводу того, что одновременно активны несколько уровней интерпретатора Python. Каждый уровень выполняет свой программный код и действует независимо.
Попробуйте по результатам работы и исходному программному коду этого примера проследить порядок выполнения операций, чтобы лучше понять, как он действует. А теперь перейдем к последнему короткому примеру, пока у нас еще есть время и место для исследований, — для симметрии попробуем использовать классы Python из программного кода на C.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, II том, 2011