2008-10-24 21 views
12

Pythons Zugriff auf Umgebungsvariablen spiegelt nicht genau die Sicht des Betriebssystems auf die Prozessumgebung wider.Umgebungsvariablen in Python unter Linux

os.getenv und os.environ funktionieren in bestimmten Fällen nicht wie erwartet.

Gibt es eine Möglichkeit, die Umgebung des laufenden Prozesses ordnungsgemäß zu erhalten?


Um zu zeigen, was ich meine, nehmen die beiden in etwa gleichwertige Programme (die erste in C, die andere in 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) 

Nun, wenn wir führen das C-Programm aus und hängen es an den laufenden Prozess mit gdb an und ändern gewaltsam die Umgebung unter der Haube, indem wir etwas tun:

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

dann beginnt das oben erwähnte C-Programm alle 5 Sekunden "my value" auszuspucken. Das oben erwähnte Python-Programm wird dies jedoch nicht tun.

Gibt es eine Möglichkeit, das Python-Programm in diesem Fall wie das C-Programm zu funktionieren?

(Ja, ich weiß, das ist eine sehr dunkel ist und potentiell schädigende Wirkung auf einem laufenden Prozess auszuführen)

Auch bin ich zur Zeit mit Python 2.4, dies in einer späteren Version von Python festgelegt worden sein .

+0

Für was es wert ist, ist dies nicht unerwartet: die Bibliothek Referenz für das os-Modul unterstreicht das Problem. – bobince

Antwort

14

Das ist eine sehr gute Frage.

Es stellt sich heraus, dass das os Modul os.environ auf den Wert von posix.environ, die auf initialisiert Dolmetscher eingestellt wird gestartet. Mit anderen Worten, die Standardbibliothek scheint keinen Zugriff auf die getenv-Funktion zu bieten.

Das ist ein Fall, in dem es wahrscheinlich sicher wäre, ctypes auf Unix zu verwenden. Da würden Sie eine Ultra-Standard-libc-Funktion aufrufen.

1

auf dem Quellcode Python sucht (2.4.5):

  • Module/posixmodule.c erhält die environ in convertenviron(), die beim Systemstart ausgeführt wird (siehe INITFUNC) und speichert die Umgebung in einem plattformspezifische Modul (nt, OS2 oder Posix)

  • Lib/os.py schaut sys.builtin_module_names und importiert alle Symbole aus entweder Posix, nt oder OS2

So Ja, es wird beim Start entschieden. os.environ wird hier nicht hilfreich sein.

Wenn Sie dies wirklich tun wollen, dann ist der offensichtlichste Ansatz, der Ihnen einfällt, Ihr eigenes benutzerdefiniertes C-basiertes Python-Modul mit einem getenv, das immer den Systemaufruf aufruft.

+0

Oder ich könnte das Ctypes-Modul verwenden, aber das ruiniert jetzt einfach den Spaß daran, nicht wahr? – Sufian

3

ich nicht viele Programme jemals glauben erwarten, dass ihre Umgebung extern geändert haben, so dass eine Kopie der übergebenen Umgebung beim Start Laden entspricht. Sie sind einfach auf eine Implementierungswahl gestoßen.

Wenn Sie all Set-at-Startwert sehen und putenv/setenv aus Ihrem Programm funktioniert, ich glaube nicht, dass es etwas gibt, besorgt zu sein. Es gibt weitaus sauberere Möglichkeiten, um aktualisierte Informationen an ausführbare ausführbare Dateien zu übergeben.

4

Ein andere Möglichkeit ist pdb oder einen anderen Python-Debugger zu verwenden, anstatt, und ändern Sie auf der Python-Ebene os.environ, anstatt die C-Ebene. Here's ein kleines Rezept, das ich gepostet habe, um einen laufenden Python-Prozess zu unterbrechen und den Zugriff auf eine Python-Konsole beim Empfang eines Signals zu ermöglichen. Alternativ kleben Sie einfach eine pdb.set_trace() an einem Punkt in Ihrem Code, den Sie unterbrechen möchten. Führen Sie in jedem Fall einfach die Anweisung "import os; os.environ['SOME_VARIABLE']='my_value'" aus und Sie sollten, soweit es Python betrifft, aktualisiert werden.

Ich bin mir nicht sicher, ob dies auch die C-Umgebung mit setenv aktualisiert, also wenn Sie C-Module direkt mit getenv verwenden, müssen Sie möglicherweise noch etwas mehr Arbeit tun, um dies zu synchronisieren.

10

können Sie ctypes, dies zu tun ziemlich einfach:

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