Изменение переменной среды `PATH` глобально и постоянно с помощью Python

#python #cross-platform #environment-variables #distutils

#python #кросс-платформенный #переменные среды #distutils

Вопрос:

Возможно ли изменить PATH переменную среды глобально и постоянно, независимым от платформы способом, используя Python (distutils)?

Предыстория

У меня есть какое-то приложение (плагин для редактора XML Serna), и теперь я собираюсь создать для него установщик, возможно, используя python distutils (setup.py ). После установки setup.py необходимо изменить переменную среды PATH, чтобы добавить каталог установки к ее значению.

Возможным решением для достижения того, чего я хочу, было бы скопировать исполняемые файлы в /usr/local/bin или куда-нибудь еще, но для MS Windows не очевидно, куда копировать execs.

Есть идеи?

Ответ №1:

Насколько я знаю, у distutils нет кроссплатформенной утилиты для постоянного изменения переменных среды. Поэтому вам придется писать код, зависящий от платформы.

В Windows переменные среды хранятся в реестре. Это пример кода для чтения и установки некоторых из его ключей. Для этого я использую только стандартную библиотеку (не нужно устанавливать pywin32!).

 import _winreg as winreg
import ctypes

ENV_HTTP_PROXY = u'http://87.254.212.121:8080'


class Registry(object):
    def __init__(self, key_location, key_path):
        self.reg_key = winreg.OpenKey(key_location, key_path, 0, winreg.KEY_ALL_ACCESS)

    def set_key(self, name, value):
        try:
            _, reg_type = winreg.QueryValueEx(self.reg_key, name)
        except WindowsError:
            # If the value does not exists yet, we (guess) use a string as the
            # reg_type
            reg_type = winreg.REG_SZ
        winreg.SetValueEx(self.reg_key, name, 0, reg_type, value)

    def delete_key(self, name):
        try:
            winreg.DeleteValue(self.reg_key, name)
        except WindowsError:
            # Ignores if the key value doesn't exists
            pass



class EnvironmentVariables(Registry):
    """
    Configures the HTTP_PROXY environment variable, it's used by the PIP proxy
    """

    def __init__(self):
        super(EnvironmentVariables, self).__init__(winreg.HKEY_LOCAL_MACHINE,
                                                   r'SYSTEMCurrentControlSetControlSession ManagerEnvironment')

    def on(self):
        self.set_key('HTTP_PROXY', ENV_HTTP_PROXY)
        self.refresh()

    def off(self):
        self.delete_key('HTTP_PROXY')
        self.refresh()

    def refresh(self):
        HWND_BROADCAST = 0xFFFF
        WM_SETTINGCHANGE = 0x1A

        SMTO_ABORTIFHUNG = 0x0002

        result = ctypes.c_long()
        SendMessageTimeoutW = ctypes.windll.user32.SendMessageTimeoutW
        SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, u'Environment', SMTO_ABORTIFHUNG, 5000, ctypes.byref(result));
 

Это всего лишь пример кода для начала, он реализует только настройки и ключи удаления.

Убедитесь, что вы всегда вызываете метод обновления после изменения реестра. Это сообщит Windows, что что-то изменилось, и обновит настройки реестра.

Вот ссылка на полное приложение, которое я написал, его прокси-переключатель для Windows: https://bitbucket.org/canassa/switch-proxy /

Комментарии:

1. Это отличный ответ, но также обратите внимание, что если это одноразовая модификация среды, вам, вероятно, будет так же удобно использовать subprocess для внесения изменений с помощью команды оболочки. Это будет значительно меньше кода за счет открытия нового процесса. Вот ссылка на команду, которую вы хотели бы использовать для Windows ( setx ): ss64.com/nt/setx.html

2. @GrandOpener Обратите внимание, что setx усекает путь до 1024 символов, потенциально повреждая его. С другой стороны, ответ Сезара не приводит к этой (потенциально катастрофической) ошибке.

3. @NickBadger Это отличное наблюдение, но мы должны выбрать наши битвы. Если длина ПУТИ превысит 2047, вам придется иметь дело с целым рядом потенциально катастрофических проблем. Любое решение, которое должно быть устойчивым к очень необычному вводу, должно проверять ввод, прежде чем пытаться его куда-то записать. Я думаю, что всем, кто работает над чем-то подобным, полезно знать об обоих вариантах.

Ответ №2:

Distutils не устанавливает переменные среды. В Windows это означало бы вмешательство в реестр; в UNIX для этого потребуется найти правильный файл конфигурации оболочки (что нетривиально) и отредактировать его, что просто не делается в этой культуре: людям говорят редактировать свой $PATH или использовать полные пути к программам.