简体   繁体   中英

Using copy.deepcopy on os.environ in python appears broken

I probably just missed some bit of documentation on how os.environ or copy.deepcopy works, but it appears that copy.deepcopy doesn't work on os.environ. But if I reconstruct the os.environ into a new dictionary, it works fine. Here's my example code:

import copy
import os

tcsh_loc = '/bin/tcsh'
safe_dict = {}
for key in os.environ.keys():
    safe_dict[key] = os.environ[key]

safe_dict['SAFE_ENV'] = 'non-leaked-var'
os.spawnv(os.P_WAIT, tcsh_loc, [tcsh_loc, '-c', 'echo $SAFE_ENV'])
os.spawnve(os.P_WAIT, tcsh_loc, [tcsh_loc, '-c', 'echo $SAFE_ENV'], safe_dict)

unsafe_dict = copy.deepcopy(os.environ)
unsafe_dict['UNSAFE_ENV'] = 'leaked-var'
os.spawnv(os.P_WAIT, tcsh_loc, [tcsh_loc, '-c', 'echo $UNSAFE_ENV'])
os.spawnve(os.P_WAIT, tcsh_loc, [tcsh_loc, '-c', 'echo $UNSAFE_ENV'], unsafe_dict)

What I expect to get out is:

SAFE_ENV: Undefined variable.
non-leaked-var
UNSAFE_ENV: Undefined variable.
leaked-var

But what I get out is:

SAFE_ENV: Undefined variable.
non-leaked-var
leaked-var
leaked-var

Which implies that somehow the unsafe_dict['UNSAFE_ENV'] = 'leaked-var' assignment somehow "leaks" into os.environ, presumably from os.environ not getting deepcopied as I expected.

I assume this is some kind of known behavior, but it seems really weird an undesirable to me, at least in terms of using things like os.spawnev(). I've got a clumsy workaround, but I'd be interested to understand what is going on and if there is a more elegant solution than a for loop...

os.environ is of type os._Environ , not a list or dictionary. It is logical that a copy of an instance of os._Environ will modify the environment as well.

See the os._Environ.__setitem__() function. It stores the values in 2 places , once using putenv() and one to assign the key in the self._data dictionary.

def __setitem__(self, key, value):
    key = self.encodekey(key)
    value = self.encodevalue(value)
    self.putenv(key, value)
    self._data[key] = value

You can reconstruct it easier: Just use dict(os.environ) .

simple test:

import os
a=os.environ
b=dict(os.environ)

print type(a), type(b)
# -> <type 'instance'> <type 'dict'>

print a['PWD'], b['PWD']
# -> /home/max /home/max

b['PWD']='/fooo'
print a['PWD'], b['PWD']
# -> /home/max /fooo

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM