Skip to content

Commit f7290dc

Browse files
authored
PR: Add a new preferred-dir traitlet (#549)
* Add a new preferred-dir traitlet
1 parent 81d46e3 commit f7290dc

File tree

2 files changed

+120
-3
lines changed

2 files changed

+120
-3
lines changed

jupyter_server/serverapp.py

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,7 @@ def start(self):
610610
'certfile': 'ServerApp.certfile',
611611
'client-ca': 'ServerApp.client_ca',
612612
'notebook-dir': 'ServerApp.root_dir',
613+
'preferred-dir': 'ServerApp.preferred_dir',
613614
'browser': 'ServerApp.browser',
614615
'pylab': 'ServerApp.pylab',
615616
'gateway-url': 'GatewayClient.url',
@@ -1355,9 +1356,7 @@ def _default_root_dir(self):
13551356
else:
13561357
return os.getcwd()
13571358

1358-
@validate('root_dir')
1359-
def _root_dir_validate(self, proposal):
1360-
value = proposal['value']
1359+
def _normalize_dir(self, value):
13611360
# Strip any trailing slashes
13621361
# *except* if it's root
13631362
_, path = os.path.splitdrive(value)
@@ -1367,13 +1366,41 @@ def _root_dir_validate(self, proposal):
13671366
if not os.path.isabs(value):
13681367
# If we receive a non-absolute path, make it absolute.
13691368
value = os.path.abspath(value)
1369+
return value
1370+
1371+
@validate('root_dir')
1372+
def _root_dir_validate(self, proposal):
1373+
value = self._normalize_dir(proposal['value'])
13701374
if not os.path.isdir(value):
13711375
raise TraitError(trans.gettext("No such directory: '%r'") % value)
13721376
return value
13731377

1378+
preferred_dir = Unicode(config=True,
1379+
help=trans.gettext("Preferred starting directory to use for notebooks and kernels.")
1380+
)
1381+
1382+
@default('preferred_dir')
1383+
def _default_prefered_dir(self):
1384+
return self.root_dir
1385+
1386+
@validate('preferred_dir')
1387+
def _preferred_dir_validate(self, proposal):
1388+
value = self._normalize_dir(proposal['value'])
1389+
if not os.path.isdir(value):
1390+
raise TraitError(trans.gettext("No such preferred dir: '%r'") % value)
1391+
1392+
# preferred_dir must be equal or a subdir of root_dir
1393+
if not value.startswith(self.root_dir):
1394+
raise TraitError(trans.gettext("preferred_dir must be equal or a subdir of root_dir: '%r'") % value)
1395+
1396+
return value
1397+
13741398
@observe('root_dir')
13751399
def _root_dir_changed(self, change):
13761400
self._root_dir_set = True
1401+
if not self.preferred_dir.startswith(change['new']):
1402+
self.log.warning(trans.gettext("Value of preferred_dir updated to use value of root_dir"))
1403+
self.preferred_dir = change['new']
13771404

13781405
@observe('server_extensions')
13791406
def _update_server_extensions(self, change):

jupyter_server/tests/test_serverapp.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,3 +286,93 @@ def test_urls(config, public_url, local_url, connection_url):
286286
assert serverapp.connection_url == connection_url
287287
# Cleanup singleton after test.
288288
ServerApp.clear_instance()
289+
290+
291+
# Preferred dir tests
292+
# ----------------------------------------------------------------------------
293+
def test_valid_preferred_dir(tmp_path, jp_configurable_serverapp):
294+
path = str(tmp_path)
295+
app = jp_configurable_serverapp(root_dir=path, preferred_dir=path)
296+
assert app.root_dir == path
297+
assert app.preferred_dir == path
298+
assert app.root_dir == app.preferred_dir
299+
300+
301+
def test_valid_preferred_dir_is_root_subdir(tmp_path, jp_configurable_serverapp):
302+
path = str(tmp_path)
303+
path_subdir = str(tmp_path / 'subdir')
304+
os.makedirs(path_subdir, exist_ok=True)
305+
app = jp_configurable_serverapp(root_dir=path, preferred_dir=path_subdir)
306+
assert app.root_dir == path
307+
assert app.preferred_dir == path_subdir
308+
assert app.preferred_dir.startswith(app.root_dir)
309+
310+
311+
def test_valid_preferred_dir_does_not_exist(tmp_path, jp_configurable_serverapp):
312+
path = str(tmp_path)
313+
path_subdir = str(tmp_path / 'subdir')
314+
with pytest.raises(TraitError) as error:
315+
app = jp_configurable_serverapp(root_dir=path, preferred_dir=path_subdir)
316+
317+
assert "No such preferred dir:" in str(error)
318+
319+
320+
def test_invalid_preferred_dir_does_not_exist(tmp_path, jp_configurable_serverapp):
321+
path = str(tmp_path)
322+
path_subdir = str(tmp_path / 'subdir')
323+
with pytest.raises(TraitError) as error:
324+
app = jp_configurable_serverapp(root_dir=path, preferred_dir=path_subdir)
325+
326+
assert "No such preferred dir:" in str(error)
327+
328+
329+
def test_invalid_preferred_dir_does_not_exist_set(tmp_path, jp_configurable_serverapp):
330+
path = str(tmp_path)
331+
path_subdir = str(tmp_path / 'subdir')
332+
333+
app = jp_configurable_serverapp(root_dir=path)
334+
with pytest.raises(TraitError) as error:
335+
app.preferred_dir = path_subdir
336+
337+
assert "No such preferred dir:" in str(error)
338+
339+
340+
def test_invalid_preferred_dir_not_root_subdir(tmp_path, jp_configurable_serverapp):
341+
path = str(tmp_path / 'subdir')
342+
os.makedirs(path, exist_ok=True)
343+
not_subdir_path = str(tmp_path)
344+
345+
with pytest.raises(TraitError) as error:
346+
app = jp_configurable_serverapp(root_dir=path, preferred_dir=not_subdir_path)
347+
348+
assert "preferred_dir must be equal or a subdir of root_dir:" in str(error)
349+
350+
351+
def test_invalid_preferred_dir_not_root_subdir_set(tmp_path, jp_configurable_serverapp):
352+
path = str(tmp_path / 'subdir')
353+
os.makedirs(path, exist_ok=True)
354+
not_subdir_path = str(tmp_path)
355+
356+
app = jp_configurable_serverapp(root_dir=path)
357+
with pytest.raises(TraitError) as error:
358+
app.preferred_dir = not_subdir_path
359+
360+
assert "preferred_dir must be equal or a subdir of root_dir:" in str(error)
361+
362+
363+
def test_observed_root_dir_updates_preferred_dir(tmp_path, jp_configurable_serverapp):
364+
path = str(tmp_path)
365+
new_path = str(tmp_path / 'subdir')
366+
os.makedirs(new_path, exist_ok=True)
367+
368+
app = jp_configurable_serverapp(root_dir=path, preferred_dir=path)
369+
app.root_dir = new_path
370+
assert app.preferred_dir == new_path
371+
372+
373+
def test_observed_root_dir_does_not_update_preferred_dir(tmp_path, jp_configurable_serverapp):
374+
path = str(tmp_path)
375+
new_path = str(tmp_path.parent)
376+
app = jp_configurable_serverapp(root_dir=path, preferred_dir=path)
377+
app.root_dir = new_path
378+
assert app.preferred_dir == path

0 commit comments

Comments
 (0)