Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GH-89812: Make symlink support configurable in pathlib tests. #106060

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 52 additions & 36 deletions Lib/test/test_pathlib.py
Original file line number Diff line number Diff line change
@@ -1573,6 +1573,7 @@ class PathTest(unittest.TestCase):
"""Tests for the FS-accessing functionalities of the Path classes."""

cls = pathlib.Path
can_symlink = os_helper.can_symlink()

# (BASE)
# |
@@ -1616,7 +1617,7 @@ def cleanup():
with open(join('dirC', 'dirD', 'fileD'), 'wb') as f:
f.write(b"this is file D\n")
os.chmod(join('dirE'), 0)
if os_helper.can_symlink():
if self.can_symlink:
# Relative symlinks.
os.symlink('fileA', join('linkA'))
os.symlink('non-existing', join('brokenLink'))
@@ -1680,7 +1681,7 @@ def test_exists(self):
self.assertIs(True, (p / 'dirA').exists())
self.assertIs(True, (p / 'fileA').exists())
self.assertIs(False, (p / 'fileA' / 'bah').exists())
if os_helper.can_symlink():
if self.can_symlink:
self.assertIs(True, (p / 'linkA').exists())
self.assertIs(True, (p / 'linkB').exists())
self.assertIs(True, (p / 'linkB' / 'fileB').exists())
@@ -1747,12 +1748,13 @@ def test_iterdir(self):
it = p.iterdir()
paths = set(it)
expected = ['dirA', 'dirB', 'dirC', 'dirE', 'fileA']
if os_helper.can_symlink():
if self.can_symlink:
expected += ['linkA', 'linkB', 'brokenLink', 'brokenLinkLoop']
self.assertEqual(paths, { P(BASE, q) for q in expected })

@os_helper.skip_unless_symlink
def test_iterdir_symlink(self):
if not self.can_symlink:
self.skipTest("symlinks required")
# __iter__ on a symlink to a directory.
P = self.cls
p = P(BASE, 'linkB')
@@ -1780,23 +1782,23 @@ def _check(glob, expected):
_check(it, ["fileA"])
_check(p.glob("fileB"), [])
_check(p.glob("dir*/file*"), ["dirB/fileB", "dirC/fileC"])
if not os_helper.can_symlink():
if not self.can_symlink:
_check(p.glob("*A"), ['dirA', 'fileA'])
else:
_check(p.glob("*A"), ['dirA', 'fileA', 'linkA'])
if not os_helper.can_symlink():
if not self.can_symlink:
_check(p.glob("*B/*"), ['dirB/fileB'])
else:
_check(p.glob("*B/*"), ['dirB/fileB', 'dirB/linkD',
'linkB/fileB', 'linkB/linkD'])
if not os_helper.can_symlink():
if not self.can_symlink:
_check(p.glob("*/fileB"), ['dirB/fileB'])
else:
_check(p.glob("*/fileB"), ['dirB/fileB', 'linkB/fileB'])
if os_helper.can_symlink():
if self.can_symlink:
_check(p.glob("brokenLink"), ['brokenLink'])

if not os_helper.can_symlink():
if not self.can_symlink:
_check(p.glob("*/"), ["dirA", "dirB", "dirC", "dirE"])
else:
_check(p.glob("*/"), ["dirA", "dirB", "dirC", "dirE", "linkB"])
@@ -1818,8 +1820,9 @@ def _check(path, pattern, case_sensitive, expected):
_check(path, "dirb/file*", True, [])
_check(path, "dirb/file*", False, ["dirB/fileB"])

@os_helper.skip_unless_symlink
def test_glob_follow_symlinks_common(self):
if not self.can_symlink:
self.skipTest("symlinks required")
def _check(path, glob, expected):
actual = {path for path in path.glob(glob, follow_symlinks=True)
if "linkD" not in path.parent.parts} # exclude symlink loop.
@@ -1843,8 +1846,9 @@ def _check(path, glob, expected):
_check(p, "dir*/*/../dirD/**/", ["dirC/dirD/../dirD"])
_check(p, "*/dirD/**/", ["dirC/dirD"])

