Skip to content

Commit

Permalink
Try to detect if we are in a virtual environment and change path prec…
Browse files Browse the repository at this point in the history
…edence accordingly (#286)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
jasongrout and pre-commit-ci[bot] committed Aug 24, 2022
1 parent 71d36ed commit 2976226
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 11 deletions.
6 changes: 3 additions & 3 deletions jupyter_core/command.py
Expand Up @@ -231,13 +231,13 @@ def main():
if args.debug:
env = os.environ

if paths.envset("JUPYTER_PREFER_ENV_PATH"):
if paths.prefer_environment_over_user():
print(
"JUPYTER_PREFER_ENV_PATH is set, making the environment-level path preferred over the user-level path for data and config"
"JUPYTER_PREFER_ENV_PATH is set to a true value, or JUPYTER_PREFER_ENV_PATH is not set and we detected a virtual environment, making the environment-level path preferred over the user-level path for data and config"
)
else:
print(
"JUPYTER_PREFER_ENV_PATH is not set, making the user-level path preferred over the environment-level path for data and config"
"JUPYTER_PREFER_ENV_PATH is set to a false value, or JUPYTER_PREFER_ENV_PATH is not set and we did not detect a virtual environment, making the user-level path preferred over the environment-level path for data and config"
)

# config path list
Expand Down
32 changes: 27 additions & 5 deletions jupyter_core/paths.py
Expand Up @@ -26,13 +26,18 @@
UF_HIDDEN = getattr(stat, "UF_HIDDEN", 32768)


def envset(name):
"""Return True if the given environment variable is set
def envset(name, default=False):
"""Return the boolean value of a given environment variable.
An environment variable is considered set if it is assigned to a value
other than 'no', 'n', 'false', 'off', '0', or '0.0' (case insensitive)
If the environment variable is not defined, the default value is returned.
"""
return os.environ.get(name, "no").lower() not in ["no", "n", "false", "off", "0", "0.0"]
if name not in os.environ:
return default

return os.environ[name].lower() not in ["no", "n", "false", "off", "0", "0.0"]


def get_home_dir():
Expand All @@ -47,6 +52,23 @@ def get_home_dir():
_dtemps: dict = {}


def prefer_environment_over_user():
"""Determine if environment-level paths should take precedence over user-level paths."""
# If JUPYTER_PREFER_ENV_PATH is defined, that signals user intent, so return its value
if "JUPYTER_PREFER_ENV_PATH" in os.environ:
return envset("JUPYTER_PREFER_ENV_PATH")

# If we are in a Python virtualenv, default to True (see https://docs.python.org/3/library/venv.html#venv-def)
if sys.prefix != sys.base_prefix:
return True

# If sys.prefix indicates Python comes from a conda/mamba environment, default to True
if "CONDA_PREFIX" in os.environ and sys.prefix.startswith(os.environ["CONDA_PREFIX"]):
return True

return False


def _mkdtemp_once(name):
"""Make or reuse a temporary directory.
Expand Down Expand Up @@ -184,7 +206,7 @@ def jupyter_path(*subdirs):

env = [p for p in ENV_JUPYTER_PATH if p not in SYSTEM_JUPYTER_PATH]

if envset("JUPYTER_PREFER_ENV_PATH"):
if prefer_environment_over_user():
paths.extend(env)
paths.extend(user)
else:
Expand Down Expand Up @@ -253,7 +275,7 @@ def jupyter_config_path():

env = [p for p in ENV_CONFIG_PATH if p not in SYSTEM_CONFIG_PATH]

if envset("JUPYTER_PREFER_ENV_PATH"):
if prefer_environment_over_user():
paths.extend(env)
paths.extend(user)
else:
Expand Down
1 change: 0 additions & 1 deletion jupyter_core/tests/test_command.py
Expand Up @@ -23,7 +23,6 @@

def setup_module():
resetenv.start()
os.environ.pop("JUPYTER_PREFER_ENV_PATH", None)


def teardown_module():
Expand Down
26 changes: 24 additions & 2 deletions jupyter_core/tests/test_paths.py
Expand Up @@ -25,6 +25,7 @@
jupyter_data_dir,
jupyter_path,
jupyter_runtime_dir,
prefer_environment_over_user,
secure_write,
)

Expand Down Expand Up @@ -61,13 +62,16 @@
jupyter_config_env = "/jupyter-cfg"
config_env = patch.dict("os.environ", {"JUPYTER_CONFIG_DIR": jupyter_config_env})
prefer_env = patch.dict("os.environ", {"JUPYTER_PREFER_ENV_PATH": "True"})
prefer_user = patch.dict("os.environ", {"JUPYTER_PREFER_ENV_PATH": "False"})

resetenv = patch.dict(os.environ)


def setup_module():
resetenv.start()
os.environ.pop("JUPYTER_PREFER_ENV_PATH", None)
# For these tests, default to preferring the user-level over environment-level paths
# Tests can override this preference using the prefer_env decorator/context manager
os.environ["JUPYTER_PREFER_ENV_PATH"] = "no"


def teardown_module():
Expand All @@ -89,7 +93,10 @@ def test_envset():
assert paths.envset(f"FOO_{v}")
for v in false_values:
assert not paths.envset(f"FOO_{v}")
assert not paths.envset("THIS_VARIABLE_SHOULD_NOT_BE_SET")
# Test default value is False
assert paths.envset("THIS_VARIABLE_SHOULD_NOT_BE_SET") is False
# Test envset returns the given default if supplied
assert paths.envset("THIS_VARIABLE_SHOULD_NOT_BE_SET", None) is None


@pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows")
Expand Down Expand Up @@ -328,6 +335,21 @@ def test_jupyter_config_path_env():
assert path[:2] == [pjoin("foo", "bar"), pjoin("bar", "baz")]


def test_prefer_environment_over_user():
with prefer_env:
assert prefer_environment_over_user() is True

with prefer_user:
assert prefer_environment_over_user() is False

# Test default if environment variable is not set, and try to determine if we are in a virtual environment
os.environ.pop("JUPYTER_PREFER_ENV_PATH", None)
in_venv = sys.prefix != sys.base_prefix or (
"CONDA_PREFIX" in os.environ and sys.prefix.startswith(os.environ["CONDA_PREFIX"])
)
assert prefer_environment_over_user() is in_venv


def test_is_hidden():
with tempfile.TemporaryDirectory() as root:
subdir1 = os.path.join(root, "subdir")
Expand Down

0 comments on commit 2976226

Please sign in to comment.