Модули расширения можно писать вручную, как мы это только что сделали, но это совершенно необязательно. Поскольку этот пример в действительности просто создает обертки для имеющихся функций в стандартных библиотеках C, весь файл cenviron.c с программным кодом C, представленным в примере 20.8, можно заменить простым входным файлом SWIG, который выглядит, как показано в примере 20.12:
Пример 20.12. PP4E\Integrate\Extend\Swig\Environ\environ.i
/**************************************************************************
* Файл Swig описания модуля, чтобы сгенерировать весь программный код обертки * Python для функций getenv/putenv из библиотеки C: "swig -python environ.i". **************************************************************************/
%module environ
extern char * getenv(const char *varname);
extern int putenv(char *assignment);
И все готово. Ну, почти — все же нужно еще пропустить этот файл через SWIG и скомпилировать полученный вывод. Как и прежде, просто добавьте в свой make-файл строки для вызова SWIG, скомпилируйте результат в разделяемый объект для динамического связывания и все будет готово. В примере 20.13 представлен make-файл для Cygwin, который решает эту задачу.
Пример 20.13. PP4E\Integrate\Extend\Swig\Environ\makefile.environ-swig
# компилирует расширение environ из программного кода, сгенерированного SWIG
PYLIB = /usr/local/bin
PYINC = /usr/local/include/python3.1
SWIG = /cygdrive/c/temp/swigwin-2.0.0/swig
_environ.dll: environ_wrap.c
gcc environ_wrap.c -g -I$(PYINC) -L$(PYLIB) -lpython3.1 -shared -o $@
environ_wrap.c: environ.i
$(SWIG) -python environ.i
clean: rm -f *.o *.dll *.pyc core environ_wrap.c environ.py
При запуске с файлом environ.i утилита SWIG создаст два файла и два модуля — environ.py (интерфейсный модуль Python) и environ_wrap.c (файл модуля со связующим программным кодом, который будет скомпилирован в файл _environ.dll и будет импортироваться файлом .py). Так как функции, для которых здесь создаются обертки, находятся в стандартных присоединяемых библиотеках C, объединять с генерируемым программным кодом нечего — этот make-файл просто запускает SWIG и компилирует файл обертки в модуль расширения C, готовый к импортированию:
…/PP4E/Integrate/Extend/Swig/Environ$ make -f makefile.environ-swig /cygdrive/c/temp/swigwin-2.0.0/swig -python environ.i
gcc environ_wrap.c -g -I/usr/local/include/python3.1 -L/usr/local/bin -lpython3.1
—shared —o _environ.dll
И вот теперь действительно все. Полученный модуль расширения на C связывается при импортировании и используется как прежде (за исключением того, что все сложности были решены с помощью SWIG):
…/PP4E/Integrate/Extend/Swig/Environ$ ls
_environ.dll environ.i environ.py environ_wrap.c makefile.environ-swig
…/PP4E/Integrate/Extend/Swig/Environ$ python
>>> import environ
>>> environ.getenv(‘USER’)
‘gilligan’
>>> environ.__name__, environ.__file__, environ
(‘environ’, ‘environ.py’, <module ‘environ’ from ‘environ.py’>)
>>> dir(environ)
[ … ‘_environ’, ‘getenv’, ‘putenv’ … ]
Если внимательно рассмотреть последний листинг сеанса, можно заметить, что на этот раз я не вызывал функцию putenv. Как оказывается, тому есть веская причина: функция putenv в библиотеке C ожидает получить строку вида «USER=Gilligan», которая станет частью окружения. Для программного кода C это означает, что мы должны выделить новый блок памяти для передачи функции — для удовлетворения этого требования в примере 20.8 мы использовали функцию malloc. Однако нет простого и непосредственного способа обеспечить выделение памяти на стороне Python. В предыдущей версии Python было достаточно сохранить строку, передаваемую функции putenv, во временной переменной Python, но в Python 3.X и/или SWIG 2.0 этот прием больше не работает. Для исправления ситуации можно либо написать собственную функцию на C, либо использовать средства SWIG отображения типов, которые позволяют настраивать обработку передачи данных. В интересах экономии места мы оставим решение этой проблемы в качестве самостоятельного упражнения — подробности смотрите в документации SWIG.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, II том, 2011