@@ -1647,11 +1647,22 @@ def _normalize_dir(self, value):
16471647 value = os .path .abspath (value )
16481648 return value
16491649
1650+ # Because the validation of preferred_dir depends on root_dir and validation
1651+ # occurs when the trait is loaded, there are times when we should defer the
1652+ # validation of preferred_dir (e.g., when preferred_dir is defined via CLI
1653+ # and root_dir is defined via a config file).
1654+ _defer_preferred_dir_validation = False
1655+
16501656 @validate ("root_dir" )
16511657 def _root_dir_validate (self , proposal ):
16521658 value = self ._normalize_dir (proposal ["value" ])
16531659 if not os .path .isdir (value ):
16541660 raise TraitError (trans .gettext ("No such directory: '%r'" ) % value )
1661+
1662+ if self ._defer_preferred_dir_validation :
1663+ # If we're here, then preferred_dir is configured on the CLI and
1664+ # root_dir is configured in a file
1665+ self ._preferred_dir_validation (self .preferred_dir , value )
16551666 return value
16561667
16571668 preferred_dir = Unicode (
@@ -1668,8 +1679,39 @@ def _preferred_dir_validate(self, proposal):
16681679 value = self ._normalize_dir (proposal ["value" ])
16691680 if not os .path .isdir (value ):
16701681 raise TraitError (trans .gettext ("No such preferred dir: '%r'" ) % value )
1682+
1683+ # Before we validate against root_dir, check if this trait is defined on the CLI
1684+ # and root_dir is not. If that's the case, we'll defer it's further validation
1685+ # until root_dir is validated or the server is starting (the latter occurs when
1686+ # the default root_dir (cwd) is used).
1687+ cli_config = self .cli_config .get ("ServerApp" , {})
1688+ if "preferred_dir" in cli_config and "root_dir" not in cli_config :
1689+ self ._defer_preferred_dir_validation = True
1690+
1691+ if not self ._defer_preferred_dir_validation : # Validate now
1692+ self ._preferred_dir_validation (value , self .root_dir )
16711693 return value
16721694
1695+ def _preferred_dir_validation (self , preferred_dir : str , root_dir : str ) -> None :
1696+ """Validate preferred dir relative to root_dir - preferred_dir must be equal or a subdir of root_dir"""
1697+ if not preferred_dir .startswith (root_dir ):
1698+ raise TraitError (
1699+ trans .gettext (
1700+ "preferred_dir must be equal or a subdir of root_dir. preferred_dir: '%r' root_dir: '%r'"
1701+ )
1702+ % (preferred_dir , root_dir )
1703+ )
1704+ self ._defer_preferred_dir_validation = False
1705+
1706+ @observe ("root_dir" )
1707+ def _root_dir_changed (self , change ):
1708+ self ._root_dir_set = True
1709+ if not self .preferred_dir .startswith (change ["new" ]):
1710+ self .log .warning (
1711+ trans .gettext ("Value of preferred_dir updated to use value of root_dir" )
1712+ )
1713+ self .preferred_dir = change ["new" ]
1714+
16731715 @observe ("server_extensions" )
16741716 def _update_server_extensions (self , change ):
16751717 self .log .warning (_i18n ("server_extensions is deprecated, use jpserver_extensions" ))
@@ -1851,9 +1893,6 @@ def init_configurables(self):
18511893 parent = self ,
18521894 log = self .log ,
18531895 )
1854- # Trigger a default/validation here explicitly while we still support the
1855- # deprecated trait on ServerApp (FIXME remove when deprecation finalized)
1856- self .contents_manager .preferred_dir
18571896 self .session_manager = self .session_manager_class (
18581897 parent = self ,
18591898 log = self .log ,
@@ -2478,6 +2517,10 @@ def initialize(
24782517 # Parse command line, load ServerApp config files,
24792518 # and update ServerApp config.
24802519 super ().initialize (argv = argv )
2520+ if self ._defer_preferred_dir_validation :
2521+ # If we're here, then preferred_dir is configured on the CLI and
2522+ # root_dir has the default value (cwd)
2523+ self ._preferred_dir_validation (self .preferred_dir , self .root_dir )
24812524 if self ._dispatching :
24822525 return
24832526 # initialize io loop as early as possible,
0 commit comments