@@ -210,10 +210,22 @@ def jupyter_runtime_dir() -> str:
210
210
return pjoin (jupyter_data_dir (), "runtime" )
211
211
212
212
213
+ # %PROGRAMDATA% is not safe by default, require opt-in to trust it
214
+ _use_programdata : bool = envset ("JUPYTER_USE_PROGRAMDATA" )
215
+ # _win_programdata is a path str if we're using it, None otherwise
216
+ _win_programdata : str | None = None
217
+ if os .name == "nt" and _use_programdata :
218
+ _win_programdata = os .environ .get ("PROGRAMDATA" , None )
219
+
220
+
213
221
if use_platform_dirs ():
214
- SYSTEM_JUPYTER_PATH = platformdirs .site_data_dir (
215
- APPNAME , appauthor = False , multipath = True
216
- ).split (os .pathsep )
222
+ if os .name == "nt" and not _use_programdata :
223
+ # default PROGRAMDATA used by site_* is not safe by default on Windows
224
+ SYSTEM_JUPYTER_PATH = [str (Path (sys .prefix , "share" , "jupyter" ))]
225
+ else :
226
+ SYSTEM_JUPYTER_PATH = platformdirs .site_data_dir (
227
+ APPNAME , appauthor = False , multipath = True
228
+ ).split (os .pathsep )
217
229
else :
218
230
deprecation (
219
231
"Jupyter is migrating its paths to use standard platformdirs\n "
@@ -223,10 +235,10 @@ def jupyter_runtime_dir() -> str:
223
235
"The use of platformdirs will be the default in `jupyter_core` v6"
224
236
)
225
237
if os .name == "nt" :
226
- programdata = os . environ . get ( " PROGRAMDATA" , None )
227
- if programdata :
228
- SYSTEM_JUPYTER_PATH = [pjoin (programdata , "jupyter" )]
229
- else : # PROGRAMDATA is not defined by default on XP.
238
+ # PROGRAMDATA is not defined by default on XP, and not safe by default
239
+ if _win_programdata :
240
+ SYSTEM_JUPYTER_PATH = [pjoin (_win_programdata , "jupyter" )]
241
+ else :
230
242
SYSTEM_JUPYTER_PATH = [str (Path (sys .prefix , "share" , "jupyter" ))]
231
243
else :
232
244
SYSTEM_JUPYTER_PATH = [
@@ -238,19 +250,40 @@ def jupyter_runtime_dir() -> str:
238
250
239
251
240
252
def jupyter_path (* subdirs : str ) -> list [str ]:
241
- """Return a list of directories to search for data files
253
+ """Return a list of directories to search for data files.
254
+
255
+ There are four sources of paths to search:
256
+
257
+ - $JUPYTER_PATH environment variable (always highest priority)
258
+ - user directories (e.g. ~/.local/share/jupyter)
259
+ - environment directories (e.g. {sys.prefix}/share/jupyter)
260
+ - system-wide paths (e.g. /usr/local/share/jupyter)
242
261
243
- JUPYTER_PATH environment variable has highest priority.
262
+ JUPYTER_PATH environment variable has highest priority, if defined,
263
+ and is purely additive.
244
264
245
265
If the JUPYTER_PREFER_ENV_PATH environment variable is set, the environment-level
246
266
directories will have priority over user-level directories.
267
+ You can also set JUPYTER_PREFER_ENV_PATH=0 to explicitly prefer user directories.
268
+ If Jupyter detects that you are in a virtualenv or conda environment,
269
+ environment paths are also preferred to user paths,
270
+ otherwise user paths are preferred to environment paths.
247
271
248
272
If the Python site.ENABLE_USER_SITE variable is True, we also add the
249
273
appropriate Python user site subdirectory to the user-level directories.
250
274
275
+ Finally, system-wide directories, such as `/usr/local/share/jupyter` are searched.
251
276
252
277
If ``*subdirs`` are given, that subdirectory will be added to each element.
253
278
279
+
280
+ .. versionchanged:: 5.8
281
+
282
+ On Windows, %PROGRAMDATA% will be used as a system-wide path only if
283
+ the JUPYTER_USE_PROGRAMDATA env is set.
284
+ By default, there is no default system-wide path on Windows and the env path
285
+ is used instead.
286
+
254
287
Examples:
255
288
256
289
>>> jupyter_path()
@@ -278,7 +311,13 @@ def jupyter_path(*subdirs: str) -> list[str]:
278
311
if userdir not in user :
279
312
user .append (userdir )
280
313
281
- env = [p for p in ENV_JUPYTER_PATH if p not in SYSTEM_JUPYTER_PATH ]
314
+ # Windows usually doesn't have a 'system' prefix,
315
+ # so 'system' and 'env' are the same
316
+ # make sure that env can still be preferred in this case
317
+ if ENV_JUPYTER_PATH == SYSTEM_JUPYTER_PATH :
318
+ env = ENV_JUPYTER_PATH
319
+ else :
320
+ env = [p for p in ENV_JUPYTER_PATH if p not in SYSTEM_JUPYTER_PATH ]
282
321
283
322
if prefer_environment_over_user ():
284
323
paths .extend (env )
@@ -287,8 +326,10 @@ def jupyter_path(*subdirs: str) -> list[str]:
287
326
paths .extend (user )
288
327
paths .extend (env )
289
328
290
- # finally, system
291
- paths .extend (SYSTEM_JUPYTER_PATH )
329
+ # finally, add system paths (can overlap with env, so avoid duplicates)
330
+ for _path in SYSTEM_JUPYTER_PATH :
331
+ if _path not in paths :
332
+ paths .append (_path )
292
333
293
334
# add subdir, if requested
294
335
if subdirs :
@@ -297,14 +338,18 @@ def jupyter_path(*subdirs: str) -> list[str]:
297
338
298
339
299
340
if use_platform_dirs ():
300
- SYSTEM_CONFIG_PATH = platformdirs .site_config_dir (
301
- APPNAME , appauthor = False , multipath = True
302
- ).split (os .pathsep )
341
+ if os .name == "nt" and not _use_programdata :
342
+ # default PROGRAMDATA is not safe by default on Windows
343
+ SYSTEM_CONFIG_PATH = []
344
+ else :
345
+ SYSTEM_CONFIG_PATH = platformdirs .site_config_dir (
346
+ APPNAME , appauthor = False , multipath = True
347
+ ).split (os .pathsep )
303
348
elif os .name == "nt" :
304
- programdata = os . environ . get ( " PROGRAMDATA" , None )
305
- if programdata :
306
- SYSTEM_CONFIG_PATH = [str (Path (programdata , "jupyter" ))]
307
- else : # PROGRAMDATA is not defined by default on XP.
349
+ # PROGRAMDATA is not defined by default on XP, and not safe by default
350
+ if _win_programdata :
351
+ SYSTEM_CONFIG_PATH = [str (Path (_win_programdata , "jupyter" ))]
352
+ else :
308
353
SYSTEM_CONFIG_PATH = []
309
354
else :
310
355
SYSTEM_CONFIG_PATH = [
@@ -323,6 +368,21 @@ def jupyter_config_path() -> list[str]:
323
368
324
369
If the Python site.ENABLE_USER_SITE variable is True, we also add the
325
370
appropriate Python user site subdirectory to the user-level directories.
371
+
372
+ Finally, system-wide directories such as `/usr/local/etc/jupyter` are searched.
373
+
374
+
375
+ .. versionchanged:: 5.8
376
+
377
+ On Windows, %PROGRAMDATA% will be used as a system-wide path only if
378
+ the JUPYTER_USE_PROGRAMDATA env is set.
379
+ By default, there is no system-wide config path on Windows.
380
+
381
+ Examples:
382
+
383
+ >>> jupyter_config_path()
384
+ ['~/.jupyter', '~/.local/etc/jupyter', '/usr/local/etc/jupyter', '/etc/jupyter']
385
+
326
386
"""
327
387
if os .environ .get ("JUPYTER_NO_CONFIG" ):
328
388
# jupyter_config_dir makes a blank config when JUPYTER_NO_CONFIG is set.
0 commit comments