Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add site user base directory to config and data paths if site.ENABLE_USER_SITE is true #242

Merged
merged 11 commits into from
Oct 13, 2021
16 changes: 16 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
Changes in jupyter-core
=======================

4.9
---

4.9.0
~~~~~

`on
GitHub <https://github.com/jupyter/jupyter_core/releases/tag/4.9.0>`__

See the `jupyter_core
4.9 <https://github.com/jupyter/jupyter_core/milestone/21?closed=1>`__
milestone on GitHub for the full list of pull requests and issues closed.

- Add Python site user base subdirectories to config and data user-level paths if ``site.ENABLE_USER_SITE`` is True. One way to disable these directory additions is to set the ``PYTHONNOUSERSITE`` environment variable. These locations can be customized by setting the ``PYTHONUSERBASE`` environment variable. (:ghpull:`242`)


4.8
---

Expand Down
6 changes: 6 additions & 0 deletions jupyter_core/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import json
import os
from shutil import which
import site
import sys
import sysconfig
from subprocess import Popen
Expand Down Expand Up @@ -257,6 +258,11 @@ def main():
else:
print("JUPYTER_CONFIG_DIR is not set, so we use the default user-level config directory")

if site.ENABLE_USER_SITE:
print(f"Python's site.ENABLE_USER_SITE is True, so we add the user site directory '{site.getuserbase()}'")
else:
print(f"Python's site.ENABLE_USER_SITE is not True, so we do not add the Python site user directory '{site.getuserbase()}'")

# data path list
if env.get('JUPYTER_PATH'):
print(f"JUPYTER_PATH is set to '{env.get('JUPYTER_PATH')}', which is prepended to the data paths")
Expand Down
37 changes: 28 additions & 9 deletions jupyter_core/paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import sys
import stat
import errno
import site
import tempfile
import warnings
from pathlib import Path
Expand Down Expand Up @@ -141,6 +142,10 @@ def jupyter_path(*subdirs):
If the JUPYTER_PREFER_ENV_PATH environment variable is set, the environment-level
directories will have priority over user-level directories.

If the Python site.ENABLE_USER_SITE variable is True, we also add the
appropriate Python user site subdirectory to the user-level directories.


If ``*subdirs`` are given, that subdirectory will be added to each element.

Examples:
Expand All @@ -161,14 +166,19 @@ def jupyter_path(*subdirs):
)

# Next is environment or user, depending on the JUPYTER_PREFER_ENV_PATH flag
user = jupyter_data_dir()
user = [jupyter_data_dir()]
if site.ENABLE_USER_SITE:
userdir = os.path.join(site.getuserbase(), 'share', 'jupyter')
if userdir not in user:
user.append(userdir)

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

if envset('JUPYTER_PREFER_ENV_PATH'):
paths.extend(env)
paths.append(user)
paths.extend(user)
else:
paths.append(user)
paths.extend(user)
paths.extend(env)

# finally, system
Expand Down Expand Up @@ -197,9 +207,13 @@ def jupyter_path(*subdirs):

def jupyter_config_path():
"""Return the search path for Jupyter config files as a list.

If the JUPYTER_PREFER_ENV_PATH environment variable is set, the environment-level
directories will have priority over user-level directories.

If the JUPYTER_PREFER_ENV_PATH environment variable is set, the
environment-level directories will have priority over user-level
directories.

If the Python site.ENABLE_USER_SITE variable is True, we also add the
appropriate Python user site subdirectory to the user-level directories.
"""
if os.environ.get('JUPYTER_NO_CONFIG'):
# jupyter_config_dir makes a blank config when JUPYTER_NO_CONFIG is set.
Expand All @@ -215,14 +229,19 @@ def jupyter_config_path():
)

# Next is environment or user, depending on the JUPYTER_PREFER_ENV_PATH flag
user = jupyter_config_dir()
user = [jupyter_config_dir()]
if site.ENABLE_USER_SITE:
userdir = os.path.join(site.getuserbase(), 'etc', 'jupyter')
if userdir not in user:
user.append(userdir)

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

