Skip to content

Commit 3fd99b5

Browse files
authored
GH-89812: Make symlink support configurable in pathlib tests. (GH-106060)
Adjust the pathlib tests to add a new `PathTest.can_symlink` class attribute, which allows us to enable or disable symlink support in tests. A (near-)future commit will add an `AbstractPath` class; its tests will hard-code the value to `True` or `False` depending on a stub subclass's capabilities.
1 parent 200f255 commit 3fd99b5

File tree

1 file changed

+52
-36
lines changed

1 file changed

+52
-36
lines changed

Lib/test/test_pathlib.py

Lines changed: 52 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1573,6 +1573,7 @@ class PathTest(unittest.TestCase):
15731573
"""Tests for the FS-accessing functionalities of the Path classes."""
15741574

15751575
cls = pathlib.Path
1576+
can_symlink = os_helper.can_symlink()
15761577

15771578
# (BASE)
15781579
# |
@@ -1616,7 +1617,7 @@ def cleanup():
16161617
with open(join('dirC', 'dirD', 'fileD'), 'wb') as f:
16171618
f.write(b"this is file D\n")
16181619
os.chmod(join('dirE'), 0)
1619-
if os_helper.can_symlink():
1620+
if self.can_symlink:
16201621
# Relative symlinks.
16211622
os.symlink('fileA', join('linkA'))
16221623
os.symlink('non-existing', join('brokenLink'))
@@ -1672,7 +1673,7 @@ def test_exists(self):
16721673
self.assertIs(True, (p / 'dirA').exists())
16731674
self.assertIs(True, (p / 'fileA').exists())
16741675
self.assertIs(False, (p / 'fileA' / 'bah').exists())
1675-
if os_helper.can_symlink():
1676+
if self.can_symlink:
16761677
self.assertIs(True, (p / 'linkA').exists())
16771678
self.assertIs(True, (p / 'linkB').exists())
16781679
self.assertIs(True, (p / 'linkB' / 'fileB').exists())
@@ -1739,12 +1740,13 @@ def test_iterdir(self):
17391740
it = p.iterdir()
17401741
paths = set(it)
17411742
expected = ['dirA', 'dirB', 'dirC', 'dirE', 'fileA']
1742-
if os_helper.can_symlink():
1743+
if self.can_symlink:
17431744
expected += ['linkA', 'linkB', 'brokenLink', 'brokenLinkLoop']
17441745
self.assertEqual(paths, { P(BASE, q) for q in expected })
17451746

1746-
@os_helper.skip_unless_symlink
17471747
def test_iterdir_symlink(self):
1748+
if not self.can_symlink:
1749+
self.skipTest("symlinks required")
17481750
# __iter__ on a symlink to a directory.
17491751
P = self.cls
17501752
p = P(BASE, 'linkB')
@@ -1772,23 +1774,23 @@ def _check(glob, expected):
17721774
_check(it, ["fileA"])
17731775
_check(p.glob("fileB"), [])
17741776
_check(p.glob("dir*/file*"), ["dirB/fileB", "dirC/fileC"])
1775-
if not os_helper.can_symlink():
1777+
if not self.can_symlink:
17761778
_check(p.glob("*A"), ['dirA', 'fileA'])
17771779
else:
17781780
_check(p.glob("*A"), ['dirA', 'fileA', 'linkA'])
1779-
if not os_helper.can_symlink():
1781+
if not self.can_symlink:
17801782
_check(p.glob("*B/*"), ['dirB/fileB'])
17811783
else:
17821784
_check(p.glob("*B/*"), ['dirB/fileB', 'dirB/linkD',
17831785
'linkB/fileB', 'linkB/linkD'])
1784-
if not os_helper.can_symlink():
1786+
if not self.can_symlink:
17851787
_check(p.glob("*/fileB"), ['dirB/fileB'])
17861788
else:
17871789
_check(p.glob("*/fileB"), ['dirB/fileB', 'linkB/fileB'])
1788-
if os_helper.can_symlink():
1790+
if self.can_symlink:
17891791
_check(p.glob("brokenLink"), ['brokenLink'])
17901792

