@@ -1328,6 +1328,7 @@ def test_different_flavours_unordered(self):
1328
1328
1329
1329
# Make sure any symbolic links in the base test path are resolved.
1330
1330
BASE = os .path .realpath (TESTFN )
1331
+ REL_BASE = TESTFN
1331
1332
join = lambda * x : os .path .join (BASE , * x )
1332
1333
rel_join = lambda * x : os .path .join (TESTFN , * x )
1333
1334
@@ -1721,11 +1722,80 @@ def _check_resolve(self, p, expected, strict=True):
1721
1722
q = p .resolve (strict )
1722
1723
self .assertEqual (q , expected )
1723
1724
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' ))
1726
1796
1727
1797
@os_helper .skip_unless_symlink
1728
- def test_resolve_common (self ):
1798
+ def test_resolve_symlinks (self ):
1729
1799
P = self .cls
1730
1800
p = P (BASE , 'foo' )
1731
1801
with self .assertRaises (OSError ) as cm :
@@ -1742,48 +1812,56 @@ def test_resolve_common(self):
1742
1812
os .path .abspath (os .path .join ('foo' , 'in' , 'spam' )))
1743
1813
# These are all relative symlinks.
1744
1814
p = P (BASE , 'dirB' , 'fileB' )
1745
- self ._check_resolve_relative (p , p )
1815
+ self ._check_resolve (p , p )
1746
1816
p = P (BASE , 'linkA' )
1747
- self ._check_resolve_relative (p , P (BASE , 'fileA' ))
1817
+ self ._check_resolve_relative_link (p , P (BASE , 'fileA' ))
1748
1818
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' ))
1750
1820
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' ))
1752
1822
# Non-strict
1753
1823
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
+ )
1756
1827
p = P (BASE , 'dirA' , 'linkC' , '..' , 'foo' , 'in' , 'spam' )
1757
1828
if os .name == 'nt' :
1758
1829
# In Windows, if linkY points to dirB, 'dirA\linkY\..'
1759
1830
# 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' ,
1761
1832
'spam' ), False )
1762
1833
else :
1763
1834
# In Posix, if linkY points to dirB, 'dirA/linkY/..'
1764
1835
# 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
+ )
1766
1839
# Now create absolute symlinks.
1767
1840
d = os_helper ._longpath (tempfile .mkdtemp (suffix = '-dirD' ,
1768
1841
dir = os .getcwd ()))
1769
1842
self .addCleanup (os_helper .rmtree , d )
1770
1843
os .symlink (os .path .join (d ), join ('dirA' , 'linkX' ))
1771
1844
os .symlink (join ('dirB' ), os .path .join (d , 'linkY' ))
1772
1845
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' ))
1774
1847
# Non-strict
1775
1848
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
+ )
1778
1852
p = P (BASE , 'dirA' , 'linkX' , 'linkY' , '..' , 'foo' , 'in' , 'spam' )
1779
1853
if os .name == 'nt' :
1780
1854
# In Windows, if linkY points to dirB, 'dirA\linkY\..'
1781
1855
# 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
+ )
1783
1859
else :
1784
1860
# In Posix, if linkY points to dirB, 'dirA/linkY/..'
1785
1861
# 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
+ )
1787
1865
1788
1866
@os_helper .skip_unless_symlink
1789
1867
def test_resolve_dot (self ):
0 commit comments