Создание оберток для функций окружения C

sozdanie obertok dlya funkcij okruzheniya c Интеграция Python/C

Следующий наш пример является модулем расширения C, который интегрирует функции getenv и putenv из стандартной библиотеки C, предназначенные для работы с переменными окружения, для использования в сценариях Python. В примере 20.8 представлен файл на языке C, достигающий поставленной цели за счет создания связующего слоя вручную.

Пример 20.8. PP4E\Integrate\Extend\Cenviron\cenviron.c

/*************************************************************************

*   Модуль расширения на C для Python с именем "cenviron". Обертывает

*   библиотечные функции getenv/putenv для использования в программах Python.

**************************************************************************/ #include <Python.h>

#include <stdlib.h>

#include <string.h> /***********************/ /* 1) функции модуля */ /***********************/

static PyObject * /* возвращаемый объект */

wrap_getenv(PyObject *self, PyObject *args) /* self не используется */

{ /* args из python */

char *varName, *varValue;

PyObject *returnObj = NULL; /* пи11=исключение */

if (PyArg_Parse(args, "(s)", &varName)) { /* Python -> C */

varValue = getenv(varName); /* вызов getenv из библ. C */

if (varValue != NULL)

returnObj = Py_BuildValue("s", varValue); /* C -> Python */ else

PyErr_SetString(PyExc_SystemError, "Error calling getenv");

}

return returnObj;

}

static PyObject *

wrap_putenv(PyObject *self, PyObject *args) {

char *varName, *varValue, *varAssign;

PyObject *returnObj = NULL;

if (PyArg_Parse(args, "(ss)", &varName, &varValue))

{

varAssign = malloc(strlen(varName) + strlen(varValue) + 2); sprintf(varAssign, "%s=%s", varName, varValue);

if (putenv(varAssign) == 0) {

Py_INCREF(Py_None); /* успешный вызов C */

returnObj = Py_None; /* ссылка на None */

} else

PyErr_SetString(PyExc_SystemError, "Error calling putenv");

} return returnObj;

} /**************************/ /* 2) таблица регистрации */ /**************************/

подпись: /* имя, адрес,... */
/* имя, адрес,... */ /* признак конца */
static PyMethodDef cenviron_methods[] = {

{"getenv", wrap_getenv, METH_VARARGS, "getenv doc"}, {"putenv", wrap_putenv, METH_VARARGS, "putenv doc"}, {NULL, NULL, 0, NULL}

};

/*************************/ /* 3) определение модуля */ /*************************/

static struct PyModuleDef cenvironmodule = {

PyModuleDef_HEAD_INIT,

"cenviron", /* имя модуля */

"cenviron doc", /* описание модуля, может быть NULL */

-1, /* размер структуры для каждого экземпляра, -1=глоб. перем. */ cenviron_methods /* ссылка на таблицу методов */

};

/***************************/

/* 4) инициализация модуля */

/***************************/

PyMODINIT_FUNC

PyInit_cenviron() /* вызывается первой инструкцией импорта */

{ /* имя имеет значение при динамической загрузке */

return PyModule_Create(&cenvironmodule);

}

Хотя этот пример достаточно представителен, тем не менее, сейчас он менее полезен, чем когда он был включен в первое издание этой книги, — как мы узнали во второй части, можно не только получать значения переменных окружения из таблицы os.environ, но и автоматически вызывать функцию C putenv, присваивая значения ключам в этой таблице, чтобы экспортировать новые значения в слой программного кода C. То есть обращение os.environ[‘key’] загрузит значение переменной окружения ‘key’, а операция os.environ[‘key’]=value присвоит значение переменной окружения как в Python, так и в C.

Второе действие — присвоение в C — было добавлено в Python после выхода первого издания книги. Кроме дополнительного показа приемов программирования расширений этот пример все же служит практической цели: даже сегодня изменения переменных окружения в программном коде на C, связанном с процессом Python, не видны при обращении к os.environ в программном коде Python. То есть после запуска программы таблица os.environ отражает только последующие изменения, сделанные в программном коде Python.