if envset('JUPYTER_PREFER_ENV_PATH'):
paths.extend(env)
paths.append(user)
paths.extend(user)
else:
paths.append(user)
paths.extend(user)
paths.extend(env)

# Finally, system path
Expand Down
9 changes: 9 additions & 0 deletions jupyter_core/tests/test_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@
)


resetenv = patch.dict(os.environ)

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

def teardown_module():
resetenv.stop()

def get_jupyter_output(cmd):
"""Get output of a jupyter command"""
if not isinstance(cmd, list):
Expand Down
64 changes: 58 additions & 6 deletions jupyter_core/tests/test_paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import tempfile
from unittest.mock import patch
import pytest
import site
import subprocess
import sys
import warnings
Expand Down Expand Up @@ -47,14 +48,24 @@

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'})

resetenv = patch.dict(os.environ)

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

def teardown_module():
resetenv.stop()



def realpath(path):
return os.path.abspath(os.path.realpath(os.path.expanduser(path)))

home_jupyter = realpath('~/.jupyter')


def test_envset():
true_values = ['', 'True', 'on', 'yes', 'Y', '1', 'anything']
false_values = ['n', 'No', 'N', 'fAlSE', '0', '0.0', 'Off']
Expand Down Expand Up @@ -184,8 +195,27 @@ def test_jupyter_path():
assert path[0] == jupyter_data_dir()
assert path[-2:] == system_path

def test_jupyter_path_user_site():
with no_config_env, patch.object(site, 'ENABLE_USER_SITE', True):
path = jupyter_path()

# deduplicated expected values
values = list(dict.fromkeys([
jupyter_data_dir(),
os.path.join(site.getuserbase(), 'share', 'jupyter'),
paths.ENV_JUPYTER_PATH[0]
]))
for p,v in zip(path, values):
assert p == v

def test_jupyter_path_no_user_site():
with no_config_env, patch.object(site, 'ENABLE_USER_SITE', False):
path = jupyter_path()
assert path[0] == jupyter_data_dir()
assert path[1] == paths.ENV_JUPYTER_PATH[0]

def test_jupyter_path_prefer_env():
with patch.dict('os.environ', {'JUPYTER_PREFER_ENV_PATH': 'true'}):
with prefer_env:
path = jupyter_path()
assert path[0] == paths.ENV_JUPYTER_PATH[0]
assert path[1] == jupyter_data_dir()
Expand Down Expand Up @@ -213,15 +243,37 @@ def test_jupyter_path_subdir():
assert p.endswith(pjoin('', 'sub1', 'sub2'))

def test_jupyter_config_path():
path = jupyter_config_path()
with patch.object(site, 'ENABLE_USER_SITE', True):
path = jupyter_config_path()

# deduplicated expected values
values = list(dict.fromkeys([
jupyter_config_dir(),
os.path.join(site.getuserbase(), 'etc', 'jupyter'),
paths.ENV_CONFIG_PATH[0]
]))
for p,v in zip(path, values):
assert p == v

def test_jupyter_config_path_no_user_site():
with patch.object(site, 'ENABLE_USER_SITE', False):
path = jupyter_config_path()
assert path[0] == jupyter_config_dir()
assert path[1] == paths.ENV_CONFIG_PATH[0]


def test_jupyter_config_path_prefer_env():
with patch.dict('os.environ', {'JUPYTER_PREFER_ENV_PATH': 'true'}):
with prefer_env, patch.object(site, 'ENABLE_USER_SITE', True):
path = jupyter_config_path()
assert path[0] == paths.ENV_CONFIG_PATH[0]
assert path[1] == jupyter_config_dir()

# deduplicated expected values
values = list(dict.fromkeys([
paths.ENV_CONFIG_PATH[0],
jupyter_config_dir(),
os.path.join(site.getuserbase(), 'etc', 'jupyter')
]))
for p,v in zip(path, values):
assert p == v

def test_jupyter_config_path_env():
path_env = os.pathsep.join([
Expand Down