@os_helper.skip_unless_symlink
def test_glob_no_follow_symlinks_common(self):
if not self.can_symlink:
self.skipTest("symlinks required")
def _check(path, glob, expected):
actual = {path for path in path.glob(glob, follow_symlinks=False)}
self.assertEqual(actual, { P(BASE, q) for q in expected })
@@ -1876,14 +1880,14 @@ def _check(glob, expected):
_check(p.rglob("fileB"), ["dirB/fileB"])
_check(p.rglob("**/fileB"), ["dirB/fileB"])
_check(p.rglob("*/fileA"), [])
if not os_helper.can_symlink():
if not self.can_symlink:
_check(p.rglob("*/fileB"), ["dirB/fileB"])
else:
_check(p.rglob("*/fileB"), ["dirB/fileB", "dirB/linkD/fileB",
"linkB/fileB", "dirA/linkC/fileB"])
_check(p.rglob("file*"), ["fileA", "dirB/fileB",
"dirC/fileC", "dirC/dirD/fileD"])
if not os_helper.can_symlink():
if not self.can_symlink:
_check(p.rglob("*/"), [
"dirA", "dirB", "dirC", "dirC/dirD", "dirE",
])
@@ -1908,8 +1912,9 @@ def _check(glob, expected):
_check(p.rglob("*.txt"), ["dirC/novel.txt"])
_check(p.rglob("*.*"), ["dirC/novel.txt"])

@os_helper.skip_unless_symlink
def test_rglob_follow_symlinks_common(self):
if not self.can_symlink:
self.skipTest("symlinks required")
def _check(path, glob, expected):
actual = {path for path in path.rglob(glob, follow_symlinks=True)
if 'linkD' not in path.parent.parts} # exclude symlink loop.
@@ -1937,8 +1942,9 @@ def _check(path, glob, expected):
_check(p, "*.txt", ["dirC/novel.txt"])
_check(p, "*.*", ["dirC/novel.txt"])

@os_helper.skip_unless_symlink
def test_rglob_no_follow_symlinks_common(self):
if not self.can_symlink:
self.skipTest("symlinks required")
def _check(path, glob, expected):
actual = {path for path in path.rglob(glob, follow_symlinks=False)}
self.assertEqual(actual, { P(BASE, q) for q in expected })
@@ -1962,9 +1968,10 @@ def _check(path, glob, expected):
_check(p, "*.txt", ["dirC/novel.txt"])
_check(p, "*.*", ["dirC/novel.txt"])

@os_helper.skip_unless_symlink
def test_rglob_symlink_loop(self):
# Don't get fooled by symlink loops (Issue #26012).
if not self.can_symlink:
self.skipTest("symlinks required")
P = self.cls
p = P(BASE)
given = set(p.rglob('*'))
@@ -2011,9 +2018,10 @@ def test_glob_dotdot(self):
self.assertEqual(set(p.glob("xyzzy/..")), set())
self.assertEqual(set(p.glob("/".join([".."] * 50))), { P(BASE, *[".."] * 50)})

@os_helper.skip_unless_symlink
def test_glob_permissions(self):
# See bpo-38894
if not self.can_symlink:
self.skipTest("symlinks required")
P = self.cls
base = P(BASE) / 'permissions'
base.mkdir()
@@ -2031,9 +2039,10 @@ def test_glob_permissions(self):
self.assertEqual(len(set(base.glob("*/fileC"))), 50)
self.assertEqual(len(set(base.glob("*/file*"))), 50)

@os_helper.skip_unless_symlink
def test_glob_long_symlink(self):
# See gh-87695
if not self.can_symlink:
self.skipTest("symlinks required")
base = self.cls(BASE) / 'long_symlink'
base.mkdir()
bad_link = base / 'bad_link'
@@ -2051,8 +2060,9 @@ def test_glob_above_recursion_limit(self):
with set_recursion_limit(recursion_limit):
list(base.glob('**'))

