@@ -1328,6 +1328,7 @@ def test_different_flavours_unordered(self):
13281328
13291329# Make sure any symbolic links in the base test path are resolved.
13301330BASE = os .path .realpath (TESTFN )
1331+ REL_BASE = TESTFN
13311332join = lambda * x : os .path .join (BASE , * x )
13321333rel_join = lambda * x : os .path .join (TESTFN , * x )
13331334
@@ -1721,11 +1722,80 @@ def _check_resolve(self, p, expected, strict=True):
17211722 q = p .resolve (strict )
17221723 self .assertEqual (q , expected )
17231724
1724- # This can be used to check both relative and absolute resolutions.
1725- _check_resolve_relative = _check_resolve_absolute = _check_resolve
1725+ # These can be used to check both relative and absolute symlink
1726+ # resolutions.
1727+ _check_resolve_relative_link = _check_resolve
1728+ _check_resolve_absolute_link = _check_resolve
1729+
1730+ def _check_resolve_relative_path (self , p , expected , strict = True ):
1731+ self .assertFalse (p .is_absolute (), msg = f'{ p } is an absolute path' )
1732+ self ._check_resolve (p , expected , strict )
1733+
1734+ def _check_resolve_absolute_path (self , p , expected , strict = True ):
1735+ self .assertTrue (p .is_absolute (), msg = f'{ p } is a relative path' )
1736+ self ._check_resolve (p , expected , strict )
1737+
1738+ def test_resolve_relative_path (self ):
1739+ def _path_depth (purepath ):
1740+ length = len (purepath .anchor )
1741+ return length if purepath .anchor == "" else length - 1
1742+ P = self .cls
1743+ drive = P (BASE ).drive
1744+ root = P (BASE ).root
1745+ p = P (REL_BASE )
1746+ self ._check_resolve_relative_path (p , P (BASE ))
1747+ p = P (REL_BASE , 'fileA' )
1748+ self ._check_resolve_relative_path (p , P (BASE , 'fileA' ))
1749+ p = P (drive , REL_BASE , 'fileA' ) # W/O a root, still relative
1750+ self ._check_resolve_relative_path (p , P (BASE , 'fileA' ))
1751+ p = P (REL_BASE , 'dirB' , 'fileB' )
1752+ self ._check_resolve_relative_path (p , P (BASE , 'dirB' , 'fileB' ))
1753+ p = P (REL_BASE , 'absent' )
1754+ self ._check_resolve_relative_path (p , P (BASE , 'absent' ), strict = False )
1755+ self .assertRaises (FileNotFoundError , p .resolve , strict = True )
1756+ p = P (REL_BASE , 'dirA' , 'really' , 'absent.ext' )
1757+ self ._check_resolve_relative_path (
1758+ p , P (BASE , 'dirA' , 'really' , 'absent.ext' ), strict = False
1759+ )
1760+ self .assertRaises (FileNotFoundError , p .resolve , strict = True )
1761+ p = P (REL_BASE , 'dirC' , 'dirD' , '..' , '..' , 'dirC' , '..' , 'dirA' )
1762+ self ._check_resolve_relative_path (p , P (BASE , 'dirA' ))
1763+ p = P (REL_BASE , '.' , '.' , 'dirA' , '..' , '.' , '.' , 'dirA' )
1764+ self ._check_resolve_relative_path (p , P (BASE , 'dirA' ))
1765+ # Path lengths on Windows must be < 260 chars, and '/..'
1766+ # is len 3, so we use this as an upper bound for dot paths
1767+ # otherwise this will throw a FileNotFound on Windows.
1768+ # There is wiggle room to prevent this from breaking
1769+ # since BASE is contained in the cur directory and
1770+ # therefore len(cur_path) < len(BASE).
1771+ max_levels_up = (260 - len (BASE )) // 3
1772+ levels_to_ascend = max (max_levels_up , _path_depth (P (BASE )))
1773+ p = P ('/' .join (('..' ,) * levels_to_ascend ))
1774+ self ._check_resolve_relative_path (p , P (drive , root ))
1775+
1776+ def test_resolve_absolute_path (self ):
1777+ P = self .cls
1778+ p = P (BASE )
1779+ self ._check_resolve_absolute_path (p , p )
1780+ p = P (BASE , 'fileA' )
1781+ self ._check_resolve_absolute_path (p , P (BASE , 'fileA' ))
1782+ p = P (BASE , 'dirB' , 'fileB' )
1783+ self ._check_resolve_absolute_path (p , P (BASE , 'dirB' , 'fileB' ))
1784+ p = P (BASE , 'absent' )
1785+ self ._check_resolve_absolute_path (p , P (BASE , 'absent' ), strict = False )
1786+ self .assertRaises (FileNotFoundError , p .resolve , strict = True )
1787+ p = P (BASE , 'dirA' , 'really' , 'absent.ext' )
1788+ self ._check_resolve_absolute_path (
1789+ p , P (BASE , 'dirA' , 'really' , 'absent.ext' ), strict = False
1790+ )
1791+ self .assertRaises (FileNotFoundError , p .resolve , strict = True )
1792+ p = P (BASE , 'dirC' , 'dirD' , '..' , '..' , 'dirC' , '..' , 'dirA' )
1793+ self ._check_resolve_absolute_path (p , P (BASE , 'dirA' ))
1794+ p = P (BASE , '.' , '.' , 'dirA' , '..' , '.' , '.' , 'dirA' )
1795+ self ._check_resolve_absolute_path (p , P (BASE , 'dirA' ))
17261796
17271797 @os_helper .skip_unless_symlink
1728- def test_resolve_common (self ):
1798+ def test_resolve_symlinks (self ):
17291799 P = self .cls
17301800 p = P (BASE , 'foo' )
17311801 with self .assertRaises (OSError ) as cm :
@@ -1742,48 +1812,56 @@ def test_resolve_common(self):
17421812 os .path .abspath (os .path .join ('foo' , 'in' , 'spam' )))
17431813 # These are all relative symlinks.
17441814 p = P (BASE , 'dirB' , 'fileB' )
1745- self ._check_resolve_relative (p , p )
1815+ self ._check_resolve (p , p )
17461816 p = P (BASE , 'linkA' )
1747- self ._check_resolve_relative (p , P (BASE , 'fileA' ))
1817+ self ._check_resolve_relative_link (p , P (BASE , 'fileA' ))
17481818 p = P (BASE , 'dirA' , 'linkC' , 'fileB' )
1749- self ._check_resolve_relative (p , P (BASE , 'dirB' , 'fileB' ))
1819+ self ._check_resolve_relative_link (p , P (BASE , 'dirB' , 'fileB' ))
17501820 p = P (BASE , 'dirB' , 'linkD' , 'fileB' )
1751- self ._check_resolve_relative (p , P (BASE , 'dirB' , 'fileB' ))
1821+ self ._check_resolve_relative_link (p , P (BASE , 'dirB' , 'fileB' ))
17521822 # Non-strict
17531823 p = P (BASE , 'dirA' , 'linkC' , 'fileB' , 'foo' , 'in' , 'spam' )
1754- self ._check_resolve_relative (p , P (BASE , 'dirB' , 'fileB' , 'foo' , 'in' ,
1755- 'spam' ), False )
1824+ self ._check_resolve_relative_link (
1825+ p , P (BASE , 'dirB' , 'fileB' , 'foo' , 'in' , 'spam' ), False
1826+ )
17561827 p = P (BASE , 'dirA' , 'linkC' , '..' , 'foo' , 'in' , 'spam' )
17571828 if os .name == 'nt' :
17581829 # In Windows, if linkY points to dirB, 'dirA\linkY\..'
17591830 # resolves to 'dirA' without resolving linkY first.
1760- self ._check_resolve_relative (p , P (BASE , 'dirA' , 'foo' , 'in' ,
1831+ self ._check_resolve_relative_link (p , P (BASE , 'dirA' , 'foo' , 'in' ,
17611832 'spam' ), False )
17621833 else :
17631834 # In Posix, if linkY points to dirB, 'dirA/linkY/..'
17641835 # resolves to 'dirB/..' first before resolving to parent of dirB.
1765- self ._check_resolve_relative (p , P (BASE , 'foo' , 'in' , 'spam' ), False )
1836+ self ._check_resolve_relative_link (
1837+ p , P (BASE , 'foo' , 'in' , 'spam' ), False
1838+ )
17661839 # Now create absolute symlinks.
17671840 d = os_helper ._longpath (tempfile .mkdtemp (suffix = '-dirD' ,
17681841 dir = os .getcwd ()))
17691842 self .addCleanup (os_helper .rmtree , d )
17701843 os .symlink (os .path .join (d ), join ('dirA' , 'linkX' ))
17711844 os .symlink (join ('dirB' ), os .path .join (d , 'linkY' ))
17721845 p = P (BASE , 'dirA' , 'linkX' , 'linkY' , 'fileB' )
1773- self ._check_resolve_absolute (p , P (BASE , 'dirB' , 'fileB' ))
1846+ self ._check_resolve_absolute_link (p , P (BASE , 'dirB' , 'fileB' ))
17741847 # Non-strict
17751848 p = P (BASE , 'dirA' , 'linkX' , 'linkY' , 'foo' , 'in' , 'spam' )
1776- self ._check_resolve_relative (p , P (BASE , 'dirB' , 'foo' , 'in' , 'spam' ),
1777- False )
1849+ self ._check_resolve_relative_link (
1850+ p , P (BASE , 'dirB' , 'foo' , 'in' , 'spam' ), False
1851+ )
17781852 p = P (BASE , 'dirA' , 'linkX' , 'linkY' , '..' , 'foo' , 'in' , 'spam' )
17791853 if os .name == 'nt' :
17801854 # In Windows, if linkY points to dirB, 'dirA\linkY\..'
17811855 # resolves to 'dirA' without resolving linkY first.
1782- self ._check_resolve_relative (p , P (d , 'foo' , 'in' , 'spam' ), False )
1856+ self ._check_resolve_relative_link (
1857+ p , P (d , 'foo' , 'in' , 'spam' ), False
1858+ )
17831859 else :
17841860 # In Posix, if linkY points to dirB, 'dirA/linkY/..'
17851861 # resolves to 'dirB/..' first before resolving to parent of dirB.
1786- self ._check_resolve_relative (p , P (BASE , 'foo' , 'in' , 'spam' ), False )
1862+ self ._check_resolve_relative_link (
1863+ p , P (BASE , 'foo' , 'in' , 'spam' ), False
1864+ )
17871865
17881866 @os_helper .skip_unless_symlink
17891867 def test_resolve_dot (self ):
0 commit comments