1111from test .support .os_helper import TESTFN
1212
1313
14+ _tests_needing_posix = set ()
15+ _tests_needing_windows = set ()
16+ _tests_needing_symlinks = set ()
17+
18+
19+ def needs_posix (fn ):
20+ """Decorator that marks a test as requiring a POSIX-flavoured path class."""
21+ _tests_needing_posix .add (fn .__name__ )
22+ return fn
23+
24+ def needs_windows (fn ):
25+ """Decorator that marks a test as requiring a Windows-flavoured path class."""
26+ _tests_needing_windows .add (fn .__name__ )
27+ return fn
28+
29+ def needs_symlinks (fn ):
30+ """Decorator that marks a test as requiring a path class that supports symlinks."""
31+ _tests_needing_symlinks .add (fn .__name__ )
32+ return fn
33+
34+
1435class UnsupportedOperationTest (unittest .TestCase ):
1536 def test_is_notimplemented (self ):
1637 self .assertTrue (issubclass (UnsupportedOperation , NotImplementedError ))
@@ -115,6 +136,11 @@ class DummyPurePathTest(unittest.TestCase):
115136 base = f'/this/path/kills/fascists/{ TESTFN } '
116137
117138 def setUp (self ):
139+ name = self .id ().split ('.' )[- 1 ]
140+ if name in _tests_needing_posix and self .cls .pathmod is not posixpath :
141+ self .skipTest ('requires POSIX-flavoured path class' )
142+ if name in _tests_needing_windows and self .cls .pathmod is posixpath :
143+ self .skipTest ('requires Windows-flavoured path class' )
118144 p = self .cls ('a' )
119145 self .pathmod = p .pathmod
120146 self .sep = self .pathmod .sep
@@ -888,6 +914,9 @@ class DummyPathTest(DummyPurePathTest):
888914
889915 def setUp (self ):
890916 super ().setUp ()
917+ name = self .id ().split ('.' )[- 1 ]
918+ if name in _tests_needing_symlinks and not self .can_symlink :
919+ self .skipTest ('requires symlinks' )
891920 pathmod = self .cls .pathmod
892921 p = self .cls (self .base )
893922 p .mkdir (parents = True )
@@ -1045,9 +1074,8 @@ def test_iterdir(self):
10451074 expected += ['linkA' , 'linkB' , 'brokenLink' , 'brokenLinkLoop' ]
10461075 self .assertEqual (paths , { P (self .base , q ) for q in expected })
10471076
1077+ @needs_symlinks
10481078 def test_iterdir_symlink (self ):
1049- if not self .can_symlink :
1050- self .skipTest ("symlinks required" )
10511079 # __iter__ on a symlink to a directory.
10521080 P = self .cls
10531081 p = P (self .base , 'linkB' )
@@ -1116,9 +1144,8 @@ def _check(path, pattern, case_sensitive, expected):
11161144 _check (path , "dirb/file*" , True , [])
11171145 _check (path , "dirb/file*" , False , ["dirB/fileB" ])
11181146
1147+ @needs_symlinks
11191148 def test_glob_follow_symlinks_common (self ):
1120- if not self .can_symlink :
1121- self .skipTest ("symlinks required" )
11221149 def _check (path , glob , expected ):
11231150 actual = {path for path in path .glob (glob , follow_symlinks = True )
11241151 if path .parts .count ("linkD" ) <= 1 } # exclude symlink loop.
@@ -1144,9 +1171,8 @@ def _check(path, glob, expected):
11441171 _check (p , "dir*/*/../dirD/**/" , ["dirC/dirD/../dirD/" ])
11451172 _check (p , "*/dirD/**/" , ["dirC/dirD/" ])
11461173
1174+ @needs_symlinks
11471175 def test_glob_no_follow_symlinks_common (self ):
1148- if not self .can_symlink :
1149- self .skipTest ("symlinks required" )
11501176 def _check (path , glob , expected ):
11511177 actual = {path for path in path .glob (glob , follow_symlinks = False )}
11521178 self .assertEqual (actual , { P (self .base , q ) for q in expected })
@@ -1210,9 +1236,8 @@ def _check(glob, expected):
12101236 _check (p .rglob ("*.txt" ), ["dirC/novel.txt" ])
12111237 _check (p .rglob ("*.*" ), ["dirC/novel.txt" ])
12121238
1239+ @needs_symlinks
12131240 def test_rglob_follow_symlinks_common (self ):
1214- if not self .can_symlink :
1215- self .skipTest ("symlinks required" )
12161241 def _check (path , glob , expected ):
12171242 actual = {path for path in path .rglob (glob , follow_symlinks = True )
12181243 if path .parts .count ("linkD" ) <= 1 } # exclude symlink loop.
@@ -1243,9 +1268,8 @@ def _check(path, glob, expected):
12431268 _check (p , "*.txt" , ["dirC/novel.txt" ])
12441269 _check (p , "*.*" , ["dirC/novel.txt" ])
12451270
1271+ @needs_symlinks
12461272 def test_rglob_no_follow_symlinks_common (self ):
1247- if not self .can_symlink :
1248- self .skipTest ("symlinks required" )
12491273 def _check (path , glob , expected ):
12501274 actual = {path for path in path .rglob (glob , follow_symlinks = False )}
12511275 self .assertEqual (actual , { P (self .base , q ) for q in expected })
@@ -1269,10 +1293,9 @@ def _check(path, glob, expected):
12691293 _check (p , "*.txt" , ["dirC/novel.txt" ])
12701294 _check (p , "*.*" , ["dirC/novel.txt" ])
12711295
1296+ @needs_symlinks
12721297 def test_rglob_symlink_loop (self ):
12731298 # Don't get fooled by symlink loops (Issue #26012).
1274- if not self .can_symlink :
1275- self .skipTest ("symlinks required" )
12761299 P = self .cls
12771300 p = P (self .base )
12781301 given = set (p .rglob ('*' ))
@@ -1302,10 +1325,9 @@ def test_glob_dotdot(self):
13021325 self .assertEqual (set (p .glob ("xyzzy/.." )), set ())
13031326 self .assertEqual (set (p .glob ("/" .join ([".." ] * 50 ))), { P (self .base , * [".." ] * 50 )})
13041327
1328+ @needs_symlinks
13051329 def test_glob_permissions (self ):
13061330 # See bpo-38894
1307- if not self .can_symlink :
1308- self .skipTest ("symlinks required" )
13091331 P = self .cls
13101332 base = P (self .base ) / 'permissions'
13111333 base .mkdir ()
@@ -1322,19 +1344,17 @@ def test_glob_permissions(self):
13221344 self .assertEqual (len (set (base .glob ("*/fileC" ))), 50 )
13231345 self .assertEqual (len (set (base .glob ("*/file*" ))), 50 )
13241346
1347+ @needs_symlinks
13251348 def test_glob_long_symlink (self ):
13261349 # See gh-87695
1327- if not self .can_symlink :
1328- self .skipTest ("symlinks required" )
13291350 base = self .cls (self .base ) / 'long_symlink'
13301351 base .mkdir ()
13311352 bad_link = base / 'bad_link'
13321353 bad_link .symlink_to ("bad" * 200 )
13331354 self .assertEqual (sorted (base .glob ('**/*' )), [bad_link ])
13341355
1356+ @needs_symlinks
13351357 def test_readlink (self ):
1336- if not self .can_symlink :
1337- self .skipTest ("symlinks required" )
13381358 P = self .cls (self .base )
13391359 self .assertEqual ((P / 'linkA' ).readlink (), self .cls ('fileA' ))
13401360 self .assertEqual ((P / 'brokenLink' ).readlink (),
@@ -1358,9 +1378,8 @@ def _check_resolve(self, p, expected, strict=True):
13581378 # This can be used to check both relative and absolute resolutions.
13591379 _check_resolve_relative = _check_resolve_absolute = _check_resolve
13601380
1381+ @needs_symlinks
13611382 def test_resolve_common (self ):
1362- if not self .can_symlink :
1363- self .skipTest ("symlinks required" )
13641383 P = self .cls
13651384 p = P (self .base , 'foo' )
13661385 with self .assertRaises (OSError ) as cm :
@@ -1419,10 +1438,9 @@ def test_resolve_common(self):
14191438 # resolves to 'dirB/..' first before resolving to parent of dirB.
14201439 self ._check_resolve_relative (p , P (self .base , 'foo' , 'in' , 'spam' ), False )
14211440
1441+ @needs_symlinks
14221442 def test_resolve_dot (self ):
14231443 # See http://web.archive.org/web/20200623062557/https://bitbucket.org/pitrou/pathlib/issues/9/
1424- if not self .can_symlink :
1425- self .skipTest ("symlinks required" )
14261444 pathmod = self .pathmod
14271445 p = self .cls (self .base )
14281446 p .joinpath ('0' ).symlink_to ('.' , target_is_directory = True )
@@ -1441,11 +1459,9 @@ def _check_symlink_loop(self, *args):
14411459 path .resolve (strict = True )
14421460 self .assertEqual (cm .exception .errno , errno .ELOOP )
14431461
1462+ @needs_posix
1463+ @needs_symlinks
14441464 def test_resolve_loop (self ):
1445- if not self .can_symlink :
1446- self .skipTest ("symlinks required" )
1447- if self .cls .pathmod is not posixpath :
1448- self .skipTest ("symlink loops work differently with concrete Windows paths" )
14491465 # Loops with relative symlinks.
14501466 self .cls (self .base , 'linkX' ).symlink_to ('linkX/inside' )
14511467 self ._check_symlink_loop (self .base , 'linkX' )
@@ -1487,9 +1503,8 @@ def test_stat(self):
14871503 self .assertEqual (statA .st_dev , statC .st_dev )
14881504 # other attributes not used by pathlib.
14891505
1506+ @needs_symlinks
14901507 def test_stat_no_follow_symlinks (self ):
1491- if not self .can_symlink :
1492- self .skipTest ("symlinks required" )
14931508 p = self .cls (self .base ) / 'linkA'
14941509 st = p .stat ()
14951510 self .assertNotEqual (st , p .stat (follow_symlinks = False ))
@@ -1499,9 +1514,8 @@ def test_stat_no_follow_symlinks_nosymlink(self):
14991514 st = p .stat ()
15001515 self .assertEqual (st , p .stat (follow_symlinks = False ))
15011516
1517+ @needs_symlinks
15021518 def test_lstat (self ):
1503- if not self .can_symlink :
1504- self .skipTest ("symlinks required" )
15051519 p = self .cls (self .base )/ 'linkA'
15061520 st = p .stat ()
15071521 self .assertNotEqual (st , p .lstat ())
@@ -1634,9 +1648,6 @@ def test_is_char_device_false(self):
16341648 self .assertIs ((P / 'fileA\x00 ' ).is_char_device (), False )
16351649
16361650 def _check_complex_symlinks (self , link0_target ):
1637- if not self .can_symlink :
1638- self .skipTest ("symlinks required" )
1639-
16401651 # Test solving a non-looping chain of symlinks (issue #19887).
16411652 pathmod = self .pathmod
16421653 P = self .cls (self .base )
@@ -1682,12 +1693,15 @@ def _check_complex_symlinks(self, link0_target):
16821693 finally :
16831694 os .chdir (old_path )
16841695
1696+ @needs_symlinks
16851697 def test_complex_symlinks_absolute (self ):
16861698 self ._check_complex_symlinks (self .base )
16871699
1700+ @needs_symlinks
16881701 def test_complex_symlinks_relative (self ):
16891702 self ._check_complex_symlinks ('.' )
16901703
1704+ @needs_symlinks
16911705 def test_complex_symlinks_relative_dot_dot (self ):
16921706 self ._check_complex_symlinks (self .pathmod .join ('dirA' , '..' ))
16931707
@@ -1803,9 +1817,8 @@ def test_walk_bottom_up(self):
18031817 raise AssertionError (f"Unexpected path: { path } " )
18041818 self .assertTrue (seen_testfn )
18051819
1820+ @needs_symlinks
18061821 def test_walk_follow_symlinks (self ):
1807- if not self .can_symlink :
1808- self .skipTest ("symlinks required" )
18091822 self .setUpWalk ()
18101823 walk_it = self .walk_path .walk (follow_symlinks = True )
18111824 for root , dirs , files in walk_it :
@@ -1816,9 +1829,8 @@ def test_walk_follow_symlinks(self):
18161829 else :
18171830 self .fail ("Didn't follow symlink with follow_symlinks=True" )
18181831
1832+ @needs_symlinks
18191833 def test_walk_symlink_location (self ):
1820- if not self .can_symlink :
1821- self .skipTest ("symlinks required" )
18221834 self .setUpWalk ()
18231835 # Tests whether symlinks end up in filenames or dirnames depending
18241836 # on the `follow_symlinks` argument.
0 commit comments