Skip to content

Commit

Permalink
Merge pull request #21065 from mrclary/issue-3891-env-var
Browse files Browse the repository at this point in the history
PR: Fix issue getting user environment variables on Posix systems and pass them to the IPython console
  • Loading branch information
ccordoba12 authored Jun 25, 2023
2 parents f8e5290 + 5f0cec7 commit 7ff772f
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 22 deletions.
1 change: 0 additions & 1 deletion installers-conda/resources/spyder-menu.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
"StartupWMClass": "Spyder"
},
"osx": {
"precommand": "eval \"$($SHELL -l -c \"declare -x\")\"",
"command": [
"$(dirname $0)/python",
"{{ PREFIX }}/bin/spyder",
Expand Down
10 changes: 7 additions & 3 deletions spyder/plugins/ipythonconsole/utils/kernelspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
SpyderKernelError)
from spyder.utils.conda import (add_quotes, get_conda_env_path, is_conda_env,
find_conda)
from spyder.utils.environ import clean_env
from spyder.utils.environ import clean_env, get_user_environment_variables
from spyder.utils.misc import get_python_executable
from spyder.utils.programs import is_python_interpreter, is_module_installed

Expand Down Expand Up @@ -171,7 +171,11 @@ def env(self):
"""Env vars for kernels"""
default_interpreter = self.get_conf(
'default', section='main_interpreter')
env_vars = os.environ.copy()

# Ensure that user environment variables are included, but don't
# override existing environ values
env_vars = get_user_environment_variables()
env_vars.update(os.environ)

# Avoid IPython adding the virtualenv on which Spyder is running
# to the kernel sys.path
Expand All @@ -193,7 +197,7 @@ def env(self):
# Environment variables that we need to pass to the kernel
env_vars.update({
'SPY_EXTERNAL_INTERPRETER': (not default_interpreter
or self.path_to_custom_interpreter),
or self.path_to_custom_interpreter),
'SPY_UMR_ENABLED': self.get_conf(
'umr/enabled', section='main_interpreter'),
'SPY_UMR_VERBOSE': self.get_conf(
Expand Down
73 changes: 55 additions & 18 deletions spyder/utils/environ.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
"""

# Standard library imports
from functools import lru_cache
import logging
import os
from pathlib import Path
import re
Expand All @@ -22,11 +24,46 @@
from qtpy.QtWidgets import QMessageBox

# Local imports
from spyder.config.base import _, running_in_ci
from spyder.config.base import _, running_in_ci, get_conf_path
from spyder.widgets.collectionseditor import CollectionsEditor
from spyder.utils.icon_manager import ima
from spyder.utils.programs import run_shell_command

logger = logging.getLogger(__name__)


@lru_cache
def _get_user_env_script():
"""
To get user environment variables from login and interactive startup files
on posix, both -l and -i flags must be used. Parsing the environment list
is problematic if the variable has a newline character, but Python's
os.environ can do this for us. However, executing Python in an interactive
shell in a subprocess causes Spyder to hang on Linux. Using a shell script
resolves the issue. Note that -i must be in the sha-bang line; if -i and
-l are swapped, Spyder will hang.
"""
script_text = None
shell = os.getenv('SHELL', '/bin/bash')
user_env_script = Path(get_conf_path()) / 'user-env.sh'

if Path(shell).name in ('bash', 'zsh'):
script_text = (
f"#!{shell} -i\n"
f"{shell} -l -c"
f" \"{sys.executable} -c 'import os; print(dict(os.environ))'\" "
f"\n"
)
else:
logger.info("Getting user environment variables is not supported "
"for shell '%s'", shell)

if script_text is not None:
user_env_script.write_text(script_text)
user_env_script.chmod(0o744) # Make it executable for the user

return str(user_env_script)


def envdict2listdict(envdict):
"""Dict --> Dict of lists"""
Expand Down Expand Up @@ -54,24 +91,24 @@ def get_user_environment_variables():
env_var : dict
Key-value pairs of environment variables.
"""
try:
if os.name == 'nt':
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Environment")
num_values = winreg.QueryInfoKey(key)[1]
env_var = dict(
[winreg.EnumValue(key, k)[:2] for k in range(num_values)]
)
else:
shell = os.environ.get("SHELL", "/bin/bash")
cmd = (
f"{shell} -l -c"
f""" "{sys.executable} -c 'import os; print(dict(os.environ))'" """
)
proc = run_shell_command(cmd, env={}, text=True)
env_var = {}
if os.name == 'nt':
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Environment")
num_values = winreg.QueryInfoKey(key)[1]
env_var = dict(
[winreg.EnumValue(key, k)[:2] for k in range(num_values)]
)
elif os.name == 'posix':
try:
user_env_script = _get_user_env_script()
proc = run_shell_command(user_env_script, env={}, text=True)
stdout, stderr = proc.communicate()
env_var = eval(stdout, None)
except Exception:
return {}
if stderr:
logger.info(stderr.strip())
if stdout:
env_var = eval(stdout, None)
except Exception as exc:
logger.info(exc)

return env_var

Expand Down

0 comments on commit 7ff772f

Please sign in to comment.