1791-
if not os_helper.can_symlink():
1793+
if not self.can_symlink:
17921794
_check(p.glob("*/"), ["dirA", "dirB", "dirC", "dirE"])
17931795
else:
17941796
_check(p.glob("*/"), ["dirA", "dirB", "dirC", "dirE", "linkB"])
@@ -1810,8 +1812,9 @@ def _check(path, pattern, case_sensitive, expected):
18101812
_check(path, "dirb/file*", True, [])
18111813
_check(path, "dirb/file*", False, ["dirB/fileB"])
18121814

1813-
@os_helper.skip_unless_symlink
18141815
def test_glob_follow_symlinks_common(self):
1816+
if not self.can_symlink:
1817+
self.skipTest("symlinks required")
18151818
def _check(path, glob, expected):
18161819
actual = {path for path in path.glob(glob, follow_symlinks=True)
18171820
if "linkD" not in path.parent.parts} # exclude symlink loop.
@@ -1835,8 +1838,9 @@ def _check(path, glob, expected):
18351838
_check(p, "dir*/*/../dirD/**/", ["dirC/dirD/../dirD"])
18361839
_check(p, "*/dirD/**/", ["dirC/dirD"])
18371840

1838-
@os_helper.skip_unless_symlink
18391841
def test_glob_no_follow_symlinks_common(self):
1842+
if not self.can_symlink:
1843+
self.skipTest("symlinks required")
18401844
def _check(path, glob, expected):
18411845
actual = {path for path in path.glob(glob, follow_symlinks=False)}
18421846
self.assertEqual(actual, { P(BASE, q) for q in expected })
@@ -1868,14 +1872,14 @@ def _check(glob, expected):
18681872
_check(p.rglob("fileB"), ["dirB/fileB"])
18691873
_check(p.rglob("**/fileB"), ["dirB/fileB"])
18701874
_check(p.rglob("*/fileA"), [])
1871-
if not os_helper.can_symlink():
1875+
if not self.can_symlink:
18721876
_check(p.rglob("*/fileB"), ["dirB/fileB"])
18731877
else:
18741878
_check(p.rglob("*/fileB"), ["dirB/fileB", "dirB/linkD/fileB",
18751879
"linkB/fileB", "dirA/linkC/fileB"])
18761880
_check(p.rglob("file*"), ["fileA", "dirB/fileB",
18771881
"dirC/fileC", "dirC/dirD/fileD"])
1878-
if not os_helper.can_symlink():
1882+
if not self.can_symlink:
18791883
_check(p.rglob("*/"), [
18801884
"dirA", "dirB", "dirC", "dirC/dirD", "dirE",
18811885
])
@@ -1900,8 +1904,9 @@ def _check(glob, expected):
19001904
_check(p.rglob("*.txt"), ["dirC/novel.txt"])
19011905
_check(p.rglob("*.*"), ["dirC/novel.txt"])
19021906

1903-
@os_helper.skip_unless_symlink
19041907
def test_rglob_follow_symlinks_common(self):
1908+
if not self.can_symlink:
1909+
self.skipTest("symlinks required")
19051910
def _check(path, glob, expected):
19061911
actual = {path for path in path.rglob(glob, follow_symlinks=True)
19071912
if 'linkD' not in path.parent.parts} # exclude symlink loop.
@@ -1929,8 +1934,9 @@ def _check(path, glob, expected):
19291934
_check(p, "*.txt", ["dirC/novel.txt"])
19301935
_check(p, "*.*", ["dirC/novel.txt"])
19311936

