How to Make Environment Variable Changes Stick in Python

How do I make environment variable changes stick in Python?

Under Windows it's possible for you to make changes to environment variables persistent via the registry with this recipe, though it seems like overkill.

To echo Brian's question, what are you trying to accomplish? There is probably an easier way.

Interface for modifying Windows environment variables from Python

Using setx has few drawbacks, especially if you're trying to append to environment variables (eg. setx PATH %Path%;C:\mypath) This will repeatedly append to the path every time you run it, which can be a problem. Worse, it doesn't distinguish between the machine path (stored in HKEY_LOCAL_MACHINE), and the user path, (stored in HKEY_CURRENT_USER). The environment variable you see at a command prompt is made up of a concatenation of these two values. Hence, before calling setx:

user PATH == u
machine PATH == m
%PATH% == m;u

> setx PATH %PATH%;new

Calling setx sets the USER path by default, hence now:
user PATH == m;u;new
machine PATH == m
%PATH% == m;m;u;new

The system path is unavoidably duplicated in the %PATH% environment variable every time you call setx to append to PATH. These changes are permanent, never reset by reboots, and so accumulate through the life of the machine.

Trying to compensate for this in DOS is beyond my ability. So I turned to Python. The solution I have come up with today, to set environment variables by tweaking the registry, including appending to PATH without introducing duplicates, is as follows:

from os import system, environ
import win32con
from win32gui import SendMessage
from _winreg import (
CloseKey, OpenKey, QueryValueEx, SetValueEx,
HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE,
KEY_ALL_ACCESS, KEY_READ, REG_EXPAND_SZ, REG_SZ
)

def env_keys(user=True):
if user:
root = HKEY_CURRENT_USER
subkey = 'Environment'
else:
root = HKEY_LOCAL_MACHINE
subkey = r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'
return root, subkey

def get_env(name, user=True):
root, subkey = env_keys(user)
key = OpenKey(root, subkey, 0, KEY_READ)
try:
value, _ = QueryValueEx(key, name)
except WindowsError:
return ''
return value

def set_env(name, value):
key = OpenKey(HKEY_CURRENT_USER, 'Environment', 0, KEY_ALL_ACCESS)
SetValueEx(key, name, 0, REG_EXPAND_SZ, value)
CloseKey(key)
SendMessage(
win32con.HWND_BROADCAST, win32con.WM_SETTINGCHANGE, 0, 'Environment')

def remove(paths, value):
while value in paths:
paths.remove(value)

def unique(paths):
unique = []
for value in paths:
if value not in unique:
unique.append(value)
return unique

def prepend_env(name, values):
for value in values:
paths = get_env(name).split(';')
remove(paths, '')
paths = unique(paths)
remove(paths, value)
paths.insert(0, value)
set_env(name, ';'.join(paths))

def prepend_env_pathext(values):
prepend_env('PathExt_User', values)
pathext = ';'.join([
get_env('PathExt_User'),
get_env('PathExt', user=False)
])
set_env('PathExt', pathext)

set_env('Home', '%HomeDrive%%HomePath%')
set_env('Docs', '%HomeDrive%%HomePath%\docs')
set_env('Prompt', '$P$_$G$S')

prepend_env('Path', [
r'%SystemDrive%\cygwin\bin', # Add cygwin binaries to path
r'%HomeDrive%%HomePath%\bin', # shortcuts and 'pass-through' bat files
r'%HomeDrive%%HomePath%\docs\bin\mswin', # copies of standalone executables
])

# allow running of these filetypes without having to type the extension
prepend_env_pathext(['.lnk', '.exe.lnk', '.py'])

It does not affect the current process or the parent shell, but it will affect all cmd windows opened after it is run, without needing a reboot, and can safely be edited and re-run many times without introducing any duplicates.

Environment variable gets removed immediately after it is set Python

You are only setting the environment variable for that specific running Python process. Once the process exits, the variable ceases to exist. It appears you cannot set a persistent environment variable from Python, as is described here:

Why can't environmental variables set in python persist?

It looks like you'll need to shell execute whatever variable you want to persist, as is described in the second answer to the above questions, like so:

import pipes
import random
r = random.randint(1,100)
print("export BLAHBLAH=%s" % (pipes.quote(str(r))))

Edit:

As per @SuperStormer's comment below--they are correct--this solution is Unix/Mac specifc. If you need to accomplish the same thing on Windows you would utilize setx like so (this would set the variable for only current user, you need to add a /m switch on the end to set for the local machine):

import pipes
import random
r = random.randint(1,100)
print("setx BLAHBLAH %s" % (pipes.quote(str(r))))

Can a python script persistently change a Windows environment variable? (elegantly)

Your long-winded solution is probably the best idea; I don't believe this is possible from Python directly. This article suggests another way, using a temporary batch file:

http://code.activestate.com/recipes/159462/

How to add to and remove from system's environment variable PATH?

import _winreg as reg
import win32gui
import win32con

# read the value
key = reg.OpenKey(reg.HKEY_CURRENT_USER, 'Environment', 0, reg.KEY_ALL_ACCESS)
# use this if you need to modify the system variable and if you have admin privileges
#key = reg.OpenKey(reg.HKEY_LOCAL_MACHINE, r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment', 0, reg.KEY_ALL_ACCESS)
try
value, _ = reg.QueryValueEx(key, 'PATH')
except WindowsError:
# in case the PATH variable is undefined
value = ''

# modify it
value = ';'.join([s for s in value.split(';') if not r'\myprogram' in s])

# write it back
reg.SetValueEx(key, 'PATH', 0, reg.REG_EXPAND_SZ, value)
reg.CloseKey(key)

# notify the system about the changes
win32gui.SendMessage(win32con.HWND_BROADCAST, win32con.WM_SETTINGCHANGE, 0, 'Environment')

os.environ not setting environment variables

os.environ[...] = ... sets the environment variable only for the duration of the python process (or its child processes).

It is not easily (i.e. without employing OS-specific tools) possible and surely not advisable to set the variable for the shell from which you run Python. See aumo's comment for alternative and somewhat obscure approaches to the problem.

how do I modify the system path variable in python script?

You shouldn't need to set the PATH from within the python script.
Instead, put something like

USER=joe
HOME=/home/joe
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/some/other/path
PYTHONPATH=/home/joe/pybin
MAILTO=joe
LANG=en_US.UTF-8

#min hr day mon dow
*/5 12 * * * reminder.py 'Eat lunch'

at the top of your crontab. These environment variables will then be available to all cron jobs run through your crontab.



Related Topics



Leave a reply



Submit