Доступ Python к переменным среды не точно отражает взгляд операционной системы на среду процессов.
os.getenv и os.environ не работают должным образом в определенных случаях.
Есть ли способ правильно получить среду запущенного процесса?
Чтобы продемонстрировать, что я имею в виду, возьмем две примерно эквивалентные программы (первая на C, другая на python):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[]){
char *env;
for(;;){
env = getenv("SOME_VARIABLE");
if (env)
puts(env);
sleep(5);
}
}
import os
import time
while True:
env = os.getenv("SOME_VARIABLE")
if env is not None:
print env
time.sleep(5)
Теперь, если мы запустим программу на C, подключимся к запущенному процессу с помощью gdb и принудительно изменим внутреннюю среду, выполнив что-то вроде этого:
(gdb) print setenv("SOME_VARIABLE", "my value", 1)
[Switching to Thread -1208600896 (LWP 16163)]
$1 = 0
(gdb) print (char *)getenv("SOME_VARIABLE")
$2 = 0x8293126 "my value"
тогда вышеупомянутая программа на языке C начнет выдавать «мое значение» каждые 5 секунд. Однако вышеупомянутая программа на Python не работает.
Есть ли способ заставить программу Python работать как программа C в этом случае?
(Да, я понимаю, что это очень непонятное и потенциально опасное действие для выполняемого процесса)
Кроме того, в настоящее время я использую python 2.4, возможно, это было исправлено в более поздней версии python.






Посмотрим на исходный код Python (2.4.5):
Modules / posixmodule.c получает среду в convertenviron (), которая запускается при запуске (см. INITFUNC), и сохраняет среду в модуле, зависящем от платформы (nt, os2 или posix).
Lib / os.py просматривает sys.builtin_module_names и импортирует все символы из posix, nt или os2.
Так что да, это решается при запуске. os.environ здесь не поможет.
Если вы действительно хотите это сделать, то наиболее очевидный подход, который приходит на ум, - это создать свой собственный модуль python на основе C с getenv, который всегда вызывает системный вызов.
Или я мог бы использовать модуль ctypes, но теперь это просто портит удовольствие, не так ли?
Это очень хороший вопрос.
Оказывается, модуль os инициализирует os.environ значением posix.environ, которое устанавливается при запуске интерпретатора. Другими словами, стандартная библиотека не предоставляет доступа к функции getenv.
В этом случае, вероятно, было бы безопасно использовать ctypes в unix. Поскольку вы вызываете сверхстандартную функцию libc.
Я не верю, что многие программы КОГДА-ЛИБО ожидают, что их среда будет изменена извне, поэтому загрузка копии переданной среды при запуске эквивалентна. Вы просто наткнулись на выбор реализации.
Если вы видите, что все значения, установленные при запуске, и putenv / setenv изнутри вашей программы работают, я не думаю, что есть о чем беспокоиться. Есть гораздо более чистые способы передать обновленную информацию работающим исполняемым файлам.
Другая возможность - использовать вместо этого pdb или какой-либо другой отладчик python и изменить os.environ на уровне python, а не на уровне C. Вот - небольшой рецепт, который я опубликовал, чтобы прервать запущенный процесс Python и предоставить доступ к консоли Python при получении сигнала. В качестве альтернативы, просто вставьте pdb.set_trace () в какой-то момент вашего кода, который вы хотите прервать. В любом случае просто запустите оператор «import os; os.environ['SOME_VARIABLE']='my_value'», и вы должны быть обновлены в отношении Python.
Я не уверен, что это также обновит среду C с помощью setenv, поэтому, если у вас есть модули C, использующие getenv напрямую, вам, возможно, придется проделать еще немного работы, чтобы синхронизировать это.
Вы можете использовать ctypes, чтобы сделать это довольно просто:
>>> from ctypes import CDLL, c_char_p
>>> getenv = CDLL("libc.so.6").getenv
>>> getenv.restype = c_char_p
>>> getenv("HOME")
'/home/glyph'
Как бы то ни было, это не является неожиданностью: ссылка на библиотеку для модуля os подчеркивает проблему.