1932-
@os_helper.skip_unless_symlink
19331937
def test_rglob_no_follow_symlinks_common(self):
1938+
if not self.can_symlink:
1939+
self.skipTest("symlinks required")
19341940
def _check(path, glob, expected):
19351941
actual = {path for path in path.rglob(glob, follow_symlinks=False)}
19361942
self.assertEqual(actual, { P(BASE, q) for q in expected })
@@ -1954,9 +1960,10 @@ def _check(path, glob, expected):
19541960
_check(p, "*.txt", ["dirC/novel.txt"])
19551961
_check(p, "*.*", ["dirC/novel.txt"])
19561962

1957-
@os_helper.skip_unless_symlink
19581963
def test_rglob_symlink_loop(self):
19591964
# Don't get fooled by symlink loops (Issue #26012).
1965+
if not self.can_symlink:
1966+
self.skipTest("symlinks required")
19601967
P = self.cls
19611968
p = P(BASE)
19621969
given = set(p.rglob('*'))
@@ -2003,9 +2010,10 @@ def test_glob_dotdot(self):
20032010
self.assertEqual(set(p.glob("xyzzy/..")), set())
20042011
self.assertEqual(set(p.glob("/".join([".."] * 50))), { P(BASE, *[".."] * 50)})
20052012

2006-
@os_helper.skip_unless_symlink
20072013
def test_glob_permissions(self):
20082014
# See bpo-38894
2015+
if not self.can_symlink:
2016+
self.skipTest("symlinks required")
20092017
P = self.cls
20102018
base = P(BASE) / 'permissions'
20112019
base.mkdir()
@@ -2023,9 +2031,10 @@ def test_glob_permissions(self):
20232031
self.assertEqual(len(set(base.glob("*/fileC"))), 50)
20242032
self.assertEqual(len(set(base.glob("*/file*"))), 50)
20252033

2026-
@os_helper.skip_unless_symlink
20272034
def test_glob_long_symlink(self):
20282035
# See gh-87695
2036+
if not self.can_symlink:
2037+
self.skipTest("symlinks required")
20292038
base = self.cls(BASE) / 'long_symlink'
20302039
base.mkdir()
20312040
bad_link = base / 'bad_link'
@@ -2043,8 +2052,9 @@ def test_glob_above_recursion_limit(self):
20432052
with set_recursion_limit(recursion_limit):
20442053
list(base.glob('**'))
20452054

2046-
@os_helper.skip_unless_symlink
20472055
def test_readlink(self):
2056+
if not self.can_symlink:
2057+
self.skipTest("symlinks required")
20482058
P = self.cls(BASE)
20492059
self.assertEqual((P / 'linkA').readlink(), self.cls('fileA'))
20502060
self.assertEqual((P / 'brokenLink').readlink(),
@@ -2067,8 +2077,9 @@ def _check_resolve(self, p, expected, strict=True):
20672077
# This can be used to check both relative and absolute resolutions.
20682078
_check_resolve_relative = _check_resolve_absolute = _check_resolve
20692079

2070-
@os_helper.skip_unless_symlink
20712080
def test_resolve_common(self):
2081+
if not self.can_symlink:
2082+
self.skipTest("symlinks required")
20722083
P = self.cls
20732084
p = P(BASE, 'foo')
20742085
with self.assertRaises(OSError) as cm:
@@ -2128,9 +2139,10 @@ def test_resolve_common(self):
21282139
# resolves to 'dirB/..' first before resolving to parent of dirB.
21292140
self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False)
21302141

2131-
@os_helper.skip_unless_symlink
21322142
def test_resolve_dot(self):
21332143
# See http://web.archive.org/web/20200623062557/https://bitbucket.org/pitrou/pathlib/issues/9/
2144+
if not self.can_symlink:
2145+
self.skipTest("symlinks required")
21342146
p = self.cls(BASE)
21352147
p.joinpath('0').symlink_to('.', target_is_directory=True)
21362148
p.joinpath('1').symlink_to(os.path.join('0', '0'), target_is_directory=True)
@@ -2152,8 +2164,9 @@ def test_stat(self):
21522164
self.addCleanup(p.chmod, st.st_mode)
21532165
self.assertNotEqual(p.stat(), st)
21542166

