2008-10-24 18 views
12

Dostęp Pythona do zmiennych środowiskowych nie odzwierciedla dokładnie widoku systemu operacyjnego na środowisko procesów.Zmienne środowiskowe w języku Python w systemie Linux

Os.getenv i os.environ nie działają zgodnie z oczekiwaniami w określonych przypadkach.

Czy istnieje sposób, aby prawidłowo uzyskać działające środowisko procesowe?


Aby wykazać, co mam na myśli, weź dwie w przybliżeniu równoważne programy (pierwszy w C, drugi w Pythonie):

#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) 

Teraz, jeśli uruchamiamy program C i dołączamy do działającego procesu z gdb i wymuszamy zmianę środowiska pod maską, wykonując coś takiego:

(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" 

następnie wspomniany program C zacznie wypluwać "moją wartość" co 5 sekund. Wspomniany wcześniej program Pythona nie będzie.

Czy istnieje sposób, aby program Pythona działał jak program C w tym przypadku?

(Tak, zdaję sobie sprawę, że jest to bardzo niejasne i potencjalnie szkodliwe działanie do wykonania na uruchomionego procesu)

Również obecnie używam Pythona 2.4, może to zostały ustalone w nowszej wersji Pythona .

+0

Na co warto, to nie jest nieoczekiwany: punktem odniesienia dla modułu biblioteki os podkreśla problem. – bobince

Odpowiedz

14

To bardzo dobre pytanie.

Okazuje się, że moduł os inicjuje os.environ do wartości posix.environ, który jest ustawiony na tłumacza rozruchu. Innymi słowy, standardowa biblioteka nie zapewnia dostępu do funkcji getenv.

Jest to przypadek, w którym byłoby prawdopodobnie bezpieczne korzystanie z Unix w wersji ctypes. Ponieważ wywołasz ultra-standardową funkcję libc.

1

Patrząc na kod źródłowy Python (2.4.5):

  • Moduły/posixmodule.c dostaje environ convertenviron(), która pobiera uruchamiane przy starcie (patrz INITFUNC) i przechowuje środowisko w moduł od platformy (nt, OS2, lub POSIX)

  • lib/os.py patrzy na sys.builtin_module_names i importuje wszystkie symbole z obu POSIX, NT, lub OS2

Tak tak, to się decyduje na starcie. os.environ nie będzie tu pomocny.

Jeśli naprawdę chcesz to zrobić, to najbardziej oczywistym podejściem, które przychodzi Ci do głowy, jest stworzenie własnego, niestandardowego modułu pythonowego opartego na C, z funkcją getenv, która zawsze wywołuje wywołanie systemowe.

+0

Albo mógłbym użyć modułu ctypes, ale to tylko psuje zabawę, prawda? – Sufian

3

Nie wierzę, że wiele programów KIEDYKOLWIEK spodziewa się, że ich środowisko będzie zewnętrznie zmodyfikowane, więc ładowanie kopii uruchomionego środowiska podczas uruchamiania jest równoważne. Po prostu natknąłeś się na wybór implementacji.

Jeśli widzisz wszystkie wartości set-at-startup i putenv/setenv z twojego programu działa, nie sądzę, że jest coś, czym należy się martwić. Istnieją znacznie czystsze sposoby przekazywania zaktualizowanych informacji do uruchamiania plików wykonywalnych.

4

Inną możliwością jest użycie pdb lub innego debugera python zamiast i zmiana os.environ na poziomie Pythona, a nie na poziomie C. Here's mały przepis, który napisałem, aby przerwać uruchomiony proces Pythona i zapewnić dostęp do konsoli Pythona po otrzymaniu sygnału. Alternatywnie, po prostu przyklej pdb.set_trace() w pewnym momencie kodu, który chcesz przerwać. W obu przypadkach po prostu uruchom polecenie "import os; os.environ['SOME_VARIABLE']='my_value'" i powinieneś zostać zaktualizowany pod względem pythona.

Nie jestem pewien, czy to również zaktualizuje środowisko C za pomocą setenv, więc jeśli masz moduły C używające getenv bezpośrednio, być może będziesz musiał wykonać więcej pracy, aby zachować synchronizację.

10

Można użyć ctypes to zrobić bardzo prosto:

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