Кроме того, несмотря на то, что в настоящее время в модуле os Python имеются обе функции, putenv и getenv, их интеграция выглядит неполной. Изменения в os.environ приводят к вызову os.putenv, но прямой вызов os.putenv не изменяет содержимое os.environ, поэтому значения в os. environ могут не соответствовать действительности. А функция os.getenv в настоящее время просто обращается к os.environ и поэтому может не замечать изменения в окружении, выполненные уже после запуска процесса, за пределами программного кода Python. Такое положение вещей может изредка приводить к проблемам, но все равно этот модуль расширения на C нельзя назвать полностью бесполезным. Для правильного взаимодействия переменных окружения со встроенным программным кодом C необходимо вызывать функции или библиотеки языка C непосредственно (по крайней мере, пока Python опять не изменит эту модель!).

Файл cenviron.c, представленный в примере 20.8, создает модуль Python с именем cenviron, который идет немного дальше, чем предыдущий пример, — он экспортирует две функции, явно определяет описания некоторых исключений и обращается к счетчику ссылок объекта Python None (он не создается заново, поэтому необходимо добавить ссылку перед отправкой его в Python). Как и раньше, чтобы добавить этот модуль в Python, следует откомпилировать и скомпоновать этот программный код в объектный файл. В примере 20.9 представлен make-файл для Cygwin, который компилирует исходный программный код на языке C в модуль, готовый для динамического связывания при импортировании.

Пример 20.9. PP4E\Integrate\Extend\Cenviron\makefile.cenviron

#################################################################### # Компилирует cenviron.c в cenviron.dllразделяемый объектный файл # в Cygwin, динамически загружаемый первой инструкцией импорта.

####################################################################

PYLIB = /usr/local/bin

PYINC = /usr/local/include/python3.1

cenviron.dll: cenviron.c

gcc cenviron.c -g -I$(PYINC) -shared -L$(PYLIB) -lpython3.1 -o $@ clean:

rm -f *.pyc cenviron.dll

Для сборки введите в оболочке команду make -f makefile.cenviron. Перед запуском сценария убедитесь, что файл .dll находится в каталоге, включенном в путь поиска Python (текущий рабочий каталог тоже годится):

/PP4E/Integrate/Extend/Cenviron$ python

>   >> import cenviron

>   >> cenviron.getenv(‘USER’) # подобно os.environ[key], но загружает заново ‘mark’

>   >> cenviron.putenv(‘USER’, ‘gilligan’) # подобно os.environ[key]=value

>   >> cenviron.getenv(‘USER‘) # программный код на C тоже видит изменения gilligan

Как и прежде, cenviron является настоящим объектом модуля Python после импорта со всей информацией, обычно прикрепляемой к модулям, и в случае ошибок корректно возбуждает исключения и передает описание ошибок:

>   >> dir(cenviron)

[‘__doc__’, ‘__file__’, ‘__name__’, ‘__packge__’, ‘getenv’, ‘putenv’]

>   >> cenviron.__file__

‘cenviron.dll’

>>> cenviron.__name__

‘cenviron’

>>> cenviron.getenv

<built-in function getenv>

>>> cenviron

<module ‘cenviron’ from ‘cenviron.dll’>

>>> cenviron.getenv(‘HOME’)

‘/home/mark’

>>> cenviron.getenv(‘NONESUCH’)

SystemError: Error calling getenv

Ниже приводится пример задачи, которая решается с помощью этого модуля (но необходимо, чтобы вызовы getenv осуществлялись присоединенным кодом C, а не Python; перед запуском этого сеанса я изменил значение переменной USER в командной оболочке с помощью команды export):

/PP4E/Integrate/Extend/Cenviron$ python

>>> import os

>>> os.environ[‘USER‘] # инициализируется из оболочки

‘skipper’

>>> from cenviron import getenv, putenv # прямые вызовы функций C

>>> getenv(‘USER’)

‘skipper’

>>> putenv(‘USER’, ‘gilligan’) # изменится для C, но не для Python

>>> getenv(‘USER’)

#   подпись: 'gilligan'
>>> os.environ['user'] 'skipper'
>>> os.getenv('user')
'skipper'
ой! — значения не загружаются заново

#   то же самое

Использованная литература:

Марк Лутц — Программирование на Python, 4-е издание, II том, 2011

Каталог сайтов Всего.ру
Оцените статью
Секреты программирования
Добавить комментарий