2155-
@os_helper.skip_unless_symlink
21562167
def test_stat_no_follow_symlinks(self):
2168+
if not self.can_symlink:
2169+
self.skipTest("symlinks required")
21572170
p = self.cls(BASE) / 'linkA'
21582171
st = p.stat()
21592172
self.assertNotEqual(st, p.stat(follow_symlinks=False))
@@ -2163,8 +2176,9 @@ def test_stat_no_follow_symlinks_nosymlink(self):
21632176
st = p.stat()
21642177
self.assertEqual(st, p.stat(follow_symlinks=False))
21652178

2166-
@os_helper.skip_unless_symlink
21672179
def test_lstat(self):
2180+
if not self.can_symlink:
2181+
self.skipTest("symlinks required")
21682182
p = self.cls(BASE)/ 'linkA'
21692183
st = p.stat()
21702184
self.assertNotEqual(st, p.lstat())
@@ -2180,7 +2194,7 @@ def test_is_dir(self):
21802194
self.assertFalse((P / 'fileA').is_dir())
21812195
self.assertFalse((P / 'non-existing').is_dir())
21822196
self.assertFalse((P / 'fileA' / 'bah').is_dir())
2183-
if os_helper.can_symlink():
2197+
if self.can_symlink:
21842198
self.assertFalse((P / 'linkA').is_dir())
21852199
self.assertTrue((P / 'linkB').is_dir())
21862200
self.assertFalse((P/ 'brokenLink').is_dir())
@@ -2193,7 +2207,7 @@ def test_is_dir_no_follow_symlinks(self):
21932207
self.assertFalse((P / 'fileA').is_dir(follow_symlinks=False))
21942208
self.assertFalse((P / 'non-existing').is_dir(follow_symlinks=False))
21952209
self.assertFalse((P / 'fileA' / 'bah').is_dir(follow_symlinks=False))
2196-
if os_helper.can_symlink():
2210+
if self.can_symlink:
21972211
self.assertFalse((P / 'linkA').is_dir(follow_symlinks=False))
21982212
self.assertFalse((P / 'linkB').is_dir(follow_symlinks=False))
21992213
self.assertFalse((P/ 'brokenLink').is_dir(follow_symlinks=False))
@@ -2206,7 +2220,7 @@ def test_is_file(self):
22062220
self.assertFalse((P / 'dirA').is_file())
22072221
self.assertFalse((P / 'non-existing').is_file())
22082222
self.assertFalse((P / 'fileA' / 'bah').is_file())
2209-
if os_helper.can_symlink():
2223+
if self.can_symlink:
22102224
self.assertTrue((P / 'linkA').is_file())
22112225
self.assertFalse((P / 'linkB').is_file())
22122226
self.assertFalse((P/ 'brokenLink').is_file())
@@ -2219,7 +2233,7 @@ def test_is_file_no_follow_symlinks(self):
22192233
self.assertFalse((P / 'dirA').is_file(follow_symlinks=False))
22202234
self.assertFalse((P / 'non-existing').is_file(follow_symlinks=False))
22212235
self.assertFalse((P / 'fileA' / 'bah').is_file(follow_symlinks=False))
2222-
if os_helper.can_symlink():
2236+
if self.can_symlink:
22232237
self.assertFalse((P / 'linkA').is_file(follow_symlinks=False))
22242238
self.assertFalse((P / 'linkB').is_file(follow_symlinks=False))
22252239
self.assertFalse((P/ 'brokenLink').is_file(follow_symlinks=False))
@@ -2237,7 +2251,7 @@ def test_is_mount(self):
22372251
self.assertFalse((P / 'non-existing').is_mount())
22382252
self.assertFalse((P / 'fileA' / 'bah').is_mount())
22392253
self.assertTrue(R.is_mount())
2240-
if os_helper.can_symlink():
2254+
if self.can_symlink:
22412255
self.assertFalse((P / 'linkA').is_mount())
22422256
self.assertIs((R / '\udfff').is_mount(), False)
22432257

