将单个 python 可执行文件添加到多台计算机的 windows 系统路径?

Adding a single python executable to windows system PATH for multiple computers?

我已经创建了一个命令行程序,我想分发给一些工作人员。让他们都安装 python 解释器是不现实的。因此,我使用 PyInstaller 创建了一个 .exe 文件。然而,我开始意识到,大多数人甚至不知道如何导航到 .exe 所在的目录,以便调用它。 (到目前为止,我还没有弄清楚如何在单击时让程序进入 运行。)有没有办法让程序在 [=15] 时将其自身添加到用户系统路径中=] 第一次还是需要安装程序?谢谢!

常见的陷阱是读取 PATH 环境。使用 os.environ('PATH') 变量。那将是一个 错误,因为此变量包含混合在一起的用户和系统路径。这是 PATH 变量的特例。

你需要做的是从注册表(用户部分)获取PATH环境变量,如果需要更新它,然后写回。

您可以使用 winreg 模块,修改用户 PATH 环境变量(如果该特定用户不存在则创建)

  • 读取用户PATH变量
  • 如果存在,标记路径(否则,路径列表默认为空)
  • 计算当前模块的路径(使用os.path.dirname(__file__)
  • 检查是否已经在路径中,如果是,退出(我在这种情况下打印路径列表,以便您测试)
  • create/update PATH 用户环境。如有必要,使用更新的路径列表变量

代码:

import winreg,os

script_directory = os.path.dirname(__file__)

paths = []
key_type = winreg.REG_EXPAND_SZ  # default if PATH doesn't exist
try:
    keyQ = winreg.OpenKey(winreg.HKEY_CURRENT_USER, 'Environment', 0, winreg.KEY_QUERY_VALUE)
    path_old, key_type = winreg.QueryValueEx(keyQ, "PATH")
    winreg.CloseKey(keyQ)
    paths = path_old.split(os.pathsep)
except WindowsError:
    pass

if script_directory in paths:
    # already set, do nothing
    print(paths)
else:
    # add the new path
    paths.append(script_directory)
    # change registry
    keyQ = winreg.OpenKey(winreg.HKEY_CURRENT_USER, 'Environment', 0, winreg.KEY_WRITE)
    winreg.SetValueEx(keyQ, 'PATH', 0, key_type, os.pathsep.join(paths))
    winreg.CloseKey(keyQ)

请注意,用户必须 logoff/logon 才能使更改生效。另一种解决方案是在 PATH 变量上调用 setx。系统调用,丑陋,但立即生效。

# change registry with immediate effect
import subprocess
subprocess.call(["setx","PATH",os.pathsep.join(paths)])

或者,感谢 eryksun,一些 python 代码将注册表更改传播到新进程。无需注销,无需丑陋 setx,只需使用以下代码调用 broadcast_change('Environment')

import ctypes

user32 = ctypes.WinDLL('user32', use_last_error=True)

HWND_BROADCAST = 0xFFFF
WM_SETTINGCHANGE = 0x001A
SMTO_ABORTIFHUNG = 0x0002
ERROR_TIMEOUT = 0x05B4

def broadcast_change(lparam):
    result = user32.SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE,
                0, ctypes.c_wchar_p(lparam), SMTO_ABORTIFHUNG, 1000, None)
    if not result:
        err = ctypes.get_last_error()
        if err != ERROR_TIMEOUT:
            raise ctypes.WinError(err)

(看来我必须用最后一点重构我自己的一些代码 :))

环境。变量读取代码取自这里: