extending PATH on Windows?
eryk sun
eryksun at gmail.com
Thu Feb 18 07:54:16 EST 2016
On Wed, Feb 17, 2016 at 6:53 PM, Dennis Lee Bieber
<wlfraed at ix.netcom.com> wrote:
> On Wed, 17 Feb 2016 17:49:11 +0000 (UTC), Ulli Horlacher
> <framstag at rus.uni-stuttgart.de> declaimed the following:
>
>>Thorsten Kampe <thorsten at thorstenkampe.de> wrote:
>>
>>> By the way: there is a script called `win_add2path.py` in your Python
>>> distribution
>>
>>I have
>>"Python 2.7.11 (v2.7.11:6d1b6a68f775, Dec 5 2015, 20:32:19) [MSC
>>v.1500 32 bit (Intel)] on win32"
>>and there is no "win_add2path.py"
>>
> C:\Python_x64\Python27\Tools\scripts\win_add2path.py
>
> PythonWin 2.7.5 (default, Sep 16 2013, 23:11:01) [MSC v.1500 64 bit
> (AMD64)] on win32.
> Portions Copyright 1994-2008 Mark Hammond - see 'Help/About PythonWin' for
> further copyright information.
>
> But I can't confirm that this is not something added in the ActiveState
> release.
It's here:
https://hg.python.org/cpython/file/v2.7.11/Tools/scripts/win_add2path.py
But there are a few issues with this script.
* A default value of u"%PATH%" is wrong. That causes the system
PATH to be concatenated with itself.
* For the user scripts directory, replacing appdata with
"%APPDATA%" causes os.path.isdir to always fail.
* For QueryValueEx, the only error it should handle is
ERROR_FILE_NOT_FOUND. Other OS errors are unlikely, but they
shouldn't be masked.
* In Python 2, the value returned by QueryValueEx is a unicode
string, which this script assumes is ASCII only. So it could
die on a UnicodeDecodeError.
* REG_EXPAND_SZ should only be used when '%' occurs 2 or more
times. Otherwise use REG_SZ. This is important for two-pass
environment variable expansion. Thus an existing REG_SZ type
should be preserved, if that's reasonable, because the user may be
reusing PATH in a REG_EXPAND_SZ variable.
* It doesn't broadcast a WM_SETTINGCHANGE message, so it
needlessly forces the user to log off and back on in order to
use the modified PATH.
Here's a new version for Python 2. I generalized the shell-variable
replacement to a list of well-known folders.
import os
import sys
import site
import ctypes
import _winreg
import warnings
user32 = ctypes.WinDLL('user32', use_last_error=True)
HWND_BROADCAST = 0xFFFF
WM_SETTINGCHANGE = 0x001A
SMTO_ABORTIFHUNG = 0x0002
ERROR_FILE_NOT_FOUND = 0x0002
ERROR_TIMEOUT = 0x05B4
HKCU = _winreg.HKEY_CURRENT_USER
ENVIRONMENT = u"Environment"
PATH = u"PATH"
SCRIPTS = u"Scripts"
dir_vars = [u"%APPDATA%",
u"%LOCALAPPDATA%",
u"%USERPROFILE%",
u"%HOMEDRIVE%%HOMEPATH%",
u"%ProgramFiles%",
u"%ProgramFiles(x86)%"]
dir_vals = [_winreg.ExpandEnvironmentStrings(v) for v in dir_vars]
def broadcast_change(lparam):
if not user32.SendMessageTimeoutW(
HWND_BROADCAST, WM_SETTINGCHANGE,
0, ctypes.c_wchar_p(lparam),
SMTO_ABORTIFHUNG, 1000, None):
err = ctypes.get_last_error()
if err != ERROR_TIMEOUT:
raise ctypes.WinError(err)
def modify():
executable = sys.executable.decode("mbcs")
pythonpath = os.path.dirname(os.path.normpath(executable))
scripts = os.path.join(pythonpath, SCRIPTS)
if hasattr(site, "USER_SITE"):
userpath = site.USER_SITE.decode("mbcs")
userscripts = os.path.join(userpath, SCRIPTS)
else:
userscripts = None
with _winreg.CreateKey(HKCU, ENVIRONMENT) as key:
try:
envpath, dtype = _winreg.QueryValueEx(key, PATH)
except WindowsError as e:
if e.winerror != ERROR_FILE_NOT_FOUND:
raise
envpath, dtype = u"", _winreg.REG_EXPAND_SZ
if dtype not in (_winreg.REG_SZ, _winreg.REG_EXPAND_SZ):
raise TypeError(r"Existing PATH value is not a string.")
if dtype == _winreg.REG_SZ and envpath.count(u"%") > 1:
warnings.warn('Changing type to REG_EXPAND_SZ.')
dtype = _winreg.REG_EXPAND_SZ
paths = [envpath]
for path in (pythonpath, scripts, userscripts):
if path and path not in envpath and os.path.isdir(path):
if dtype == _winreg.REG_EXPAND_SZ:
for var, val in zip(dir_vars, dir_vals):
if val in path:
path = path.replace(val, var)
break
if path in envpath:
continue
paths.append(path)
envpath = os.pathsep.join(paths)
_winreg.SetValueEx(key, PATH, 0, dtype, envpath)
broadcast_change(ENVIRONMENT)
return paths, envpath
def main():
paths, envpath = modify()
if len(paths) > 1:
print "Path(s) added:"
print '\n'.join(paths[1:])
else:
print "No path was added"
print "\nCurrent user PATH is now:\n%s\n" % envpath
print "Expanded:"
print _winreg.ExpandEnvironmentStrings(envpath)
if __name__ == '__main__':
main()
More information about the Python-list
mailing list