Вопрос

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

Это было полезно?

Решение

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

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

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

Другие советы

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

>>> from ctypes import CDLL, c_char_p
>>> getenv = CDLL("libc.so.6").getenv
>>> getenv.restype = c_char_p
>>> getenv("HOME")
'/home/glyph'

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

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

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

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

Глядя на исходный код 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, который всегда вызывает системный вызов.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top