2008-10-24 5 views
12

Доступ к переменным среды Python не точно отражает представление операционной системы о состоянии операционной системы.Переменные среды в Python для Linux

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, это может быть исправлено в более поздней версии питона ,

+0

Для чего это стоит, это не является неожиданным: библиотека ссылки для модуля зева выдвигает на первый план проблему. – bobince 2008-10-24 22:35:50

ответ

14

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

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

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

1

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

  • Модули/posixmodule.c получает окружа в convertenviron() который запускается при старте (см INITFUNC) и сохраняет окружающую среду в платформы специальный модуль (нуклеотиды, os2 или POSIX)

  • Lib/os.py смотрит на sys.builtin_module_names, и импортирует все символы из любой POSIX, нт или os2

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

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

+0

Или я мог бы использовать модуль ctypes, но это просто разрушает удовольствие от этого сейчас, не так ли? – Sufian 2008-10-24 22:18:06

3

Я не верю, что многие программы ВСЕГДА ожидают изменения внешней среды, поэтому загрузка копии пройденной среды при запуске эквивалентна. Вы просто наткнулись на выбор реализации.

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

4

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

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

10

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

>>> from ctypes import CDLL, c_char_p 
>>> getenv = CDLL("libc.so.6").getenv 
>>> getenv.restype = c_char_p 
>>> getenv("HOME") 
'/home/glyph' 
Смежные вопросы