@os_helper.skip_unless_symlink
def test_readlink(self):
if not self.can_symlink:
self.skipTest("symlinks required")
P = self.cls(BASE)
self.assertEqual((P / 'linkA').readlink(), self.cls('fileA'))
self.assertEqual((P / 'brokenLink').readlink(),
@@ -2075,8 +2085,9 @@ def _check_resolve(self, p, expected, strict=True):
# This can be used to check both relative and absolute resolutions.
_check_resolve_relative = _check_resolve_absolute = _check_resolve

@os_helper.skip_unless_symlink
def test_resolve_common(self):
if not self.can_symlink:
self.skipTest("symlinks required")
P = self.cls
p = P(BASE, 'foo')
with self.assertRaises(OSError) as cm:
@@ -2136,9 +2147,10 @@ def test_resolve_common(self):
# resolves to 'dirB/..' first before resolving to parent of dirB.
self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False)

@os_helper.skip_unless_symlink
def test_resolve_dot(self):
# See http://web.archive.org/web/20200623062557/https://bitbucket.org/pitrou/pathlib/issues/9/
if not self.can_symlink:
self.skipTest("symlinks required")
p = self.cls(BASE)
self.dirlink('.', join('0'))
self.dirlink(os.path.join('0', '0'), join('1'))
@@ -2160,8 +2172,9 @@ def test_stat(self):
self.addCleanup(p.chmod, st.st_mode)
self.assertNotEqual(p.stat(), st)

@os_helper.skip_unless_symlink
def test_stat_no_follow_symlinks(self):
if not self.can_symlink:
self.skipTest("symlinks required")
p = self.cls(BASE) / 'linkA'
st = p.stat()
self.assertNotEqual(st, p.stat(follow_symlinks=False))
@@ -2171,8 +2184,9 @@ def test_stat_no_follow_symlinks_nosymlink(self):
st = p.stat()
self.assertEqual(st, p.stat(follow_symlinks=False))

@os_helper.skip_unless_symlink
def test_lstat(self):
if not self.can_symlink:
self.skipTest("symlinks required")
p = self.cls(BASE)/ 'linkA'
st = p.stat()
self.assertNotEqual(st, p.lstat())
@@ -2188,7 +2202,7 @@ def test_is_dir(self):
self.assertFalse((P / 'fileA').is_dir())
self.assertFalse((P / 'non-existing').is_dir())
self.assertFalse((P / 'fileA' / 'bah').is_dir())
if os_helper.can_symlink():
if self.can_symlink:
self.assertFalse((P / 'linkA').is_dir())
self.assertTrue((P / 'linkB').is_dir())
self.assertFalse((P/ 'brokenLink').is_dir())
@@ -2201,7 +2215,7 @@ def test_is_dir_no_follow_symlinks(self):
self.assertFalse((P / 'fileA').is_dir(follow_symlinks=False))
self.assertFalse((P / 'non-existing').is_dir(follow_symlinks=False))
self.assertFalse((P / 'fileA' / 'bah').is_dir(follow_symlinks=False))
if os_helper.can_symlink():
if self.can_symlink:
self.assertFalse((P / 'linkA').is_dir(follow_symlinks=False))
self.assertFalse((P / 'linkB').is_dir(follow_symlinks=False))
self.assertFalse((P/ 'brokenLink').is_dir(follow_symlinks=False))
@@ -2214,7 +2228,7 @@ def test_is_file(self):
self.assertFalse((P / 'dirA').is_file())
self.assertFalse((P / 'non-existing').is_file())
self.assertFalse((P / 'fileA' / 'bah').is_file())
if os_helper.can_symlink():
if self.can_symlink:
self.assertTrue((P / 'linkA').is_file())
self.assertFalse((P / 'linkB').is_file())
self.assertFalse((P/ 'brokenLink').is_file())
@@ -2227,7 +2241,7 @@ def test_is_file_no_follow_symlinks(self):
self.assertFalse((P / 'dirA').is_file(follow_symlinks=False))
self.assertFalse((P / 'non-existing').is_file(follow_symlinks=False))
self.assertFalse((P / 'fileA' / 'bah').is_file(follow_symlinks=False))
if os_helper.can_symlink():
if self.can_symlink:
self.assertFalse((P / 'linkA').is_file(follow_symlinks=False))
self.assertFalse((P / 'linkB').is_file(follow_symlinks=False))
self.assertFalse((P/ 'brokenLink').is_file(follow_symlinks=False))
@@ -2245,7 +2259,7 @@ def test_is_mount(self):
self.assertFalse((P / 'non-existing').is_mount())
self.assertFalse((P / 'fileA' / 'bah').is_mount())
self.assertTrue(R.is_mount())
if os_helper.can_symlink():
if self.can_symlink:
self.assertFalse((P / 'linkA').is_mount())
self.assertIs((R / '\udfff').is_mount(), False)

