|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
__all__ = ['remove_from_system_path', 'add_to_system_path', |
|
'broadcast_environment_settings_change'] |
|
|
|
import ctypes |
|
import os |
|
import re |
|
import sys |
|
from ctypes import wintypes |
|
from os import path |
|
|
|
if sys.version_info[0] >= 3: |
|
import winreg as reg |
|
else: |
|
import _winreg as reg |
|
|
|
|
|
if sys.stdout and sys.stdout.write: |
|
out = sys.stdout.write |
|
err = sys.stderr.write |
|
else: |
|
OutputDebugString = ctypes.windll.kernel32.OutputDebugStringW |
|
OutputDebugString.argtypes = [ctypes.c_wchar_p] |
|
|
|
def out(x): |
|
OutputDebugString('_nsis.py: ' + x) |
|
|
|
def err(x): |
|
OutputDebugString('_nsis.py: Error: ' + x) |
|
|
|
HWND_BROADCAST = 0xffff |
|
WM_SETTINGCHANGE = 0x001A |
|
SMTO_ABORTIFHUNG = 0x0002 |
|
SendMessageTimeout = ctypes.windll.user32.SendMessageTimeoutW |
|
SendMessageTimeout.restype = None |
|
SendMessageTimeout.argtypes = [wintypes.HWND, wintypes.UINT, wintypes.WPARAM, |
|
wintypes.LPCWSTR, wintypes.UINT, wintypes.UINT, |
|
ctypes.POINTER(wintypes.DWORD)] |
|
|
|
|
|
def sz_expand(value, value_type): |
|
if value_type == reg.REG_EXPAND_SZ: |
|
return reg.ExpandEnvironmentStrings(value) |
|
else: |
|
return value |
|
|
|
|
|
def remove_from_system_path(pathname, allusers=True, path_env_var='PATH'): |
|
"""Removes all entries from the path which match the value in 'pathname' |
|
|
|
You must call broadcast_environment_settings_change() after you are finished |
|
manipulating the environment with this and other functions. |
|
|
|
For example, |
|
# Remove Anaconda from PATH |
|
remove_from_system_path('C:\\Anaconda') |
|
broadcast_environment_settings_change() |
|
""" |
|
pathname = path.normcase(path.normpath(pathname)) |
|
|
|
envkeys = [(reg.HKEY_CURRENT_USER, r'Environment')] |
|
if allusers: |
|
envkeys.append((reg.HKEY_LOCAL_MACHINE, |
|
r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment')) |
|
for root, keyname in envkeys: |
|
key = reg.OpenKey(root, keyname, 0, |
|
reg.KEY_QUERY_VALUE | reg.KEY_SET_VALUE) |
|
reg_value = None |
|
try: |
|
reg_value = reg.QueryValueEx(key, path_env_var) |
|
except WindowsError: |
|
|
|
|
|
reg.CloseKey(key) |
|
continue |
|
|
|
try: |
|
any_change = False |
|
results = [] |
|
for v in reg_value[0].split(os.pathsep): |
|
vexp = sz_expand(v, reg_value[1]) |
|
|
|
|
|
if path.normcase(path.normpath(vexp)) == pathname: |
|
any_change = True |
|
else: |
|
|
|
results.append(v) |
|
|
|
modified_path = os.pathsep.join(results) |
|
if any_change: |
|
reg.SetValueEx(key, path_env_var, 0, reg_value[1], modified_path) |
|
except Exception: |
|
|
|
|
|
reg.CloseKey(key) |
|
|
|
|
|
def add_to_system_path(paths, allusers=True, path_env_var='PATH'): |
|
"""Adds the requested paths to the system PATH variable. |
|
|
|
You must call broadcast_environment_settings_change() after you are finished |
|
manipulating the environment with this and other functions. |
|
|
|
""" |
|
|
|
if not issubclass(type(paths), list): |
|
paths = [paths] |
|
|
|
|
|
|
|
new_paths = None |
|
for p in paths: |
|
p = path.abspath(p) |
|
if new_paths: |
|
new_paths = new_paths + os.pathsep + p |
|
else: |
|
new_paths = p |
|
|
|
if allusers: |
|
|
|
root, keyname = (reg.HKEY_LOCAL_MACHINE, |
|
r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment') |
|
else: |
|
|
|
root, keyname = (reg.HKEY_CURRENT_USER, r'Environment') |
|
|
|
key = reg.OpenKey(root, keyname, 0, |
|
reg.KEY_QUERY_VALUE | reg.KEY_SET_VALUE) |
|
|
|
reg_type = None |
|
reg_value = None |
|
try: |
|
try: |
|
reg_value = reg.QueryValueEx(key, path_env_var) |
|
except WindowsError: |
|
|
|
|
|
|
|
reg_type = reg.REG_EXPAND_SZ |
|
final_value = new_paths |
|
else: |
|
|
|
|
|
|
|
reg_type = reg_value[1] |
|
final_value = new_paths + os.pathsep + reg_value[0] |
|
|
|
final_value = re.sub(r'([\;])+', r'\1', final_value) |
|
|
|
final_value = re.sub(r'\;$', '', final_value) |
|
|
|
final_value = final_value.replace('"', '') |
|
|
|
directories = final_value.split(';') |
|
for directory in directories: |
|
if '%' not in directory and not os.path.exists(directory): |
|
out("WARNING: Old PATH entry '%s' does not exist\n" % (directory)) |
|
reg.SetValueEx(key, path_env_var, 0, reg_type, final_value) |
|
|
|
finally: |
|
reg.CloseKey(key) |
|
|
|
|
|
def _reg_query_sub_keys(handle, key, keylist=[]): |
|
reghandle = reg.OpenKey(handle, key, 0, reg.KEY_READ) |
|
try: |
|
i = 0 |
|
while True: |
|
subkey = reg.EnumKey(reghandle, i) |
|
i += 1 |
|
_reg_query_sub_keys(handle, key + subkey + "\\", keylist) |
|
except WindowsError as ex: |
|
if ex.winerror == 259: |
|
keylist.append(key) |
|
finally: |
|
reg.CloseKey(reghandle) |
|
|
|
|
|
def get_previous_install_prefixes(pyversion, arch, allusers=True): |
|
"""Returns a list of prefixes for all old installations of this arch so that |
|
they can be removed from PATH if present. Note, it would be preferable to |
|
uninstall them properly instead. |
|
""" |
|
if allusers: |
|
|
|
key, subkey = (reg.HKEY_LOCAL_MACHINE, |
|
r'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\') |
|
else: |
|
|
|
key, subkey = (reg.HKEY_CURRENT_USER, |
|
r'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\') |
|
|
|
keylist = [] |
|
|
|
regex = re.compile(r'Python \S+ \(\S+conda[0-9]+ \S+ '+arch+r'\)') |
|
_reg_query_sub_keys(key, subkey, keylist) |
|
results = [] |
|
for uninstsubkey in keylist: |
|
final_part = os.path.basename(uninstsubkey.rstrip('\\')) |
|
if regex.match(final_part): |
|
try: |
|
with reg.OpenKeyEx(key, uninstsubkey, 0, |
|
reg.KEY_QUERY_VALUE) as keyhandle: |
|
reg_value = reg.QueryValueEx(keyhandle, 'UninstallString') |
|
results.append(os.path.dirname(re.sub(r'^"|"$', '', reg_value[0]))) |
|
except Exception: |
|
pass |
|
return results |
|
|
|
|
|
def broadcast_environment_settings_change(): |
|
"""Broadcasts to the system indicating that master environment variables have changed. |
|
|
|
This must be called after using the other functions in this module to |
|
manipulate environment variables. |
|
""" |
|
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, u'Environment', |
|
SMTO_ABORTIFHUNG, 5000, ctypes.pointer(wintypes.DWORD())) |
|
|