@@ -1593,11 +1593,22 @@ def _normalize_dir(self, value):
1593
1593
value = os .path .abspath (value )
1594
1594
return value
1595
1595
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
+
1596
1602
@validate ("root_dir" )
1597
1603
def _root_dir_validate (self , proposal ):
1598
1604
value = self ._normalize_dir (proposal ["value" ])
1599
1605
if not os .path .isdir (value ):
1600
1606
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 )
1601
1612
return value
1602
1613
1603
1614
preferred_dir = Unicode (
@@ -1615,16 +1626,28 @@ def _preferred_dir_validate(self, proposal):
1615
1626
if not os .path .isdir (value ):
1616
1627
raise TraitError (trans .gettext ("No such preferred dir: '%r'" ) % value )
1617
1628
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 ):
1620
1644
raise TraitError (
1621
1645
trans .gettext (
1622
1646
"preferred_dir must be equal or a subdir of root_dir. preferred_dir: '%r' root_dir: '%r'"
1623
1647
)
1624
- % (value , self . root_dir )
1648
+ % (preferred_dir , root_dir )
1625
1649
)
1626
-
1627
- return value
1650
+ self ._defer_preferred_dir_validation = False
1628
1651
1629
1652
@observe ("root_dir" )
1630
1653
def _root_dir_changed (self , change ):
@@ -2387,6 +2410,10 @@ def initialize(
2387
2410
# Parse command line, load ServerApp config files,
2388
2411
# and update ServerApp config.
2389
2412
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 )
2390
2417
if self ._dispatching :
2391
2418
return
2392
2419
# Then, use extensions' config loading mechanism to
0 commit comments