2008-10-24 20 views
12

El acceso de Python a las variables de entorno no refleja con precisión la vista del sistema operativo del entorno de procesos.Variables de entorno en Python en Linux

os.getenv y os.environ no funcionan como se esperaba en casos particulares.

¿Hay alguna forma de obtener correctamente el entorno del proceso en ejecución?


Para demostrar lo que quiero decir, tomar los dos programas más o menos equivalentes (la primera en C, y el otro en 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) 

Ahora, si ejecutamos el programa C y lo conectamos al proceso en ejecución con gdb y cambiamos forzosamente el entorno bajo el capó haciendo algo como esto:

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

luego el citado programa C comenzará a arrojar "mi valor" una vez cada 5 segundos. El programa python antes mencionado, sin embargo, no lo hará.

¿Hay alguna manera de hacer que el programa python funcione como el programa C en este caso?

(Sí, me di cuenta que es una acción muy oscura y potencialmente perjudiciales para llevar a cabo en un proceso en ejecución)

Además, actualmente estoy usando Python 2.4, esto puede haber sido arreglado en una versión posterior de pitón .

+0

Por lo que vale la pena, esto no es inesperado: la referencia de la biblioteca para el módulo os pone de relieve el tema. – bobince

Respuesta

14

Esa es una muy buena pregunta.

Resulta que el módulo inicializa osos.environ al valor de posix.environ, que se establece en el intérprete de puesta en marcha. En otras palabras, la biblioteca estándar no parece proporcionar acceso a la función getenv.

Este es un caso en el que probablemente sería seguro usar ctypes en Unix. Ya que estarías llamando a una función libc ultra estándar.

1

Si examina el código fuente de Python (2.4.5):

  • Módulos/posixmodule.c obtiene el Environ en convertenviron() el cual es ejecutado en el arranque (ver INITFUNC) y almacena el medio ambiente de una específico de la plataforma de módulo (nt, OS2, o POSIX)

  • Lib/os.py mira sys.builtin_module_names, e importa todos los símbolos de cualquiera de POSIX, NT o OS2

Así sí, se decide al inicio. os.environ no va a ser útil aquí.

Si realmente desea hacer esto, entonces el enfoque más obvio que se le viene a la mente es crear su propio módulo de python basado en C personalizado, con un getenv que siempre invoque la llamada al sistema.

+0

O podría usar el módulo ctypes, pero eso solo arruina la diversión ahora, ¿no? – Sufian

3

No creo que muchos programas esperen que su entorno se modifique externamente, por lo que cargar una copia del entorno pasado al inicio es equivalente. Simplemente ha tropezado con una opción de implementación.

Si ve todos los valores establecidos al inicio y putenv/setenv desde el programa funciona, no creo que haya nada de qué preocuparse. Hay maneras mucho más limpias de pasar información actualizada a ejecutar ejecutables.

4

Otra posibilidad es usar pdb, o algún otro depurador de python en su lugar, y cambiar os.environ en el nivel de Python, en lugar del nivel C. Here's una pequeña receta que publiqué para interrumpir un proceso python en ejecución y proporcionar acceso a una consola de python al recibir una señal. Alternativamente, simplemente inserte un pdb.set_trace() en algún punto de su código que quiera interrumpir. En cualquier caso, solo ejecuta la instrucción "import os; os.environ['SOME_VARIABLE']='my_value'" y deberías estar actualizado en lo que se refiere a python.

No estoy seguro si esto también actualizará el entorno C con setenv, por lo que si tiene módulos C que usan getenv directamente, es posible que tenga que trabajar un poco más para mantener esto sincronizado.

10

Puede utilizar ctypes hacer esto bastante simple:

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