Interface for modifying Windows environment variables from Python

后端 未结 6 1928
无人共我
无人共我 2020-12-08 05:01

How can I persistently modify the Windows environment variables from a Python script? (it\'s the setup.py script)

I\'m looking for a standard function or module to u

6条回答
  •  無奈伤痛
    2020-12-08 05:59

    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.

提交回复
热议问题