#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.html2. @GrandOpener Обратите внимание, что
setx
усекает путь до 1024 символов, потенциально повреждая его. С другой стороны, ответ Сезара не приводит к этой (потенциально катастрофической) ошибке.3. @NickBadger Это отличное наблюдение, но мы должны выбрать наши битвы. Если длина ПУТИ превысит 2047, вам придется иметь дело с целым рядом потенциально катастрофических проблем. Любое решение, которое должно быть устойчивым к очень необычному вводу, должно проверять ввод, прежде чем пытаться его куда-то записать. Я думаю, что всем, кто работает над чем-то подобным, полезно знать об обоих вариантах.
Ответ №2:
Distutils не устанавливает переменные среды. В Windows это означало бы вмешательство в реестр; в UNIX для этого потребуется найти правильный файл конфигурации оболочки (что нетривиально) и отредактировать его, что просто не делается в этой культуре: людям говорят редактировать свой $PATH или использовать полные пути к программам.