@@ -1593,11 +1593,22 @@ def _normalize_dir(self, value):
15931593 value = os .path .abspath (value )
15941594 return value
15951595
1596+ # Because the validation of preferred_dir depends on root_dir and validation
1597+ # occurs when the trait is loaded, there are times when we should defer the
1598+ # validation of preferred_dir (e.g., when preferred_dir is defined via CLI
1599+ # and root_dir is defined via a config file).
1600+ _defer_preferred_dir_validation = False
1601+
15961602 @validate ("root_dir" )
15971603 def _root_dir_validate (self , proposal ):
15981604 value = self ._normalize_dir (proposal ["value" ])
15991605 if not os .path .isdir (value ):
16001606 raise TraitError (trans .gettext ("No such directory: '%r'" ) % value )
1607+
1608+ if self ._defer_preferred_dir_validation :
1609+ # If we're here, then preferred_dir is configured on the CLI and
1610+ # root_dir is configured in a file
1611+ self ._preferred_dir_validation (self .preferred_dir , value )
16011612 return value
16021613
16031614 preferred_dir = Unicode (
@@ -1615,16 +1626,28 @@ def _preferred_dir_validate(self, proposal):
16151626 if not os .path .isdir (value ):
16161627 raise TraitError (trans .gettext ("No such preferred dir: '%r'" ) % value )
16171628
1618- # preferred_dir must be equal or a subdir of root_dir
1619- if not value .startswith (self .root_dir ):
1629+ # Before we validate against root_dir, check if this trait is defined on the CLI
1630+ # and root_dir is not. If that's the case, we'll defer it's further validation
1631+ # until root_dir is validated or the server is starting (the latter occurs when
1632+ # the default root_dir (cwd) is used).
1633+ cli_config = self .cli_config .get ("ServerApp" , {})
1634+ if "preferred_dir" in cli_config and "root_dir" not in cli_config :
1635+ self ._defer_preferred_dir_validation = True
1636+
1637+ if not self ._defer_preferred_dir_validation : # Validate now
1638+ self ._preferred_dir_validation (value , self .root_dir )
1639+ return value
1640+
1641+ def _preferred_dir_validation (self , preferred_dir : str , root_dir : str ) -> None :
1642+ """Validate preferred dir relative to root_dir - preferred_dir must be equal or a subdir of root_dir"""
1643+ if not preferred_dir .startswith (root_dir ):
16201644 raise TraitError (
16211645 trans .gettext (
16221646 "preferred_dir must be equal or a subdir of root_dir. preferred_dir: '%r' root_dir: '%r'"
16231647 )
1624- % (value , self . root_dir )
1648+ % (preferred_dir , root_dir )
16251649 )
1626-
1627- return value
1650+ self ._defer_preferred_dir_validation = False
16281651
16291652 @observe ("root_dir" )
16301653 def _root_dir_changed (self , change ):
@@ -2387,6 +2410,10 @@ def initialize(
23872410 # Parse command line, load ServerApp config files,
23882411 # and update ServerApp config.
23892412 super ().initialize (argv = argv )
2413+ if self ._defer_preferred_dir_validation :
2414+ # If we're here, then preferred_dir is configured on the CLI and
2415+ # root_dir has the default value (cwd)
2416+ self ._preferred_dir_validation (self .preferred_dir , self .root_dir )
23902417 if self ._dispatching :
23912418 return
23922419 # Then, use extensions' config loading mechanism to
0 commit comments