@@ -1486,8 +1486,8 @@ def test_glob_dotdot(self):
14861486 self .assertEqual (set (p .glob ("../xyzzy" )), set ())
14871487
14881488
1489- def _check_resolve (self , p , expected ):
1490- q = p .resolve ()
1489+ def _check_resolve (self , p , expected , strict = True ):
1490+ q = p .resolve (strict )
14911491 self .assertEqual (q , expected )
14921492
14931493 # this can be used to check both relative and absolute resolutions
@@ -1498,8 +1498,17 @@ def test_resolve_common(self):
14981498 P = self .cls
14991499 p = P (BASE , 'foo' )
15001500 with self .assertRaises (OSError ) as cm :
1501- p .resolve ()
1501+ p .resolve (strict = True )
15021502 self .assertEqual (cm .exception .errno , errno .ENOENT )
1503+ # Non-strict
1504+ self .assertEqual (str (p .resolve (strict = False )),
1505+ os .path .join (BASE , 'foo' ))
1506+ p = P (BASE , 'foo' , 'in' , 'spam' )
1507+ self .assertEqual (str (p .resolve (strict = False )),
1508+ os .path .join (BASE , 'foo' ))
1509+ p = P (BASE , '..' , 'foo' , 'in' , 'spam' )
1510+ self .assertEqual (str (p .resolve (strict = False )),
1511+ os .path .abspath (os .path .join ('foo' )))
15031512 # These are all relative symlinks
15041513 p = P (BASE , 'dirB' , 'fileB' )
15051514 self ._check_resolve_relative (p , p )
@@ -1509,13 +1518,37 @@ def test_resolve_common(self):
15091518 self ._check_resolve_relative (p , P (BASE , 'dirB' , 'fileB' ))
15101519 p = P (BASE , 'dirB' , 'linkD' , 'fileB' )
15111520 self ._check_resolve_relative (p , P (BASE , 'dirB' , 'fileB' ))
1521+ # Non-strict
1522+ p = P (BASE , 'dirA' , 'linkC' , 'fileB' , 'foo' , 'in' , 'spam' )
1523+ self ._check_resolve_relative (p , P (BASE , 'dirB' , 'fileB' , 'foo' ), False )
1524+ p = P (BASE , 'dirA' , 'linkC' , '..' , 'foo' , 'in' , 'spam' )
1525+ if os .name == 'nt' :
1526+ # In Windows, if linkY points to dirB, 'dirA\linkY\..'
1527+ # resolves to 'dirA' without resolving linkY first.
1528+ self ._check_resolve_relative (p , P (BASE , 'dirA' , 'foo' ), False )
1529+ else :
1530+ # In Posix, if linkY points to dirB, 'dirA/linkY/..'
1531+ # resolves to 'dirB/..' first before resolving to parent of dirB.
1532+ self ._check_resolve_relative (p , P (BASE , 'foo' ), False )
15121533 # Now create absolute symlinks
15131534 d = tempfile .mkdtemp (suffix = '-dirD' )
15141535 self .addCleanup (support .rmtree , d )
15151536 os .symlink (os .path .join (d ), join ('dirA' , 'linkX' ))
15161537 os .symlink (join ('dirB' ), os .path .join (d , 'linkY' ))
15171538 p = P (BASE , 'dirA' , 'linkX' , 'linkY' , 'fileB' )
15181539 self ._check_resolve_absolute (p , P (BASE , 'dirB' , 'fileB' ))
1540+ # Non-strict
1541+ p = P (BASE , 'dirA' , 'linkX' , 'linkY' , 'foo' , 'in' , 'spam' )
1542+ self ._check_resolve_relative (p , P (BASE , 'dirB' , 'foo' ), False )
1543+ p = P (BASE , 'dirA' , 'linkX' , 'linkY' , '..' , 'foo' , 'in' , 'spam' )
1544+ if os .name == 'nt' :
1545+ # In Windows, if linkY points to dirB, 'dirA\linkY\..'
1546+ # resolves to 'dirA' without resolving linkY first.
1547+ self ._check_resolve_relative (p , P (d , 'foo' ), False )
1548+ else :
1549+ # In Posix, if linkY points to dirB, 'dirA/linkY/..'
1550+ # resolves to 'dirB/..' first before resolving to parent of dirB.
1551+ self ._check_resolve_relative (p , P (BASE , 'foo' ), False )
15191552
15201553 @with_symlinks
15211554 def test_resolve_dot (self ):
@@ -1525,7 +1558,11 @@ def test_resolve_dot(self):
15251558 self .dirlink (os .path .join ('0' , '0' ), join ('1' ))
15261559 self .dirlink (os .path .join ('1' , '1' ), join ('2' ))
15271560 q = p / '2'
1528- self .assertEqual (q .resolve (), p )
1561+ self .assertEqual (q .resolve (strict = True ), p )
1562+ r = q / '3' / '4'
1563+ self .assertRaises (FileNotFoundError , r .resolve , strict = True )
1564+ # Non-strict
1565+ self .assertEqual (r .resolve (strict = False ), p / '3' )
15291566
15301567 def test_with (self ):
15311568 p = self .cls (BASE )
@@ -1972,10 +2009,10 @@ def test_glob_empty_pattern(self):
19722009class PosixPathTest (_BasePathTest , unittest .TestCase ):
19732010 cls = pathlib .PosixPath
19742011
1975- def _check_symlink_loop (self , * args ):
2012+ def _check_symlink_loop (self , * args , strict = True ):
19762013 path = self .cls (* args )
19772014 with self .assertRaises (RuntimeError ):
1978- print (path .resolve ())
2015+ print (path .resolve (strict ))
19792016
19802017 def test_open_mode (self ):
19812018 old_mask = os .umask (0 )
@@ -2008,21 +2045,24 @@ def test_touch_mode(self):
20082045
20092046 @with_symlinks
20102047 def test_resolve_loop (self ):
2011- # Loop detection for broken symlinks under POSIX
20122048 # Loops with relative symlinks
20132049 os .symlink ('linkX/inside' , join ('linkX' ))
20142050 self ._check_symlink_loop (BASE , 'linkX' )
20152051 os .symlink ('linkY' , join ('linkY' ))
20162052 self ._check_symlink_loop (BASE , 'linkY' )
20172053 os .symlink ('linkZ/../linkZ' , join ('linkZ' ))
20182054 self ._check_symlink_loop (BASE , 'linkZ' )
2055+ # Non-strict
2056+ self ._check_symlink_loop (BASE , 'linkZ' , 'foo' , strict = False )
20192057 # Loops with absolute symlinks
20202058 os .symlink (join ('linkU/inside' ), join ('linkU' ))
20212059 self ._check_symlink_loop (BASE , 'linkU' )
20222060 os .symlink (join ('linkV' ), join ('linkV' ))
20232061 self ._check_symlink_loop (BASE , 'linkV' )
20242062 os .symlink (join ('linkW/../linkW' ), join ('linkW' ))
20252063 self ._check_symlink_loop (BASE , 'linkW' )
2064+ # Non-strict
2065+ self ._check_symlink_loop (BASE , 'linkW' , 'foo' , strict = False )
20262066
20272067 def test_glob (self ):
20282068 P = self .cls
0 commit comments