Skip to content

Commit 1e37e3b

Browse files
committed
bpo-38671: Add regression tests for Path.resolve in pathlib
Bug was fixed by pythonGH-25264 which, for other reasons, moved to a system independent resolve. Adds tests for verifying resolve() on cases other than for symlinks. New cases include relative and absolute paths with and without dotted (./..) paths. Also renames test helper function to avoid name overloading and adds new REL_PATH test constant.
1 parent ee8e7c2 commit 1e37e3b

File tree

1 file changed

+94
-16
lines changed

1 file changed

+94
-16
lines changed

Lib/test/test_pathlib.py

Lines changed: 94 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1328,6 +1328,7 @@ def test_different_flavours_unordered(self):
13281328

13291329
# Make sure any symbolic links in the base test path are resolved.
13301330
BASE = os.path.realpath(TESTFN)
1331+
REL_BASE = TESTFN
13311332
join = lambda *x: os.path.join(BASE, *x)
13321333
rel_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

Comments
 (0)