Skip to content

Commit 29000fe

Browse files
authored
Merge pull request #242 from jasongrout/user
Add site user base directory to config and data paths if site.ENABLE_USER_SITE is true
2 parents 0609690 + c97ab5d commit 29000fe

File tree

5 files changed

+117
-15
lines changed

5 files changed

+117
-15
lines changed

docs/changelog.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
11
Changes in jupyter-core
22
=======================
33

4+
4.9
5+
---
6+
7+
4.9.0
8+
~~~~~
9+
10+
`on
11+
GitHub <https://github.com/jupyter/jupyter_core/releases/tag/4.9.0>`__
12+
13+
See the `jupyter_core
14+
4.9 <https://github.com/jupyter/jupyter_core/milestone/21?closed=1>`__
15+
milestone on GitHub for the full list of pull requests and issues closed.
16+
17+
- 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`)
18+
19+
420
4.8
521
---
622

jupyter_core/command.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import json
1212
import os
1313
from shutil import which
14+
import site
1415
import sys
1516
import sysconfig
1617
from subprocess import Popen
@@ -257,6 +258,11 @@ def main():
257258
else:
258259
print("JUPYTER_CONFIG_DIR is not set, so we use the default user-level config directory")
259260

261+
if site.ENABLE_USER_SITE:
262+
print(f"Python's site.ENABLE_USER_SITE is True, so we add the user site directory '{site.getuserbase()}'")
263+
else:
264+
print(f"Python's site.ENABLE_USER_SITE is not True, so we do not add the Python site user directory '{site.getuserbase()}'")
265+
260266
# data path list
261267
if env.get('JUPYTER_PATH'):
262268
print(f"JUPYTER_PATH is set to '{env.get('JUPYTER_PATH')}', which is prepended to the data paths")

jupyter_core/paths.py

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import sys
1313
import stat
1414
import errno
15+
import site
1516
import tempfile
1617
import warnings
1718
from pathlib import Path
@@ -141,6 +142,10 @@ def jupyter_path(*subdirs):
141142
If the JUPYTER_PREFER_ENV_PATH environment variable is set, the environment-level
142143
directories will have priority over user-level directories.
143144
145+
If the Python site.ENABLE_USER_SITE variable is True, we also add the
146+
appropriate Python user site subdirectory to the user-level directories.
147+
148+
144149
If ``*subdirs`` are given, that subdirectory will be added to each element.
145150
146151
Examples:
@@ -161,14 +166,19 @@ def jupyter_path(*subdirs):
161166
)
162167

163168
# Next is environment or user, depending on the JUPYTER_PREFER_ENV_PATH flag
164-
user = jupyter_data_dir()
169+
user = [jupyter_data_dir()]
170+
if site.ENABLE_USER_SITE:
171+
userdir = os.path.join(site.getuserbase(), 'share', 'jupyter')
172+
if userdir not in user:
173+
user.append(userdir)
174+
165175
env = [p for p in ENV_JUPYTER_PATH if p not in SYSTEM_JUPYTER_PATH]
166176

167177
if envset('JUPYTER_PREFER_ENV_PATH'):
168178
paths.extend(env)
169-
paths.append(user)
179+
paths.extend(user)
170180
else:
171-
paths.append(user)
181+
paths.extend(user)
172182
paths.extend(env)
173183

174184
# finally, system
@@ -197,9 +207,13 @@ def jupyter_path(*subdirs):
197207

198208
def jupyter_config_path():
199209
"""Return the search path for Jupyter config files as a list.
200-
201-
If the JUPYTER_PREFER_ENV_PATH environment variable is set, the environment-level
202-
directories will have priority over user-level directories.
210+
211+
If the JUPYTER_PREFER_ENV_PATH environment variable is set, the
212+
environment-level directories will have priority over user-level
213+
directories.
214+
215+
If the Python site.ENABLE_USER_SITE variable is True, we also add the
216+
appropriate Python user site subdirectory to the user-level directories.
203217
"""
204218
if os.environ.get('JUPYTER_NO_CONFIG'):
205219
# jupyter_config_dir makes a blank config when JUPYTER_NO_CONFIG is set.
@@ -215,14 +229,19 @@ def jupyter_config_path():
215229
)
216230

217231
# Next is environment or user, depending on the JUPYTER_PREFER_ENV_PATH flag
218-
user = jupyter_config_dir()
232+
user = [jupyter_config_dir()]
233+
if site.ENABLE_USER_SITE:
234+
userdir = os.path.join(site.getuserbase(), 'etc', 'jupyter')
235+
if userdir not in user:
236+
user.append(userdir)
237+
219238
env = [p for p in ENV_CONFIG_PATH if p not in SYSTEM_CONFIG_PATH]
220239

221240
if envset('JUPYTER_PREFER_ENV_PATH'):
222241
paths.extend(env)
223-
paths.append(user)
242+
paths.extend(user)
224243
else:
225-
paths.append(user)
244+
paths.extend(user)
226245
paths.extend(env)
227246

228247
# Finally, system path

jupyter_core/tests/test_command.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@
1717
)
1818

1919

20+
resetenv = patch.dict(os.environ)
21+
22+
def setup_module():
23+
resetenv.start()
24+
os.environ.pop('JUPYTER_PREFER_ENV_PATH', None)
25+
26+
def teardown_module():
27+
resetenv.stop()
28+
2029
def get_jupyter_output(cmd):
2130
"""Get output of a jupyter command"""
2231
if not isinstance(cmd, list):

jupyter_core/tests/test_paths.py

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import tempfile
1111
from unittest.mock import patch
1212
import pytest
13+
import site
1314
import subprocess
1415
import sys
1516
import warnings
@@ -47,14 +48,24 @@
4748

4849
jupyter_config_env = '/jupyter-cfg'
4950
config_env = patch.dict('os.environ', {'JUPYTER_CONFIG_DIR': jupyter_config_env})
51+
prefer_env = patch.dict('os.environ', {'JUPYTER_PREFER_ENV_PATH': 'True'})
52+
53+
resetenv = patch.dict(os.environ)
54+
55+
def setup_module():
56+
resetenv.start()
57+
os.environ.pop('JUPYTER_PREFER_ENV_PATH', None)
58+
59+
def teardown_module():
60+
resetenv.stop()
61+
5062

5163

5264
def realpath(path):
5365
return os.path.abspath(os.path.realpath(os.path.expanduser(path)))
5466

5567
home_jupyter = realpath('~/.jupyter')
5668

57-
5869
def test_envset():
5970
true_values = ['', 'True', 'on', 'yes', 'Y', '1', 'anything']
6071
false_values = ['n', 'No', 'N', 'fAlSE', '0', '0.0', 'Off']
@@ -184,8 +195,27 @@ def test_jupyter_path():
184195
assert path[0] == jupyter_data_dir()
185196
assert path[-2:] == system_path
186197

198+
def test_jupyter_path_user_site():
199+
with no_config_env, patch.object(site, 'ENABLE_USER_SITE', True):
200+
path = jupyter_path()
201+
202+
# deduplicated expected values
203+
values = list(dict.fromkeys([
204+
jupyter_data_dir(),
205+
os.path.join(site.getuserbase(), 'share', 'jupyter'),
206+
paths.ENV_JUPYTER_PATH[0]
207+
]))
208+
for p,v in zip(path, values):
209+
assert p == v
210+
211+
def test_jupyter_path_no_user_site():
212+
with no_config_env, patch.object(site, 'ENABLE_USER_SITE', False):
213+
path = jupyter_path()
214+
assert path[0] == jupyter_data_dir()
215+
assert path[1] == paths.ENV_JUPYTER_PATH[0]
216+
187217
def test_jupyter_path_prefer_env():
188-
with patch.dict('os.environ', {'JUPYTER_PREFER_ENV_PATH': 'true'}):
218+
with prefer_env:
189219
path = jupyter_path()
190220
assert path[0] == paths.ENV_JUPYTER_PATH[0]
191221
assert path[1] == jupyter_data_dir()
@@ -213,15 +243,37 @@ def test_jupyter_path_subdir():
213243
assert p.endswith(pjoin('', 'sub1', 'sub2'))
214244

215245
def test_jupyter_config_path():
216-
path = jupyter_config_path()
246+
with patch.object(site, 'ENABLE_USER_SITE', True):
247+
path = jupyter_config_path()
248+
249+
# deduplicated expected values
250+
values = list(dict.fromkeys([
251+
jupyter_config_dir(),
252+
os.path.join(site.getuserbase(), 'etc', 'jupyter'),
253+
paths.ENV_CONFIG_PATH[0]
254+
]))
255+
for p,v in zip(path, values):
256+
assert p == v
257+
258+
def test_jupyter_config_path_no_user_site():
259+
with patch.object(site, 'ENABLE_USER_SITE', False):
260+
path = jupyter_config_path()
217261
assert path[0] == jupyter_config_dir()
218262
assert path[1] == paths.ENV_CONFIG_PATH[0]
219263

264+
220265
def test_jupyter_config_path_prefer_env():
221-
with patch.dict('os.environ', {'JUPYTER_PREFER_ENV_PATH': 'true'}):
266+
with prefer_env, patch.object(site, 'ENABLE_USER_SITE', True):
222267
path = jupyter_config_path()
223-
assert path[0] == paths.ENV_CONFIG_PATH[0]
224-
assert path[1] == jupyter_config_dir()
268+
269+
# deduplicated expected values
270+
values = list(dict.fromkeys([
271+
paths.ENV_CONFIG_PATH[0],
272+
jupyter_config_dir(),
273+
os.path.join(site.getuserbase(), 'etc', 'jupyter')
274+
]))
275+
for p,v in zip(path, values):
276+
assert p == v
225277

226278
def test_jupyter_config_path_env():
227279
path_env = os.pathsep.join([

0 commit comments

Comments
 (0)