# -*- coding: utf-8 -*-
#
# Copyright © Spyder Project Contributors
# Licensed under the terms of the MIT License
# (see spyder/__init__.py for details)
"""
Environment variable utilities.
"""
# Standard library imports
import os
# Third party imports
from qtpy.QtWidgets import QDialog, QMessageBox
# Local imports
from spyder.config.base import _
from spyder.widgets.collectionseditor import CollectionsEditor
from spyder.py3compat import PY2, iteritems, to_text_string, to_binary_string
from spyder.utils.icon_manager import ima
from spyder.utils.encoding import to_unicode_from_fs
def envdict2listdict(envdict):
"""Dict --> Dict of lists"""
sep = os.path.pathsep
for key in envdict:
if sep in envdict[key]:
envdict[key] = [path.strip() for path in envdict[key].split(sep)]
return envdict
def listdict2envdict(listdict):
"""Dict of lists --> Dict"""
for key in listdict:
if isinstance(listdict[key], list):
listdict[key] = os.path.pathsep.join(listdict[key])
return listdict
def clean_env(env_vars):
"""
Remove non-ascii entries from a dictionary of environments variables.
The values will be converted to strings or bytes (on Python 2). If an
exception is raised, an empty string will be used.
"""
new_env_vars = env_vars.copy()
for key, var in iteritems(env_vars):
if PY2:
# Try to convert vars first to utf-8.
try:
unicode_var = to_text_string(var)
except UnicodeDecodeError:
# If that fails, try to use the file system
# encoding because one of our vars is our
# PYTHONPATH, and that contains file system
# directories
try:
unicode_var = to_unicode_from_fs(var)
except Exception:
# If that also fails, make the var empty
# to be able to start Spyder.
# See https://stackoverflow.com/q/44506900/438386
# for details.
unicode_var = ''
new_env_vars[key] = to_binary_string(unicode_var, encoding='utf-8')
else:
new_env_vars[key] = to_text_string(var)
return new_env_vars
class RemoteEnvDialog(CollectionsEditor):
"""Remote process environment variables dialog."""
def __init__(self, environ, parent=None):
super(RemoteEnvDialog, self).__init__(parent)
try:
self.setup(
envdict2listdict(environ),
title=_("Environment variables"),
readonly=True,
icon=ima.icon('environ')
)
except Exception as e:
QMessageBox.warning(
parent,
_("Warning"),
_("An error occurred while trying to show your "
"environment variables. The error was
"
"{0}").format(e),
QMessageBox.Ok
)
class EnvDialog(RemoteEnvDialog):
"""Environment variables Dialog"""
def __init__(self, parent=None):
RemoteEnvDialog.__init__(self, dict(os.environ), parent=parent)
# For Windows only
try:
from spyder.py3compat import winreg
def get_user_env():
"""Return HKCU (current user) environment variables"""
reg = dict()
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Environment")
for index in range(0, winreg.QueryInfoKey(key)[1]):
try:
value = winreg.EnumValue(key, index)
reg[value[0]] = value[1]
except:
break
return envdict2listdict(reg)
def set_user_env(reg, parent=None):
"""Set HKCU (current user) environment variables"""
reg = listdict2envdict(reg)
types = dict()
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Environment")
for name in reg:
try:
_x, types[name] = winreg.QueryValueEx(key, name)
except WindowsError:
types[name] = winreg.REG_EXPAND_SZ
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Environment", 0,
winreg.KEY_SET_VALUE)
for name in reg:
winreg.SetValueEx(key, name, 0, types[name], reg[name])
try:
from win32gui import SendMessageTimeout
from win32con import (HWND_BROADCAST, WM_SETTINGCHANGE,
SMTO_ABORTIFHUNG)
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0,
"Environment", SMTO_ABORTIFHUNG, 5000)
except Exception:
QMessageBox.warning(parent, _("Warning"),
_("Module pywin32 was not found.
"
"Please restart this Windows session "
"(not the computer) for changes to take effect."))
class WinUserEnvDialog(CollectionsEditor):
"""Windows User Environment Variables Editor"""
def __init__(self, parent=None):
super(WinUserEnvDialog, self).__init__(parent)
self.setup(get_user_env(),
title=r"HKEY_CURRENT_USER\Environment")
if parent is None:
parent = self
QMessageBox.warning(parent, _("Warning"),
_("If you accept changes, "
"this will modify the current user environment "
"variables directly in Windows registry. "
"Use it with precautions, at your own risks.
"
"
Note that for changes to take effect, you will "
"need to restart the parent process of this applica"
"tion (simply restart Spyder if you have executed it "
"from a Windows shortcut, otherwise restart any "
"application from which you may have executed it, "
"like Python(x,y) Home for example)"))
def accept(self):
"""Reimplement Qt method"""
set_user_env(listdict2envdict(self.get_value()), parent=self)
QDialog.accept(self)
except Exception:
pass
def main():
"""Run Windows environment variable editor"""
from spyder.utils.qthelpers import qapplication
app = qapplication()
if os.name == 'nt':
dialog = WinUserEnvDialog()
else:
dialog = EnvDialog()
dialog.show()
app.exec_()
if __name__ == "__main__":
main()