Переменные среды в Python на Linux - PullRequest
13 голосов
/ 25 октября 2008

Доступ 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"

тогда вышеупомянутая программа на С начнет извергать «мое значение» каждые 5 секунд. Однако вышеупомянутая программа на python не будет.

Есть ли способ заставить программу на python функционировать в этом случае как программу на C?

(Да, я понимаю, что это очень непонятное и потенциально разрушительное действие для выполняющегося процесса)

Кроме того, в настоящее время я использую python 2.4, возможно, это было исправлено в более поздней версии python.

Ответы [ 5 ]

14 голосов
/ 25 октября 2008

Это очень хороший вопрос.

Оказывается, что модуль os инициализирует os.environ значением posix.environ, которое устанавливается при запуске интерпретатора. Другими словами, стандартная библиотека не предоставляет доступа к функции getenv .

Это тот случай, когда было бы безопасно использовать ctypes в unix. Поскольку вы будете вызывать сверхстандартную функцию libc.

11 голосов
/ 28 октября 2008

Вы можете использовать ctypes, чтобы сделать это довольно просто:

>>> from ctypes import CDLL, c_char_p
>>> getenv = CDLL("libc.so.6").getenv
>>> getenv.restype = c_char_p
>>> getenv("HOME")
'/home/glyph'
4 голосов
/ 26 октября 2008

Другая возможность - использовать вместо этого pdb или какой-либо другой отладчик python и изменить os.environ на уровне python, а не на уровне C. Вот небольшой рецепт, который я опубликовал, чтобы прервать запущенный процесс python и предоставить доступ к консоли python при получении сигнала. В качестве альтернативы, просто вставьте pdb.set_trace () в какой-то момент кода, который вы хотите прервать. В любом случае, просто запустите оператор "import os; os.environ['SOME_VARIABLE']='my_value'", и вы должны быть обновлены в отношении python.

Я не уверен, что это также обновит среду C с помощью setenv, поэтому, если у вас есть модули C, использующие getenv напрямую, вам, возможно, придется проделать дополнительную работу, чтобы синхронизировать это.

3 голосов
/ 25 октября 2008

Я не верю, что многие программы КОГДА-ЛИБО ожидают, что их среда будет изменена извне, поэтому загрузка копии переданной среды при запуске эквивалентна. Вы просто наткнулись на выбор реализации.

Если вы видите, что все значения set-at-startup и putenv / setenv изнутри вашей программы работают, я не думаю, что есть о чем беспокоиться. Существуют более понятные способы передачи обновленной информации исполняемым файлам.

1 голос
/ 25 октября 2008

Глядя на исходный код Python (2.4.5):

  • Modules / posixmodule.c получает среду в converttenviron (), которая запускается при запуске (см. INITFUNC) и сохраняет среду в модуле для платформы (nt, os2 или posix)

  • Lib / os.py просматривает sys.builtin_module_names и импортирует все символы из posix, nt или os2

Так что да, это решается при запуске. os.environ здесь не поможет.

Если вы действительно хотите это сделать, то самый очевидный подход, который приходит вам в голову, - это создать свой собственный модуль Python на основе C с помощью getenv, который всегда вызывает системный вызов.

...