@@ -2247,13 +2261,13 @@ def test_is_symlink(self):
22472261
self.assertFalse((P / 'dirA').is_symlink())
22482262
self.assertFalse((P / 'non-existing').is_symlink())
22492263
self.assertFalse((P / 'fileA' / 'bah').is_symlink())
2250-
if os_helper.can_symlink():
2264+
if self.can_symlink:
22512265
self.assertTrue((P / 'linkA').is_symlink())
22522266
self.assertTrue((P / 'linkB').is_symlink())
22532267
self.assertTrue((P/ 'brokenLink').is_symlink())
22542268
self.assertIs((P / 'fileA\udfff').is_file(), False)
22552269
self.assertIs((P / 'fileA\x00').is_file(), False)
2256-
if os_helper.can_symlink():
2270+
if self.can_symlink:
22572271
self.assertIs((P / 'linkA\udfff').is_file(), False)
22582272
self.assertIs((P / 'linkA\x00').is_file(), False)
22592273

@@ -2310,6 +2324,9 @@ def test_parts_interning(self):
23102324
self.assertIs(p.parts[2], q.parts[3])
23112325

23122326
def _check_complex_symlinks(self, link0_target):
2327+
if not self.can_symlink:
2328+
self.skipTest("symlinks required")
2329+
23132330
# Test solving a non-looping chain of symlinks (issue #19887).
23142331
P = self.cls(BASE)
23152332
P.joinpath('link1').symlink_to(os.path.join('link0', 'link0'), target_is_directory=True)
@@ -2350,15 +2367,12 @@ def _check_complex_symlinks(self, link0_target):
23502367
finally:
23512368
os.chdir(old_path)
23522369

2353-
@os_helper.skip_unless_symlink
23542370
def test_complex_symlinks_absolute(self):
23552371
self._check_complex_symlinks(BASE)
23562372

2357-
@os_helper.skip_unless_symlink
23582373
def test_complex_symlinks_relative(self):
23592374
self._check_complex_symlinks('.')
23602375

2361-
@os_helper.skip_unless_symlink
23622376
def test_complex_symlinks_relative_dot_dot(self):
23632377
self._check_complex_symlinks(os.path.join('dirA', '..'))
23642378

@@ -2462,7 +2476,7 @@ def with_segments(self, *pathsegments):
24622476
self.assertEqual(42, p.with_segments('~').expanduser().session_id)
24632477
self.assertEqual(42, (p / 'fileA').rename(p / 'fileB').session_id)
24642478
self.assertEqual(42, (p / 'fileB').replace(p / 'fileA').session_id)
2465-
if os_helper.can_symlink():
2479+
if self.can_symlink:
24662480
self.assertEqual(42, (p / 'linkA').readlink().session_id)
24672481
for path in p.iterdir():
24682482
self.assertEqual(42, path.session_id)
@@ -2783,8 +2797,9 @@ def my_mkdir(path, mode=0o777):
27832797
self.assertNotIn(str(p12), concurrently_created)
27842798
self.assertTrue(p.exists())
27852799

2786-
@os_helper.skip_unless_symlink
27872800
def test_symlink_to(self):
2801+
if not self.can_symlink:
2802+
self.skipTest("symlinks required")
27882803
P = self.cls(BASE)
27892804
target = P / 'fileA'
27902805
# Symlinking a path target.
@@ -3167,8 +3182,9 @@ def test_touch_mode(self):
31673182
st = os.stat(join('masked_new_file'))
31683183
self.assertEqual(stat.S_IMODE(st.st_mode), 0o750)
31693184

3170-
@os_helper.skip_unless_symlink
31713185
def test_resolve_loop(self):
3186+
if not self.can_symlink:
3187+
self.skipTest("symlinks required")
31723188
# Loops with relative symlinks.
31733189
os.symlink('linkX/inside', join('linkX'))
31743190
self._check_symlink_loop(BASE, 'linkX')

0 commit comments

Comments
 (0)