2

What would be a pythonic way of resetting os.environ to the default values one would find in a command shell? I could handle this by first pushing os.environ into a default dictionary, but that method would fail in the event os.environ is changed by another module before mine is imported.

In Windows i'm currently able to reset values like so:

import os, subprocess, tempfile

def is_locked(filepath):
    ''' Needed to determine when the set command below completes
    '''
    locked = None
    file_object = None
    if os.path.exists(filepath):
        try:
            buffer_size = 8
            file_object = open(filepath, 'a', buffer_size)
            if file_object:
                locked = False
        except IOError, message:
            locked = True
        finally:
            if file_object:
                file_object.close()
    else:
        locked = True
    return locked

# Define a Windows command which would dump default env variables into a tempfile
#
# - start /i will give default cmd.exe environment variable to what follows.
#   It's the only way i found to get cmd.exe's default env variables and because
#   start terminates immediately i need to use is_locked defined above to wait for
#   Window's set command to finish dumping it's variables into the temp file
# - /b is will prevent a command shell to pop up
temp = tempfile.mktemp()
cmd = 'start /i /b cmd /c set>%s'%temp
p = subprocess.Popen(cmd, shell=True)
p.wait()

# Wait for the command to complete and parse the tempfile
data = []
while(is_locked(temp)):
    pass

with open(temp,'r') as file:
    data = file.readlines()
os.remove(temp)

# defaults will contain default env variables
defaults = dict()
for env in data:
    env = env.strip().split('=')
    defaults[env[0]]=env[1]

    print '%s %s'%(env[0],env[1])


os.environ = dict(defaults)

My way works currently in python 2.7.3 64 bit, but i just noticed that when i run this in 32 bit, both the PROGRAMFILES and PROGRAMFILES(x86) env variables point to "Program Files (x86)" which is an issue discussed here.

Thanks in advance for all you help!

Community
  • 1
  • 1
Fnord
  • 5,365
  • 4
  • 31
  • 48
  • 1
    Why not push the settings before making any changes? – Brian Cain Oct 25 '13 at 03:02
  • 1
    @BrianCain i thought about that, but the reason i'm writing this is to catch the case where the module i'm working on would get called after os.environ has been changed from it's default values – Fnord Oct 25 '13 at 04:39
  • Seems like your module might not have sufficient scope to restore `os.environ` to anything other than how it was when it was invoked. – Brian Cain Oct 25 '13 at 14:13

2 Answers2

0

I wonder that your hack seems to work, because it is by design that the child inherits the current environment of the parent. So I think there's no alternative to saving the environment at app startup. This is not necessarily the env of a shell started from the start menu, though, as your parent could have manipulated it, too.

Do the following experiment: start an explorer, run a shell from it and investigate the env. Then start a shell from the start menu direcly and check again. Now go to control panel -> system and add a variable. Restart the two shells (but not the explorer) and note the difference.

To get the "root" environment (the one that is created at login) you could read the keys at HKCU/Environment. But that's probably hacky, too.

PMF
  • 14,535
  • 3
  • 23
  • 49
0

You may need something like the modified_environ context manager describe in this question to set/restore the environment variables.

I suggest you to wrap your main function with this context manager:

with modified_environ():
    main()

That way, everything will be restored at function completion.

You can also wrap the imports:

with modified_environ():
    import pkg.which.can.change.env
    main()
Community
  • 1
  • 1
Laurent LAPORTE
  • 21,958
  • 6
  • 58
  • 103