@@ -2255,13 +2269,13 @@ def test_is_symlink(self):
self.assertFalse((P / 'dirA').is_symlink())
self.assertFalse((P / 'non-existing').is_symlink())
self.assertFalse((P / 'fileA' / 'bah').is_symlink())
if os_helper.can_symlink():
if self.can_symlink:
self.assertTrue((P / 'linkA').is_symlink())
self.assertTrue((P / 'linkB').is_symlink())
self.assertTrue((P/ 'brokenLink').is_symlink())
self.assertIs((P / 'fileA\udfff').is_file(), False)
self.assertIs((P / 'fileA\x00').is_file(), False)
if os_helper.can_symlink():
if self.can_symlink:
self.assertIs((P / 'linkA\udfff').is_file(), False)
self.assertIs((P / 'linkA\x00').is_file(), False)

@@ -2318,6 +2332,9 @@ def test_parts_interning(self):
self.assertIs(p.parts[2], q.parts[3])

def _check_complex_symlinks(self, link0_target):
if not self.can_symlink:
self.skipTest("symlinks required")

# Test solving a non-looping chain of symlinks (issue #19887).
P = self.cls(BASE)
self.dirlink(os.path.join('link0', 'link0'), join('link1'))
@@ -2358,15 +2375,12 @@ def _check_complex_symlinks(self, link0_target):
finally:
os.chdir(old_path)

@os_helper.skip_unless_symlink
def test_complex_symlinks_absolute(self):
self._check_complex_symlinks(BASE)

@os_helper.skip_unless_symlink
def test_complex_symlinks_relative(self):
self._check_complex_symlinks('.')

@os_helper.skip_unless_symlink
def test_complex_symlinks_relative_dot_dot(self):
self._check_complex_symlinks(os.path.join('dirA', '..'))

@@ -2470,7 +2484,7 @@ def with_segments(self, *pathsegments):
self.assertEqual(42, p.with_segments('~').expanduser().session_id)
self.assertEqual(42, (p / 'fileA').rename(p / 'fileB').session_id)
self.assertEqual(42, (p / 'fileB').replace(p / 'fileA').session_id)
if os_helper.can_symlink():
if self.can_symlink:
self.assertEqual(42, (p / 'linkA').readlink().session_id)
for path in p.iterdir():
self.assertEqual(42, path.session_id)
@@ -2791,8 +2805,9 @@ def my_mkdir(path, mode=0o777):
self.assertNotIn(str(p12), concurrently_created)
self.assertTrue(p.exists())

@os_helper.skip_unless_symlink
def test_symlink_to(self):
if not self.can_symlink:
self.skipTest("symlinks required")
P = self.cls(BASE)
target = P / 'fileA'
# Symlinking a path target.
@@ -3175,8 +3190,9 @@ def test_touch_mode(self):
st = os.stat(join('masked_new_file'))
self.assertEqual(stat.S_IMODE(st.st_mode), 0o750)

@os_helper.skip_unless_symlink
def test_resolve_loop(self):
if not self.can_symlink:
self.skipTest("symlinks required")
# Loops with relative symlinks.
os.symlink('linkX/inside', join('linkX'))
self._check_symlink_loop(BASE, 'linkX')