1import contextlib 2import collections.abc 3import io 4import os 5import sys 6import errno 7import pathlib 8import pickle 9import socket 10import stat 11import tempfile 12import unittest 13from unittest import mock 14 15from test.support import import_helper 16from test.support import is_emscripten, is_wasi 17from test.support import os_helper 18from test.support.os_helper import TESTFN, FakePath 19 20try: 21 import grp, pwd 22except ImportError: 23 grp = pwd = None 24 25 26class _BaseFlavourTest(object): 27 28 def _check_parse_parts(self, arg, expected): 29 f = self.flavour.parse_parts 30 sep = self.flavour.sep 31 altsep = self.flavour.altsep 32 actual = f([x.replace('/', sep) for x in arg]) 33 self.assertEqual(actual, expected) 34 if altsep: 35 actual = f([x.replace('/', altsep) for x in arg]) 36 self.assertEqual(actual, expected) 37 38 def test_parse_parts_common(self): 39 check = self._check_parse_parts 40 sep = self.flavour.sep 41 # Unanchored parts. 42 check([], ('', '', [])) 43 check(['a'], ('', '', ['a'])) 44 check(['a/'], ('', '', ['a'])) 45 check(['a', 'b'], ('', '', ['a', 'b'])) 46 # Expansion. 47 check(['a/b'], ('', '', ['a', 'b'])) 48 check(['a/b/'], ('', '', ['a', 'b'])) 49 check(['a', 'b/c', 'd'], ('', '', ['a', 'b', 'c', 'd'])) 50 # Collapsing and stripping excess slashes. 51 check(['a', 'b//c', 'd'], ('', '', ['a', 'b', 'c', 'd'])) 52 check(['a', 'b/c/', 'd'], ('', '', ['a', 'b', 'c', 'd'])) 53 # Eliminating standalone dots. 54 check(['.'], ('', '', [])) 55 check(['.', '.', 'b'], ('', '', ['b'])) 56 check(['a', '.', 'b'], ('', '', ['a', 'b'])) 57 check(['a', '.', '.'], ('', '', ['a'])) 58 # The first part is anchored. 59 check(['/a/b'], ('', sep, [sep, 'a', 'b'])) 60 check(['/a', 'b'], ('', sep, [sep, 'a', 'b'])) 61 check(['/a/', 'b'], ('', sep, [sep, 'a', 'b'])) 62 # Ignoring parts before an anchored part. 63 check(['a', '/b', 'c'], ('', sep, [sep, 'b', 'c'])) 64 check(['a', '/b', '/c'], ('', sep, [sep, 'c'])) 65 66 67class PosixFlavourTest(_BaseFlavourTest, unittest.TestCase): 68 flavour = pathlib._posix_flavour 69 70 def test_parse_parts(self): 71 check = self._check_parse_parts 72 # Collapsing of excess leading slashes, except for the double-slash 73 # special case. 74 check(['//a', 'b'], ('', '//', ['//', 'a', 'b'])) 75 check(['///a', 'b'], ('', '/', ['/', 'a', 'b'])) 76 check(['////a', 'b'], ('', '/', ['/', 'a', 'b'])) 77 # Paths which look like NT paths aren't treated specially. 78 check(['c:a'], ('', '', ['c:a'])) 79 check(['c:\\a'], ('', '', ['c:\\a'])) 80 check(['\\a'], ('', '', ['\\a'])) 81 82 def test_splitroot(self): 83 f = self.flavour.splitroot 84 self.assertEqual(f(''), ('', '', '')) 85 self.assertEqual(f('a'), ('', '', 'a')) 86 self.assertEqual(f('a/b'), ('', '', 'a/b')) 87 self.assertEqual(f('a/b/'), ('', '', 'a/b/')) 88 self.assertEqual(f('/a'), ('', '/', 'a')) 89 self.assertEqual(f('/a/b'), ('', '/', 'a/b')) 90 self.assertEqual(f('/a/b/'), ('', '/', 'a/b/')) 91 # The root is collapsed when there are redundant slashes 92 # except when there are exactly two leading slashes, which 93 # is a special case in POSIX. 94 self.assertEqual(f('//a'), ('', '//', 'a')) 95 self.assertEqual(f('///a'), ('', '/', 'a')) 96 self.assertEqual(f('///a/b'), ('', '/', 'a/b')) 97 # Paths which look like NT paths aren't treated specially. 98 self.assertEqual(f('c:/a/b'), ('', '', 'c:/a/b')) 99 self.assertEqual(f('\\/a/b'), ('', '', '\\/a/b')) 100 self.assertEqual(f('\\a\\b'), ('', '', '\\a\\b')) 101 102 103class NTFlavourTest(_BaseFlavourTest, unittest.TestCase): 104 flavour = pathlib._windows_flavour 105 106 def test_parse_parts(self): 107 check = self._check_parse_parts 108 # First part is anchored. 109 check(['c:'], ('c:', '', ['c:'])) 110 check(['c:/'], ('c:', '\\', ['c:\\'])) 111 check(['/'], ('', '\\', ['\\'])) 112 check(['c:a'], ('c:', '', ['c:', 'a'])) 113 check(['c:/a'], ('c:', '\\', ['c:\\', 'a'])) 114 check(['/a'], ('', '\\', ['\\', 'a'])) 115 # UNC paths. 116 check(['//a/b'], ('\\\\a\\b', '\\', ['\\\\a\\b\\'])) 117 check(['//a/b/'], ('\\\\a\\b', '\\', ['\\\\a\\b\\'])) 118 check(['//a/b/c'], ('\\\\a\\b', '\\', ['\\\\a\\b\\', 'c'])) 119 # Second part is anchored, so that the first part is ignored. 120 check(['a', 'Z:b', 'c'], ('Z:', '', ['Z:', 'b', 'c'])) 121 check(['a', 'Z:/b', 'c'], ('Z:', '\\', ['Z:\\', 'b', 'c'])) 122 # UNC paths. 123 check(['a', '//b/c', 'd'], ('\\\\b\\c', '\\', ['\\\\b\\c\\', 'd'])) 124 # Collapsing and stripping excess slashes. 125 check(['a', 'Z://b//c/', 'd/'], ('Z:', '\\', ['Z:\\', 'b', 'c', 'd'])) 126 # UNC paths. 127 check(['a', '//b/c//', 'd'], ('\\\\b\\c', '\\', ['\\\\b\\c\\', 'd'])) 128 # Extended paths. 129 check(['//?/c:/'], ('\\\\?\\c:', '\\', ['\\\\?\\c:\\'])) 130 check(['//?/c:/a'], ('\\\\?\\c:', '\\', ['\\\\?\\c:\\', 'a'])) 131 check(['//?/c:/a', '/b'], ('\\\\?\\c:', '\\', ['\\\\?\\c:\\', 'b'])) 132 # Extended UNC paths (format is "\\?\UNC\server\share"). 133 check(['//?/UNC/b/c'], ('\\\\?\\UNC\\b\\c', '\\', ['\\\\?\\UNC\\b\\c\\'])) 134 check(['//?/UNC/b/c/d'], ('\\\\?\\UNC\\b\\c', '\\', ['\\\\?\\UNC\\b\\c\\', 'd'])) 135 # Second part has a root but not drive. 136 check(['a', '/b', 'c'], ('', '\\', ['\\', 'b', 'c'])) 137 check(['Z:/a', '/b', 'c'], ('Z:', '\\', ['Z:\\', 'b', 'c'])) 138 check(['//?/Z:/a', '/b', 'c'], ('\\\\?\\Z:', '\\', ['\\\\?\\Z:\\', 'b', 'c'])) 139 140 def test_splitroot(self): 141 f = self.flavour.splitroot 142 self.assertEqual(f(''), ('', '', '')) 143 self.assertEqual(f('a'), ('', '', 'a')) 144 self.assertEqual(f('a\\b'), ('', '', 'a\\b')) 145 self.assertEqual(f('\\a'), ('', '\\', 'a')) 146 self.assertEqual(f('\\a\\b'), ('', '\\', 'a\\b')) 147 self.assertEqual(f('c:a\\b'), ('c:', '', 'a\\b')) 148 self.assertEqual(f('c:\\a\\b'), ('c:', '\\', 'a\\b')) 149 # Redundant slashes in the root are collapsed. 150 self.assertEqual(f('\\\\a'), ('', '\\', 'a')) 151 self.assertEqual(f('\\\\\\a/b'), ('', '\\', 'a/b')) 152 self.assertEqual(f('c:\\\\a'), ('c:', '\\', 'a')) 153 self.assertEqual(f('c:\\\\\\a/b'), ('c:', '\\', 'a/b')) 154 # Valid UNC paths. 155 self.assertEqual(f('\\\\a\\b'), ('\\\\a\\b', '\\', '')) 156 self.assertEqual(f('\\\\a\\b\\'), ('\\\\a\\b', '\\', '')) 157 self.assertEqual(f('\\\\a\\b\\c\\d'), ('\\\\a\\b', '\\', 'c\\d')) 158 # These are non-UNC paths (according to ntpath.py and test_ntpath). 159 # However, command.com says such paths are invalid, so it's 160 # difficult to know what the right semantics are. 161 self.assertEqual(f('\\\\\\a\\b'), ('', '\\', 'a\\b')) 162 self.assertEqual(f('\\\\a'), ('', '\\', 'a')) 163 164 165# 166# Tests for the pure classes. 167# 168 169class _BasePurePathTest(object): 170 171 # Keys are canonical paths, values are list of tuples of arguments 172 # supposed to produce equal paths. 173 equivalences = { 174 'a/b': [ 175 ('a', 'b'), ('a/', 'b'), ('a', 'b/'), ('a/', 'b/'), 176 ('a/b/',), ('a//b',), ('a//b//',), 177 # Empty components get removed. 178 ('', 'a', 'b'), ('a', '', 'b'), ('a', 'b', ''), 179 ], 180 '/b/c/d': [ 181 ('a', '/b/c', 'd'), ('a', '///b//c', 'd/'), 182 ('/a', '/b/c', 'd'), 183 # Empty components get removed. 184 ('/', 'b', '', 'c/d'), ('/', '', 'b/c/d'), ('', '/b/c/d'), 185 ], 186 } 187 188 def setUp(self): 189 p = self.cls('a') 190 self.flavour = p._flavour 191 self.sep = self.flavour.sep 192 self.altsep = self.flavour.altsep 193 194 def test_constructor_common(self): 195 P = self.cls 196 p = P('a') 197 self.assertIsInstance(p, P) 198 P('a', 'b', 'c') 199 P('/a', 'b', 'c') 200 P('a/b/c') 201 P('/a/b/c') 202 P(FakePath("a/b/c")) 203 self.assertEqual(P(P('a')), P('a')) 204 self.assertEqual(P(P('a'), 'b'), P('a/b')) 205 self.assertEqual(P(P('a'), P('b')), P('a/b')) 206 self.assertEqual(P(P('a'), P('b'), P('c')), P(FakePath("a/b/c"))) 207 208 def _check_str_subclass(self, *args): 209 # Issue #21127: it should be possible to construct a PurePath object 210 # from a str subclass instance, and it then gets converted to 211 # a pure str object. 212 class StrSubclass(str): 213 pass 214 P = self.cls 215 p = P(*(StrSubclass(x) for x in args)) 216 self.assertEqual(p, P(*args)) 217 for part in p.parts: 218 self.assertIs(type(part), str) 219 220 def test_str_subclass_common(self): 221 self._check_str_subclass('') 222 self._check_str_subclass('.') 223 self._check_str_subclass('a') 224 self._check_str_subclass('a/b.txt') 225 self._check_str_subclass('/a/b.txt') 226 227 def test_join_common(self): 228 P = self.cls 229 p = P('a/b') 230 pp = p.joinpath('c') 231 self.assertEqual(pp, P('a/b/c')) 232 self.assertIs(type(pp), type(p)) 233 pp = p.joinpath('c', 'd') 234 self.assertEqual(pp, P('a/b/c/d')) 235 pp = p.joinpath(P('c')) 236 self.assertEqual(pp, P('a/b/c')) 237 pp = p.joinpath('/c') 238 self.assertEqual(pp, P('/c')) 239 240 def test_div_common(self): 241 # Basically the same as joinpath(). 242 P = self.cls 243 p = P('a/b') 244 pp = p / 'c' 245 self.assertEqual(pp, P('a/b/c')) 246 self.assertIs(type(pp), type(p)) 247 pp = p / 'c/d' 248 self.assertEqual(pp, P('a/b/c/d')) 249 pp = p / 'c' / 'd' 250 self.assertEqual(pp, P('a/b/c/d')) 251 pp = 'c' / p / 'd' 252 self.assertEqual(pp, P('c/a/b/d')) 253 pp = p / P('c') 254 self.assertEqual(pp, P('a/b/c')) 255 pp = p/ '/c' 256 self.assertEqual(pp, P('/c')) 257 258 def _check_str(self, expected, args): 259 p = self.cls(*args) 260 self.assertEqual(str(p), expected.replace('/', self.sep)) 261 262 def test_str_common(self): 263 # Canonicalized paths roundtrip. 264 for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'): 265 self._check_str(pathstr, (pathstr,)) 266 # Special case for the empty path. 267 self._check_str('.', ('',)) 268 # Other tests for str() are in test_equivalences(). 269 270 def test_as_posix_common(self): 271 P = self.cls 272 for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'): 273 self.assertEqual(P(pathstr).as_posix(), pathstr) 274 # Other tests for as_posix() are in test_equivalences(). 275 276 def test_as_bytes_common(self): 277 sep = os.fsencode(self.sep) 278 P = self.cls 279 self.assertEqual(bytes(P('a/b')), b'a' + sep + b'b') 280 281 def test_as_uri_common(self): 282 P = self.cls 283 with self.assertRaises(ValueError): 284 P('a').as_uri() 285 with self.assertRaises(ValueError): 286 P().as_uri() 287 288 def test_repr_common(self): 289 for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'): 290 p = self.cls(pathstr) 291 clsname = p.__class__.__name__ 292 r = repr(p) 293 # The repr() is in the form ClassName("forward-slashes path"). 294 self.assertTrue(r.startswith(clsname + '('), r) 295 self.assertTrue(r.endswith(')'), r) 296 inner = r[len(clsname) + 1 : -1] 297 self.assertEqual(eval(inner), p.as_posix()) 298 # The repr() roundtrips. 299 q = eval(r, pathlib.__dict__) 300 self.assertIs(q.__class__, p.__class__) 301 self.assertEqual(q, p) 302 self.assertEqual(repr(q), r) 303 304 def test_eq_common(self): 305 P = self.cls 306 self.assertEqual(P('a/b'), P('a/b')) 307 self.assertEqual(P('a/b'), P('a', 'b')) 308 self.assertNotEqual(P('a/b'), P('a')) 309 self.assertNotEqual(P('a/b'), P('/a/b')) 310 self.assertNotEqual(P('a/b'), P()) 311 self.assertNotEqual(P('/a/b'), P('/')) 312 self.assertNotEqual(P(), P('/')) 313 self.assertNotEqual(P(), "") 314 self.assertNotEqual(P(), {}) 315 self.assertNotEqual(P(), int) 316 317 def test_match_common(self): 318 P = self.cls 319 self.assertRaises(ValueError, P('a').match, '') 320 self.assertRaises(ValueError, P('a').match, '.') 321 # Simple relative pattern. 322 self.assertTrue(P('b.py').match('b.py')) 323 self.assertTrue(P('a/b.py').match('b.py')) 324 self.assertTrue(P('/a/b.py').match('b.py')) 325 self.assertFalse(P('a.py').match('b.py')) 326 self.assertFalse(P('b/py').match('b.py')) 327 self.assertFalse(P('/a.py').match('b.py')) 328 self.assertFalse(P('b.py/c').match('b.py')) 329 # Wildcard relative pattern. 330 self.assertTrue(P('b.py').match('*.py')) 331 self.assertTrue(P('a/b.py').match('*.py')) 332 self.assertTrue(P('/a/b.py').match('*.py')) 333 self.assertFalse(P('b.pyc').match('*.py')) 334 self.assertFalse(P('b./py').match('*.py')) 335 self.assertFalse(P('b.py/c').match('*.py')) 336 # Multi-part relative pattern. 337 self.assertTrue(P('ab/c.py').match('a*/*.py')) 338 self.assertTrue(P('/d/ab/c.py').match('a*/*.py')) 339 self.assertFalse(P('a.py').match('a*/*.py')) 340 self.assertFalse(P('/dab/c.py').match('a*/*.py')) 341 self.assertFalse(P('ab/c.py/d').match('a*/*.py')) 342 # Absolute pattern. 343 self.assertTrue(P('/b.py').match('/*.py')) 344 self.assertFalse(P('b.py').match('/*.py')) 345 self.assertFalse(P('a/b.py').match('/*.py')) 346 self.assertFalse(P('/a/b.py').match('/*.py')) 347 # Multi-part absolute pattern. 348 self.assertTrue(P('/a/b.py').match('/a/*.py')) 349 self.assertFalse(P('/ab.py').match('/a/*.py')) 350 self.assertFalse(P('/a/b/c.py').match('/a/*.py')) 351 # Multi-part glob-style pattern. 352 self.assertFalse(P('/a/b/c.py').match('/**/*.py')) 353 self.assertTrue(P('/a/b/c.py').match('/a/**/*.py')) 354 355 def test_ordering_common(self): 356 # Ordering is tuple-alike. 357 def assertLess(a, b): 358 self.assertLess(a, b) 359 self.assertGreater(b, a) 360 P = self.cls 361 a = P('a') 362 b = P('a/b') 363 c = P('abc') 364 d = P('b') 365 assertLess(a, b) 366 assertLess(a, c) 367 assertLess(a, d) 368 assertLess(b, c) 369 assertLess(c, d) 370 P = self.cls 371 a = P('/a') 372 b = P('/a/b') 373 c = P('/abc') 374 d = P('/b') 375 assertLess(a, b) 376 assertLess(a, c) 377 assertLess(a, d) 378 assertLess(b, c) 379 assertLess(c, d) 380 with self.assertRaises(TypeError): 381 P() < {} 382 383 def test_parts_common(self): 384 # `parts` returns a tuple. 385 sep = self.sep 386 P = self.cls 387 p = P('a/b') 388 parts = p.parts 389 self.assertEqual(parts, ('a', 'b')) 390 # The object gets reused. 391 self.assertIs(parts, p.parts) 392 # When the path is absolute, the anchor is a separate part. 393 p = P('/a/b') 394 parts = p.parts 395 self.assertEqual(parts, (sep, 'a', 'b')) 396 397 def test_fspath_common(self): 398 P = self.cls 399 p = P('a/b') 400 self._check_str(p.__fspath__(), ('a/b',)) 401 self._check_str(os.fspath(p), ('a/b',)) 402 403 def test_equivalences(self): 404 for k, tuples in self.equivalences.items(): 405 canon = k.replace('/', self.sep) 406 posix = k.replace(self.sep, '/') 407 if canon != posix: 408 tuples = tuples + [ 409 tuple(part.replace('/', self.sep) for part in t) 410 for t in tuples 411 ] 412 tuples.append((posix, )) 413 pcanon = self.cls(canon) 414 for t in tuples: 415 p = self.cls(*t) 416 self.assertEqual(p, pcanon, "failed with args {}".format(t)) 417 self.assertEqual(hash(p), hash(pcanon)) 418 self.assertEqual(str(p), canon) 419 self.assertEqual(p.as_posix(), posix) 420 421 def test_parent_common(self): 422 # Relative 423 P = self.cls 424 p = P('a/b/c') 425 self.assertEqual(p.parent, P('a/b')) 426 self.assertEqual(p.parent.parent, P('a')) 427 self.assertEqual(p.parent.parent.parent, P()) 428 self.assertEqual(p.parent.parent.parent.parent, P()) 429 # Anchored 430 p = P('/a/b/c') 431 self.assertEqual(p.parent, P('/a/b')) 432 self.assertEqual(p.parent.parent, P('/a')) 433 self.assertEqual(p.parent.parent.parent, P('/')) 434 self.assertEqual(p.parent.parent.parent.parent, P('/')) 435 436 def test_parents_common(self): 437 # Relative 438 P = self.cls 439 p = P('a/b/c') 440 par = p.parents 441 self.assertEqual(len(par), 3) 442 self.assertEqual(par[0], P('a/b')) 443 self.assertEqual(par[1], P('a')) 444 self.assertEqual(par[2], P('.')) 445 self.assertEqual(par[-1], P('.')) 446 self.assertEqual(par[-2], P('a')) 447 self.assertEqual(par[-3], P('a/b')) 448 self.assertEqual(par[0:1], (P('a/b'),)) 449 self.assertEqual(par[:2], (P('a/b'), P('a'))) 450 self.assertEqual(par[:-1], (P('a/b'), P('a'))) 451 self.assertEqual(par[1:], (P('a'), P('.'))) 452 self.assertEqual(par[::2], (P('a/b'), P('.'))) 453 self.assertEqual(par[::-1], (P('.'), P('a'), P('a/b'))) 454 self.assertEqual(list(par), [P('a/b'), P('a'), P('.')]) 455 with self.assertRaises(IndexError): 456 par[-4] 457 with self.assertRaises(IndexError): 458 par[3] 459 with self.assertRaises(TypeError): 460 par[0] = p 461 # Anchored 462 p = P('/a/b/c') 463 par = p.parents 464 self.assertEqual(len(par), 3) 465 self.assertEqual(par[0], P('/a/b')) 466 self.assertEqual(par[1], P('/a')) 467 self.assertEqual(par[2], P('/')) 468 self.assertEqual(par[-1], P('/')) 469 self.assertEqual(par[-2], P('/a')) 470 self.assertEqual(par[-3], P('/a/b')) 471 self.assertEqual(par[0:1], (P('/a/b'),)) 472 self.assertEqual(par[:2], (P('/a/b'), P('/a'))) 473 self.assertEqual(par[:-1], (P('/a/b'), P('/a'))) 474 self.assertEqual(par[1:], (P('/a'), P('/'))) 475 self.assertEqual(par[::2], (P('/a/b'), P('/'))) 476 self.assertEqual(par[::-1], (P('/'), P('/a'), P('/a/b'))) 477 self.assertEqual(list(par), [P('/a/b'), P('/a'), P('/')]) 478 with self.assertRaises(IndexError): 479 par[-4] 480 with self.assertRaises(IndexError): 481 par[3] 482 483 def test_drive_common(self): 484 P = self.cls 485 self.assertEqual(P('a/b').drive, '') 486 self.assertEqual(P('/a/b').drive, '') 487 self.assertEqual(P('').drive, '') 488 489 def test_root_common(self): 490 P = self.cls 491 sep = self.sep 492 self.assertEqual(P('').root, '') 493 self.assertEqual(P('a/b').root, '') 494 self.assertEqual(P('/').root, sep) 495 self.assertEqual(P('/a/b').root, sep) 496 497 def test_anchor_common(self): 498 P = self.cls 499 sep = self.sep 500 self.assertEqual(P('').anchor, '') 501 self.assertEqual(P('a/b').anchor, '') 502 self.assertEqual(P('/').anchor, sep) 503 self.assertEqual(P('/a/b').anchor, sep) 504 505 def test_name_common(self): 506 P = self.cls 507 self.assertEqual(P('').name, '') 508 self.assertEqual(P('.').name, '') 509 self.assertEqual(P('/').name, '') 510 self.assertEqual(P('a/b').name, 'b') 511 self.assertEqual(P('/a/b').name, 'b') 512 self.assertEqual(P('/a/b/.').name, 'b') 513 self.assertEqual(P('a/b.py').name, 'b.py') 514 self.assertEqual(P('/a/b.py').name, 'b.py') 515 516 def test_suffix_common(self): 517 P = self.cls 518 self.assertEqual(P('').suffix, '') 519 self.assertEqual(P('.').suffix, '') 520 self.assertEqual(P('..').suffix, '') 521 self.assertEqual(P('/').suffix, '') 522 self.assertEqual(P('a/b').suffix, '') 523 self.assertEqual(P('/a/b').suffix, '') 524 self.assertEqual(P('/a/b/.').suffix, '') 525 self.assertEqual(P('a/b.py').suffix, '.py') 526 self.assertEqual(P('/a/b.py').suffix, '.py') 527 self.assertEqual(P('a/.hgrc').suffix, '') 528 self.assertEqual(P('/a/.hgrc').suffix, '') 529 self.assertEqual(P('a/.hg.rc').suffix, '.rc') 530 self.assertEqual(P('/a/.hg.rc').suffix, '.rc') 531 self.assertEqual(P('a/b.tar.gz').suffix, '.gz') 532 self.assertEqual(P('/a/b.tar.gz').suffix, '.gz') 533 self.assertEqual(P('a/Some name. Ending with a dot.').suffix, '') 534 self.assertEqual(P('/a/Some name. Ending with a dot.').suffix, '') 535 536 def test_suffixes_common(self): 537 P = self.cls 538 self.assertEqual(P('').suffixes, []) 539 self.assertEqual(P('.').suffixes, []) 540 self.assertEqual(P('/').suffixes, []) 541 self.assertEqual(P('a/b').suffixes, []) 542 self.assertEqual(P('/a/b').suffixes, []) 543 self.assertEqual(P('/a/b/.').suffixes, []) 544 self.assertEqual(P('a/b.py').suffixes, ['.py']) 545 self.assertEqual(P('/a/b.py').suffixes, ['.py']) 546 self.assertEqual(P('a/.hgrc').suffixes, []) 547 self.assertEqual(P('/a/.hgrc').suffixes, []) 548 self.assertEqual(P('a/.hg.rc').suffixes, ['.rc']) 549 self.assertEqual(P('/a/.hg.rc').suffixes, ['.rc']) 550 self.assertEqual(P('a/b.tar.gz').suffixes, ['.tar', '.gz']) 551 self.assertEqual(P('/a/b.tar.gz').suffixes, ['.tar', '.gz']) 552 self.assertEqual(P('a/Some name. Ending with a dot.').suffixes, []) 553 self.assertEqual(P('/a/Some name. Ending with a dot.').suffixes, []) 554 555 def test_stem_common(self): 556 P = self.cls 557 self.assertEqual(P('').stem, '') 558 self.assertEqual(P('.').stem, '') 559 self.assertEqual(P('..').stem, '..') 560 self.assertEqual(P('/').stem, '') 561 self.assertEqual(P('a/b').stem, 'b') 562 self.assertEqual(P('a/b.py').stem, 'b') 563 self.assertEqual(P('a/.hgrc').stem, '.hgrc') 564 self.assertEqual(P('a/.hg.rc').stem, '.hg') 565 self.assertEqual(P('a/b.tar.gz').stem, 'b.tar') 566 self.assertEqual(P('a/Some name. Ending with a dot.').stem, 567 'Some name. Ending with a dot.') 568 569 def test_with_name_common(self): 570 P = self.cls 571 self.assertEqual(P('a/b').with_name('d.xml'), P('a/d.xml')) 572 self.assertEqual(P('/a/b').with_name('d.xml'), P('/a/d.xml')) 573 self.assertEqual(P('a/b.py').with_name('d.xml'), P('a/d.xml')) 574 self.assertEqual(P('/a/b.py').with_name('d.xml'), P('/a/d.xml')) 575 self.assertEqual(P('a/Dot ending.').with_name('d.xml'), P('a/d.xml')) 576 self.assertEqual(P('/a/Dot ending.').with_name('d.xml'), P('/a/d.xml')) 577 self.assertRaises(ValueError, P('').with_name, 'd.xml') 578 self.assertRaises(ValueError, P('.').with_name, 'd.xml') 579 self.assertRaises(ValueError, P('/').with_name, 'd.xml') 580 self.assertRaises(ValueError, P('a/b').with_name, '') 581 self.assertRaises(ValueError, P('a/b').with_name, '/c') 582 self.assertRaises(ValueError, P('a/b').with_name, 'c/') 583 self.assertRaises(ValueError, P('a/b').with_name, 'c/d') 584 585 def test_with_stem_common(self): 586 P = self.cls 587 self.assertEqual(P('a/b').with_stem('d'), P('a/d')) 588 self.assertEqual(P('/a/b').with_stem('d'), P('/a/d')) 589 self.assertEqual(P('a/b.py').with_stem('d'), P('a/d.py')) 590 self.assertEqual(P('/a/b.py').with_stem('d'), P('/a/d.py')) 591 self.assertEqual(P('/a/b.tar.gz').with_stem('d'), P('/a/d.gz')) 592 self.assertEqual(P('a/Dot ending.').with_stem('d'), P('a/d')) 593 self.assertEqual(P('/a/Dot ending.').with_stem('d'), P('/a/d')) 594 self.assertRaises(ValueError, P('').with_stem, 'd') 595 self.assertRaises(ValueError, P('.').with_stem, 'd') 596 self.assertRaises(ValueError, P('/').with_stem, 'd') 597 self.assertRaises(ValueError, P('a/b').with_stem, '') 598 self.assertRaises(ValueError, P('a/b').with_stem, '/c') 599 self.assertRaises(ValueError, P('a/b').with_stem, 'c/') 600 self.assertRaises(ValueError, P('a/b').with_stem, 'c/d') 601 602 def test_with_suffix_common(self): 603 P = self.cls 604 self.assertEqual(P('a/b').with_suffix('.gz'), P('a/b.gz')) 605 self.assertEqual(P('/a/b').with_suffix('.gz'), P('/a/b.gz')) 606 self.assertEqual(P('a/b.py').with_suffix('.gz'), P('a/b.gz')) 607 self.assertEqual(P('/a/b.py').with_suffix('.gz'), P('/a/b.gz')) 608 # Stripping suffix. 609 self.assertEqual(P('a/b.py').with_suffix(''), P('a/b')) 610 self.assertEqual(P('/a/b').with_suffix(''), P('/a/b')) 611 # Path doesn't have a "filename" component. 612 self.assertRaises(ValueError, P('').with_suffix, '.gz') 613 self.assertRaises(ValueError, P('.').with_suffix, '.gz') 614 self.assertRaises(ValueError, P('/').with_suffix, '.gz') 615 # Invalid suffix. 616 self.assertRaises(ValueError, P('a/b').with_suffix, 'gz') 617 self.assertRaises(ValueError, P('a/b').with_suffix, '/') 618 self.assertRaises(ValueError, P('a/b').with_suffix, '.') 619 self.assertRaises(ValueError, P('a/b').with_suffix, '/.gz') 620 self.assertRaises(ValueError, P('a/b').with_suffix, 'c/d') 621 self.assertRaises(ValueError, P('a/b').with_suffix, '.c/.d') 622 self.assertRaises(ValueError, P('a/b').with_suffix, './.d') 623 self.assertRaises(ValueError, P('a/b').with_suffix, '.d/.') 624 self.assertRaises(ValueError, P('a/b').with_suffix, 625 (self.flavour.sep, 'd')) 626 627 def test_relative_to_common(self): 628 P = self.cls 629 p = P('a/b') 630 self.assertRaises(TypeError, p.relative_to) 631 self.assertRaises(TypeError, p.relative_to, b'a') 632 self.assertEqual(p.relative_to(P()), P('a/b')) 633 self.assertEqual(p.relative_to(''), P('a/b')) 634 self.assertEqual(p.relative_to(P('a')), P('b')) 635 self.assertEqual(p.relative_to('a'), P('b')) 636 self.assertEqual(p.relative_to('a/'), P('b')) 637 self.assertEqual(p.relative_to(P('a/b')), P()) 638 self.assertEqual(p.relative_to('a/b'), P()) 639 # With several args. 640 self.assertEqual(p.relative_to('a', 'b'), P()) 641 # Unrelated paths. 642 self.assertRaises(ValueError, p.relative_to, P('c')) 643 self.assertRaises(ValueError, p.relative_to, P('a/b/c')) 644 self.assertRaises(ValueError, p.relative_to, P('a/c')) 645 self.assertRaises(ValueError, p.relative_to, P('/a')) 646 p = P('/a/b') 647 self.assertEqual(p.relative_to(P('/')), P('a/b')) 648 self.assertEqual(p.relative_to('/'), P('a/b')) 649 self.assertEqual(p.relative_to(P('/a')), P('b')) 650 self.assertEqual(p.relative_to('/a'), P('b')) 651 self.assertEqual(p.relative_to('/a/'), P('b')) 652 self.assertEqual(p.relative_to(P('/a/b')), P()) 653 self.assertEqual(p.relative_to('/a/b'), P()) 654 # Unrelated paths. 655 self.assertRaises(ValueError, p.relative_to, P('/c')) 656 self.assertRaises(ValueError, p.relative_to, P('/a/b/c')) 657 self.assertRaises(ValueError, p.relative_to, P('/a/c')) 658 self.assertRaises(ValueError, p.relative_to, P()) 659 self.assertRaises(ValueError, p.relative_to, '') 660 self.assertRaises(ValueError, p.relative_to, P('a')) 661 662 def test_is_relative_to_common(self): 663 P = self.cls 664 p = P('a/b') 665 self.assertRaises(TypeError, p.is_relative_to) 666 self.assertRaises(TypeError, p.is_relative_to, b'a') 667 self.assertTrue(p.is_relative_to(P())) 668 self.assertTrue(p.is_relative_to('')) 669 self.assertTrue(p.is_relative_to(P('a'))) 670 self.assertTrue(p.is_relative_to('a/')) 671 self.assertTrue(p.is_relative_to(P('a/b'))) 672 self.assertTrue(p.is_relative_to('a/b')) 673 # With several args. 674 self.assertTrue(p.is_relative_to('a', 'b')) 675 # Unrelated paths. 676 self.assertFalse(p.is_relative_to(P('c'))) 677 self.assertFalse(p.is_relative_to(P('a/b/c'))) 678 self.assertFalse(p.is_relative_to(P('a/c'))) 679 self.assertFalse(p.is_relative_to(P('/a'))) 680 p = P('/a/b') 681 self.assertTrue(p.is_relative_to(P('/'))) 682 self.assertTrue(p.is_relative_to('/')) 683 self.assertTrue(p.is_relative_to(P('/a'))) 684 self.assertTrue(p.is_relative_to('/a')) 685 self.assertTrue(p.is_relative_to('/a/')) 686 self.assertTrue(p.is_relative_to(P('/a/b'))) 687 self.assertTrue(p.is_relative_to('/a/b')) 688 # Unrelated paths. 689 self.assertFalse(p.is_relative_to(P('/c'))) 690 self.assertFalse(p.is_relative_to(P('/a/b/c'))) 691 self.assertFalse(p.is_relative_to(P('/a/c'))) 692 self.assertFalse(p.is_relative_to(P())) 693 self.assertFalse(p.is_relative_to('')) 694 self.assertFalse(p.is_relative_to(P('a'))) 695 696 def test_pickling_common(self): 697 P = self.cls 698 p = P('/a/b') 699 for proto in range(0, pickle.HIGHEST_PROTOCOL + 1): 700 dumped = pickle.dumps(p, proto) 701 pp = pickle.loads(dumped) 702 self.assertIs(pp.__class__, p.__class__) 703 self.assertEqual(pp, p) 704 self.assertEqual(hash(pp), hash(p)) 705 self.assertEqual(str(pp), str(p)) 706 707 708class PurePosixPathTest(_BasePurePathTest, unittest.TestCase): 709 cls = pathlib.PurePosixPath 710 711 def test_root(self): 712 P = self.cls 713 self.assertEqual(P('/a/b').root, '/') 714 self.assertEqual(P('///a/b').root, '/') 715 # POSIX special case for two leading slashes. 716 self.assertEqual(P('//a/b').root, '//') 717 718 def test_eq(self): 719 P = self.cls 720 self.assertNotEqual(P('a/b'), P('A/b')) 721 self.assertEqual(P('/a'), P('///a')) 722 self.assertNotEqual(P('/a'), P('//a')) 723 724 def test_as_uri(self): 725 P = self.cls 726 self.assertEqual(P('/').as_uri(), 'file:///') 727 self.assertEqual(P('/a/b.c').as_uri(), 'file:///a/b.c') 728 self.assertEqual(P('/a/b%#c').as_uri(), 'file:///a/b%25%23c') 729 730 def test_as_uri_non_ascii(self): 731 from urllib.parse import quote_from_bytes 732 P = self.cls 733 try: 734 os.fsencode('\xe9') 735 except UnicodeEncodeError: 736 self.skipTest("\\xe9 cannot be encoded to the filesystem encoding") 737 self.assertEqual(P('/a/b\xe9').as_uri(), 738 'file:///a/b' + quote_from_bytes(os.fsencode('\xe9'))) 739 740 def test_match(self): 741 P = self.cls 742 self.assertFalse(P('A.py').match('a.PY')) 743 744 def test_is_absolute(self): 745 P = self.cls 746 self.assertFalse(P().is_absolute()) 747 self.assertFalse(P('a').is_absolute()) 748 self.assertFalse(P('a/b/').is_absolute()) 749 self.assertTrue(P('/').is_absolute()) 750 self.assertTrue(P('/a').is_absolute()) 751 self.assertTrue(P('/a/b/').is_absolute()) 752 self.assertTrue(P('//a').is_absolute()) 753 self.assertTrue(P('//a/b').is_absolute()) 754 755 def test_is_reserved(self): 756 P = self.cls 757 self.assertIs(False, P('').is_reserved()) 758 self.assertIs(False, P('/').is_reserved()) 759 self.assertIs(False, P('/foo/bar').is_reserved()) 760 self.assertIs(False, P('/dev/con/PRN/NUL').is_reserved()) 761 762 def test_join(self): 763 P = self.cls 764 p = P('//a') 765 pp = p.joinpath('b') 766 self.assertEqual(pp, P('//a/b')) 767 pp = P('/a').joinpath('//c') 768 self.assertEqual(pp, P('//c')) 769 pp = P('//a').joinpath('/c') 770 self.assertEqual(pp, P('/c')) 771 772 def test_div(self): 773 # Basically the same as joinpath(). 774 P = self.cls 775 p = P('//a') 776 pp = p / 'b' 777 self.assertEqual(pp, P('//a/b')) 778 pp = P('/a') / '//c' 779 self.assertEqual(pp, P('//c')) 780 pp = P('//a') / '/c' 781 self.assertEqual(pp, P('/c')) 782 783 784class PureWindowsPathTest(_BasePurePathTest, unittest.TestCase): 785 cls = pathlib.PureWindowsPath 786 787 equivalences = _BasePurePathTest.equivalences.copy() 788 equivalences.update({ 789 'c:a': [ ('c:', 'a'), ('c:', 'a/'), ('/', 'c:', 'a') ], 790 'c:/a': [ 791 ('c:/', 'a'), ('c:', '/', 'a'), ('c:', '/a'), 792 ('/z', 'c:/', 'a'), ('//x/y', 'c:/', 'a'), 793 ], 794 '//a/b/': [ ('//a/b',) ], 795 '//a/b/c': [ 796 ('//a/b', 'c'), ('//a/b/', 'c'), 797 ], 798 }) 799 800 def test_str(self): 801 p = self.cls('a/b/c') 802 self.assertEqual(str(p), 'a\\b\\c') 803 p = self.cls('c:/a/b/c') 804 self.assertEqual(str(p), 'c:\\a\\b\\c') 805 p = self.cls('//a/b') 806 self.assertEqual(str(p), '\\\\a\\b\\') 807 p = self.cls('//a/b/c') 808 self.assertEqual(str(p), '\\\\a\\b\\c') 809 p = self.cls('//a/b/c/d') 810 self.assertEqual(str(p), '\\\\a\\b\\c\\d') 811 812 def test_str_subclass(self): 813 self._check_str_subclass('c:') 814 self._check_str_subclass('c:a') 815 self._check_str_subclass('c:a\\b.txt') 816 self._check_str_subclass('c:\\') 817 self._check_str_subclass('c:\\a') 818 self._check_str_subclass('c:\\a\\b.txt') 819 self._check_str_subclass('\\\\some\\share') 820 self._check_str_subclass('\\\\some\\share\\a') 821 self._check_str_subclass('\\\\some\\share\\a\\b.txt') 822 823 def test_eq(self): 824 P = self.cls 825 self.assertEqual(P('c:a/b'), P('c:a/b')) 826 self.assertEqual(P('c:a/b'), P('c:', 'a', 'b')) 827 self.assertNotEqual(P('c:a/b'), P('d:a/b')) 828 self.assertNotEqual(P('c:a/b'), P('c:/a/b')) 829 self.assertNotEqual(P('/a/b'), P('c:/a/b')) 830 # Case-insensitivity. 831 self.assertEqual(P('a/B'), P('A/b')) 832 self.assertEqual(P('C:a/B'), P('c:A/b')) 833 self.assertEqual(P('//Some/SHARE/a/B'), P('//somE/share/A/b')) 834 835 def test_as_uri(self): 836 P = self.cls 837 with self.assertRaises(ValueError): 838 P('/a/b').as_uri() 839 with self.assertRaises(ValueError): 840 P('c:a/b').as_uri() 841 self.assertEqual(P('c:/').as_uri(), 'file:///c:/') 842 self.assertEqual(P('c:/a/b.c').as_uri(), 'file:///c:/a/b.c') 843 self.assertEqual(P('c:/a/b%#c').as_uri(), 'file:///c:/a/b%25%23c') 844 self.assertEqual(P('c:/a/b\xe9').as_uri(), 'file:///c:/a/b%C3%A9') 845 self.assertEqual(P('//some/share/').as_uri(), 'file://some/share/') 846 self.assertEqual(P('//some/share/a/b.c').as_uri(), 847 'file://some/share/a/b.c') 848 self.assertEqual(P('//some/share/a/b%#c\xe9').as_uri(), 849 'file://some/share/a/b%25%23c%C3%A9') 850 851 def test_match_common(self): 852 P = self.cls 853 # Absolute patterns. 854 self.assertTrue(P('c:/b.py').match('/*.py')) 855 self.assertTrue(P('c:/b.py').match('c:*.py')) 856 self.assertTrue(P('c:/b.py').match('c:/*.py')) 857 self.assertFalse(P('d:/b.py').match('c:/*.py')) # wrong drive 858 self.assertFalse(P('b.py').match('/*.py')) 859 self.assertFalse(P('b.py').match('c:*.py')) 860 self.assertFalse(P('b.py').match('c:/*.py')) 861 self.assertFalse(P('c:b.py').match('/*.py')) 862 self.assertFalse(P('c:b.py').match('c:/*.py')) 863 self.assertFalse(P('/b.py').match('c:*.py')) 864 self.assertFalse(P('/b.py').match('c:/*.py')) 865 # UNC patterns. 866 self.assertTrue(P('//some/share/a.py').match('/*.py')) 867 self.assertTrue(P('//some/share/a.py').match('//some/share/*.py')) 868 self.assertFalse(P('//other/share/a.py').match('//some/share/*.py')) 869 self.assertFalse(P('//some/share/a/b.py').match('//some/share/*.py')) 870 # Case-insensitivity. 871 self.assertTrue(P('B.py').match('b.PY')) 872 self.assertTrue(P('c:/a/B.Py').match('C:/A/*.pY')) 873 self.assertTrue(P('//Some/Share/B.Py').match('//somE/sharE/*.pY')) 874 875 def test_ordering_common(self): 876 # Case-insensitivity. 877 def assertOrderedEqual(a, b): 878 self.assertLessEqual(a, b) 879 self.assertGreaterEqual(b, a) 880 P = self.cls 881 p = P('c:A/b') 882 q = P('C:a/B') 883 assertOrderedEqual(p, q) 884 self.assertFalse(p < q) 885 self.assertFalse(p > q) 886 p = P('//some/Share/A/b') 887 q = P('//Some/SHARE/a/B') 888 assertOrderedEqual(p, q) 889 self.assertFalse(p < q) 890 self.assertFalse(p > q) 891 892 def test_parts(self): 893 P = self.cls 894 p = P('c:a/b') 895 parts = p.parts 896 self.assertEqual(parts, ('c:', 'a', 'b')) 897 p = P('c:/a/b') 898 parts = p.parts 899 self.assertEqual(parts, ('c:\\', 'a', 'b')) 900 p = P('//a/b/c/d') 901 parts = p.parts 902 self.assertEqual(parts, ('\\\\a\\b\\', 'c', 'd')) 903 904 def test_parent(self): 905 # Anchored 906 P = self.cls 907 p = P('z:a/b/c') 908 self.assertEqual(p.parent, P('z:a/b')) 909 self.assertEqual(p.parent.parent, P('z:a')) 910 self.assertEqual(p.parent.parent.parent, P('z:')) 911 self.assertEqual(p.parent.parent.parent.parent, P('z:')) 912 p = P('z:/a/b/c') 913 self.assertEqual(p.parent, P('z:/a/b')) 914 self.assertEqual(p.parent.parent, P('z:/a')) 915 self.assertEqual(p.parent.parent.parent, P('z:/')) 916 self.assertEqual(p.parent.parent.parent.parent, P('z:/')) 917 p = P('//a/b/c/d') 918 self.assertEqual(p.parent, P('//a/b/c')) 919 self.assertEqual(p.parent.parent, P('//a/b')) 920 self.assertEqual(p.parent.parent.parent, P('//a/b')) 921 922 def test_parents(self): 923 # Anchored 924 P = self.cls 925 p = P('z:a/b/') 926 par = p.parents 927 self.assertEqual(len(par), 2) 928 self.assertEqual(par[0], P('z:a')) 929 self.assertEqual(par[1], P('z:')) 930 self.assertEqual(par[0:1], (P('z:a'),)) 931 self.assertEqual(par[:-1], (P('z:a'),)) 932 self.assertEqual(par[:2], (P('z:a'), P('z:'))) 933 self.assertEqual(par[1:], (P('z:'),)) 934 self.assertEqual(par[::2], (P('z:a'),)) 935 self.assertEqual(par[::-1], (P('z:'), P('z:a'))) 936 self.assertEqual(list(par), [P('z:a'), P('z:')]) 937 with self.assertRaises(IndexError): 938 par[2] 939 p = P('z:/a/b/') 940 par = p.parents 941 self.assertEqual(len(par), 2) 942 self.assertEqual(par[0], P('z:/a')) 943 self.assertEqual(par[1], P('z:/')) 944 self.assertEqual(par[0:1], (P('z:/a'),)) 945 self.assertEqual(par[0:-1], (P('z:/a'),)) 946 self.assertEqual(par[:2], (P('z:/a'), P('z:/'))) 947 self.assertEqual(par[1:], (P('z:/'),)) 948 self.assertEqual(par[::2], (P('z:/a'),)) 949 self.assertEqual(par[::-1], (P('z:/'), P('z:/a'),)) 950 self.assertEqual(list(par), [P('z:/a'), P('z:/')]) 951 with self.assertRaises(IndexError): 952 par[2] 953 p = P('//a/b/c/d') 954 par = p.parents 955 self.assertEqual(len(par), 2) 956 self.assertEqual(par[0], P('//a/b/c')) 957 self.assertEqual(par[1], P('//a/b')) 958 self.assertEqual(par[0:1], (P('//a/b/c'),)) 959 self.assertEqual(par[0:-1], (P('//a/b/c'),)) 960 self.assertEqual(par[:2], (P('//a/b/c'), P('//a/b'))) 961 self.assertEqual(par[1:], (P('//a/b'),)) 962 self.assertEqual(par[::2], (P('//a/b/c'),)) 963 self.assertEqual(par[::-1], (P('//a/b'), P('//a/b/c'))) 964 self.assertEqual(list(par), [P('//a/b/c'), P('//a/b')]) 965 with self.assertRaises(IndexError): 966 par[2] 967 968 def test_drive(self): 969 P = self.cls 970 self.assertEqual(P('c:').drive, 'c:') 971 self.assertEqual(P('c:a/b').drive, 'c:') 972 self.assertEqual(P('c:/').drive, 'c:') 973 self.assertEqual(P('c:/a/b/').drive, 'c:') 974 self.assertEqual(P('//a/b').drive, '\\\\a\\b') 975 self.assertEqual(P('//a/b/').drive, '\\\\a\\b') 976 self.assertEqual(P('//a/b/c/d').drive, '\\\\a\\b') 977 978 def test_root(self): 979 P = self.cls 980 self.assertEqual(P('c:').root, '') 981 self.assertEqual(P('c:a/b').root, '') 982 self.assertEqual(P('c:/').root, '\\') 983 self.assertEqual(P('c:/a/b/').root, '\\') 984 self.assertEqual(P('//a/b').root, '\\') 985 self.assertEqual(P('//a/b/').root, '\\') 986 self.assertEqual(P('//a/b/c/d').root, '\\') 987 988 def test_anchor(self): 989 P = self.cls 990 self.assertEqual(P('c:').anchor, 'c:') 991 self.assertEqual(P('c:a/b').anchor, 'c:') 992 self.assertEqual(P('c:/').anchor, 'c:\\') 993 self.assertEqual(P('c:/a/b/').anchor, 'c:\\') 994 self.assertEqual(P('//a/b').anchor, '\\\\a\\b\\') 995 self.assertEqual(P('//a/b/').anchor, '\\\\a\\b\\') 996 self.assertEqual(P('//a/b/c/d').anchor, '\\\\a\\b\\') 997 998 def test_name(self): 999 P = self.cls 1000 self.assertEqual(P('c:').name, '') 1001 self.assertEqual(P('c:/').name, '') 1002 self.assertEqual(P('c:a/b').name, 'b') 1003 self.assertEqual(P('c:/a/b').name, 'b') 1004 self.assertEqual(P('c:a/b.py').name, 'b.py') 1005 self.assertEqual(P('c:/a/b.py').name, 'b.py') 1006 self.assertEqual(P('//My.py/Share.php').name, '') 1007 self.assertEqual(P('//My.py/Share.php/a/b').name, 'b') 1008 1009 def test_suffix(self): 1010 P = self.cls 1011 self.assertEqual(P('c:').suffix, '') 1012 self.assertEqual(P('c:/').suffix, '') 1013 self.assertEqual(P('c:a/b').suffix, '') 1014 self.assertEqual(P('c:/a/b').suffix, '') 1015 self.assertEqual(P('c:a/b.py').suffix, '.py') 1016 self.assertEqual(P('c:/a/b.py').suffix, '.py') 1017 self.assertEqual(P('c:a/.hgrc').suffix, '') 1018 self.assertEqual(P('c:/a/.hgrc').suffix, '') 1019 self.assertEqual(P('c:a/.hg.rc').suffix, '.rc') 1020 self.assertEqual(P('c:/a/.hg.rc').suffix, '.rc') 1021 self.assertEqual(P('c:a/b.tar.gz').suffix, '.gz') 1022 self.assertEqual(P('c:/a/b.tar.gz').suffix, '.gz') 1023 self.assertEqual(P('c:a/Some name. Ending with a dot.').suffix, '') 1024 self.assertEqual(P('c:/a/Some name. Ending with a dot.').suffix, '') 1025 self.assertEqual(P('//My.py/Share.php').suffix, '') 1026 self.assertEqual(P('//My.py/Share.php/a/b').suffix, '') 1027 1028 def test_suffixes(self): 1029 P = self.cls 1030 self.assertEqual(P('c:').suffixes, []) 1031 self.assertEqual(P('c:/').suffixes, []) 1032 self.assertEqual(P('c:a/b').suffixes, []) 1033 self.assertEqual(P('c:/a/b').suffixes, []) 1034 self.assertEqual(P('c:a/b.py').suffixes, ['.py']) 1035 self.assertEqual(P('c:/a/b.py').suffixes, ['.py']) 1036 self.assertEqual(P('c:a/.hgrc').suffixes, []) 1037 self.assertEqual(P('c:/a/.hgrc').suffixes, []) 1038 self.assertEqual(P('c:a/.hg.rc').suffixes, ['.rc']) 1039 self.assertEqual(P('c:/a/.hg.rc').suffixes, ['.rc']) 1040 self.assertEqual(P('c:a/b.tar.gz').suffixes, ['.tar', '.gz']) 1041 self.assertEqual(P('c:/a/b.tar.gz').suffixes, ['.tar', '.gz']) 1042 self.assertEqual(P('//My.py/Share.php').suffixes, []) 1043 self.assertEqual(P('//My.py/Share.php/a/b').suffixes, []) 1044 self.assertEqual(P('c:a/Some name. Ending with a dot.').suffixes, []) 1045 self.assertEqual(P('c:/a/Some name. Ending with a dot.').suffixes, []) 1046 1047 def test_stem(self): 1048 P = self.cls 1049 self.assertEqual(P('c:').stem, '') 1050 self.assertEqual(P('c:.').stem, '') 1051 self.assertEqual(P('c:..').stem, '..') 1052 self.assertEqual(P('c:/').stem, '') 1053 self.assertEqual(P('c:a/b').stem, 'b') 1054 self.assertEqual(P('c:a/b.py').stem, 'b') 1055 self.assertEqual(P('c:a/.hgrc').stem, '.hgrc') 1056 self.assertEqual(P('c:a/.hg.rc').stem, '.hg') 1057 self.assertEqual(P('c:a/b.tar.gz').stem, 'b.tar') 1058 self.assertEqual(P('c:a/Some name. Ending with a dot.').stem, 1059 'Some name. Ending with a dot.') 1060 1061 def test_with_name(self): 1062 P = self.cls 1063 self.assertEqual(P('c:a/b').with_name('d.xml'), P('c:a/d.xml')) 1064 self.assertEqual(P('c:/a/b').with_name('d.xml'), P('c:/a/d.xml')) 1065 self.assertEqual(P('c:a/Dot ending.').with_name('d.xml'), P('c:a/d.xml')) 1066 self.assertEqual(P('c:/a/Dot ending.').with_name('d.xml'), P('c:/a/d.xml')) 1067 self.assertRaises(ValueError, P('c:').with_name, 'd.xml') 1068 self.assertRaises(ValueError, P('c:/').with_name, 'd.xml') 1069 self.assertRaises(ValueError, P('//My/Share').with_name, 'd.xml') 1070 self.assertRaises(ValueError, P('c:a/b').with_name, 'd:') 1071 self.assertRaises(ValueError, P('c:a/b').with_name, 'd:e') 1072 self.assertRaises(ValueError, P('c:a/b').with_name, 'd:/e') 1073 self.assertRaises(ValueError, P('c:a/b').with_name, '//My/Share') 1074 1075 def test_with_stem(self): 1076 P = self.cls 1077 self.assertEqual(P('c:a/b').with_stem('d'), P('c:a/d')) 1078 self.assertEqual(P('c:/a/b').with_stem('d'), P('c:/a/d')) 1079 self.assertEqual(P('c:a/Dot ending.').with_stem('d'), P('c:a/d')) 1080 self.assertEqual(P('c:/a/Dot ending.').with_stem('d'), P('c:/a/d')) 1081 self.assertRaises(ValueError, P('c:').with_stem, 'd') 1082 self.assertRaises(ValueError, P('c:/').with_stem, 'd') 1083 self.assertRaises(ValueError, P('//My/Share').with_stem, 'd') 1084 self.assertRaises(ValueError, P('c:a/b').with_stem, 'd:') 1085 self.assertRaises(ValueError, P('c:a/b').with_stem, 'd:e') 1086 self.assertRaises(ValueError, P('c:a/b').with_stem, 'd:/e') 1087 self.assertRaises(ValueError, P('c:a/b').with_stem, '//My/Share') 1088 1089 def test_with_suffix(self): 1090 P = self.cls 1091 self.assertEqual(P('c:a/b').with_suffix('.gz'), P('c:a/b.gz')) 1092 self.assertEqual(P('c:/a/b').with_suffix('.gz'), P('c:/a/b.gz')) 1093 self.assertEqual(P('c:a/b.py').with_suffix('.gz'), P('c:a/b.gz')) 1094 self.assertEqual(P('c:/a/b.py').with_suffix('.gz'), P('c:/a/b.gz')) 1095 # Path doesn't have a "filename" component. 1096 self.assertRaises(ValueError, P('').with_suffix, '.gz') 1097 self.assertRaises(ValueError, P('.').with_suffix, '.gz') 1098 self.assertRaises(ValueError, P('/').with_suffix, '.gz') 1099 self.assertRaises(ValueError, P('//My/Share').with_suffix, '.gz') 1100 # Invalid suffix. 1101 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'gz') 1102 self.assertRaises(ValueError, P('c:a/b').with_suffix, '/') 1103 self.assertRaises(ValueError, P('c:a/b').with_suffix, '\\') 1104 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c:') 1105 self.assertRaises(ValueError, P('c:a/b').with_suffix, '/.gz') 1106 self.assertRaises(ValueError, P('c:a/b').with_suffix, '\\.gz') 1107 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c:.gz') 1108 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c/d') 1109 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c\\d') 1110 self.assertRaises(ValueError, P('c:a/b').with_suffix, '.c/d') 1111 self.assertRaises(ValueError, P('c:a/b').with_suffix, '.c\\d') 1112 1113 def test_relative_to(self): 1114 P = self.cls 1115 p = P('C:Foo/Bar') 1116 self.assertEqual(p.relative_to(P('c:')), P('Foo/Bar')) 1117 self.assertEqual(p.relative_to('c:'), P('Foo/Bar')) 1118 self.assertEqual(p.relative_to(P('c:foO')), P('Bar')) 1119 self.assertEqual(p.relative_to('c:foO'), P('Bar')) 1120 self.assertEqual(p.relative_to('c:foO/'), P('Bar')) 1121 self.assertEqual(p.relative_to(P('c:foO/baR')), P()) 1122 self.assertEqual(p.relative_to('c:foO/baR'), P()) 1123 # Unrelated paths. 1124 self.assertRaises(ValueError, p.relative_to, P()) 1125 self.assertRaises(ValueError, p.relative_to, '') 1126 self.assertRaises(ValueError, p.relative_to, P('d:')) 1127 self.assertRaises(ValueError, p.relative_to, P('/')) 1128 self.assertRaises(ValueError, p.relative_to, P('Foo')) 1129 self.assertRaises(ValueError, p.relative_to, P('/Foo')) 1130 self.assertRaises(ValueError, p.relative_to, P('C:/Foo')) 1131 self.assertRaises(ValueError, p.relative_to, P('C:Foo/Bar/Baz')) 1132 self.assertRaises(ValueError, p.relative_to, P('C:Foo/Baz')) 1133 p = P('C:/Foo/Bar') 1134 self.assertEqual(p.relative_to(P('c:')), P('/Foo/Bar')) 1135 self.assertEqual(p.relative_to('c:'), P('/Foo/Bar')) 1136 self.assertEqual(str(p.relative_to(P('c:'))), '\\Foo\\Bar') 1137 self.assertEqual(str(p.relative_to('c:')), '\\Foo\\Bar') 1138 self.assertEqual(p.relative_to(P('c:/')), P('Foo/Bar')) 1139 self.assertEqual(p.relative_to('c:/'), P('Foo/Bar')) 1140 self.assertEqual(p.relative_to(P('c:/foO')), P('Bar')) 1141 self.assertEqual(p.relative_to('c:/foO'), P('Bar')) 1142 self.assertEqual(p.relative_to('c:/foO/'), P('Bar')) 1143 self.assertEqual(p.relative_to(P('c:/foO/baR')), P()) 1144 self.assertEqual(p.relative_to('c:/foO/baR'), P()) 1145 # Unrelated paths. 1146 self.assertRaises(ValueError, p.relative_to, P('C:/Baz')) 1147 self.assertRaises(ValueError, p.relative_to, P('C:/Foo/Bar/Baz')) 1148 self.assertRaises(ValueError, p.relative_to, P('C:/Foo/Baz')) 1149 self.assertRaises(ValueError, p.relative_to, P('C:Foo')) 1150 self.assertRaises(ValueError, p.relative_to, P('d:')) 1151 self.assertRaises(ValueError, p.relative_to, P('d:/')) 1152 self.assertRaises(ValueError, p.relative_to, P('/')) 1153 self.assertRaises(ValueError, p.relative_to, P('/Foo')) 1154 self.assertRaises(ValueError, p.relative_to, P('//C/Foo')) 1155 # UNC paths. 1156 p = P('//Server/Share/Foo/Bar') 1157 self.assertEqual(p.relative_to(P('//sErver/sHare')), P('Foo/Bar')) 1158 self.assertEqual(p.relative_to('//sErver/sHare'), P('Foo/Bar')) 1159 self.assertEqual(p.relative_to('//sErver/sHare/'), P('Foo/Bar')) 1160 self.assertEqual(p.relative_to(P('//sErver/sHare/Foo')), P('Bar')) 1161 self.assertEqual(p.relative_to('//sErver/sHare/Foo'), P('Bar')) 1162 self.assertEqual(p.relative_to('//sErver/sHare/Foo/'), P('Bar')) 1163 self.assertEqual(p.relative_to(P('//sErver/sHare/Foo/Bar')), P()) 1164 self.assertEqual(p.relative_to('//sErver/sHare/Foo/Bar'), P()) 1165 # Unrelated paths. 1166 self.assertRaises(ValueError, p.relative_to, P('/Server/Share/Foo')) 1167 self.assertRaises(ValueError, p.relative_to, P('c:/Server/Share/Foo')) 1168 self.assertRaises(ValueError, p.relative_to, P('//z/Share/Foo')) 1169 self.assertRaises(ValueError, p.relative_to, P('//Server/z/Foo')) 1170 1171 def test_is_relative_to(self): 1172 P = self.cls 1173 p = P('C:Foo/Bar') 1174 self.assertTrue(p.is_relative_to(P('c:'))) 1175 self.assertTrue(p.is_relative_to('c:')) 1176 self.assertTrue(p.is_relative_to(P('c:foO'))) 1177 self.assertTrue(p.is_relative_to('c:foO')) 1178 self.assertTrue(p.is_relative_to('c:foO/')) 1179 self.assertTrue(p.is_relative_to(P('c:foO/baR'))) 1180 self.assertTrue(p.is_relative_to('c:foO/baR')) 1181 # Unrelated paths. 1182 self.assertFalse(p.is_relative_to(P())) 1183 self.assertFalse(p.is_relative_to('')) 1184 self.assertFalse(p.is_relative_to(P('d:'))) 1185 self.assertFalse(p.is_relative_to(P('/'))) 1186 self.assertFalse(p.is_relative_to(P('Foo'))) 1187 self.assertFalse(p.is_relative_to(P('/Foo'))) 1188 self.assertFalse(p.is_relative_to(P('C:/Foo'))) 1189 self.assertFalse(p.is_relative_to(P('C:Foo/Bar/Baz'))) 1190 self.assertFalse(p.is_relative_to(P('C:Foo/Baz'))) 1191 p = P('C:/Foo/Bar') 1192 self.assertTrue(p.is_relative_to('c:')) 1193 self.assertTrue(p.is_relative_to(P('c:/'))) 1194 self.assertTrue(p.is_relative_to(P('c:/foO'))) 1195 self.assertTrue(p.is_relative_to('c:/foO/')) 1196 self.assertTrue(p.is_relative_to(P('c:/foO/baR'))) 1197 self.assertTrue(p.is_relative_to('c:/foO/baR')) 1198 # Unrelated paths. 1199 self.assertFalse(p.is_relative_to(P('C:/Baz'))) 1200 self.assertFalse(p.is_relative_to(P('C:/Foo/Bar/Baz'))) 1201 self.assertFalse(p.is_relative_to(P('C:/Foo/Baz'))) 1202 self.assertFalse(p.is_relative_to(P('C:Foo'))) 1203 self.assertFalse(p.is_relative_to(P('d:'))) 1204 self.assertFalse(p.is_relative_to(P('d:/'))) 1205 self.assertFalse(p.is_relative_to(P('/'))) 1206 self.assertFalse(p.is_relative_to(P('/Foo'))) 1207 self.assertFalse(p.is_relative_to(P('//C/Foo'))) 1208 # UNC paths. 1209 p = P('//Server/Share/Foo/Bar') 1210 self.assertTrue(p.is_relative_to(P('//sErver/sHare'))) 1211 self.assertTrue(p.is_relative_to('//sErver/sHare')) 1212 self.assertTrue(p.is_relative_to('//sErver/sHare/')) 1213 self.assertTrue(p.is_relative_to(P('//sErver/sHare/Foo'))) 1214 self.assertTrue(p.is_relative_to('//sErver/sHare/Foo')) 1215 self.assertTrue(p.is_relative_to('//sErver/sHare/Foo/')) 1216 self.assertTrue(p.is_relative_to(P('//sErver/sHare/Foo/Bar'))) 1217 self.assertTrue(p.is_relative_to('//sErver/sHare/Foo/Bar')) 1218 # Unrelated paths. 1219 self.assertFalse(p.is_relative_to(P('/Server/Share/Foo'))) 1220 self.assertFalse(p.is_relative_to(P('c:/Server/Share/Foo'))) 1221 self.assertFalse(p.is_relative_to(P('//z/Share/Foo'))) 1222 self.assertFalse(p.is_relative_to(P('//Server/z/Foo'))) 1223 1224 def test_is_absolute(self): 1225 P = self.cls 1226 # Under NT, only paths with both a drive and a root are absolute. 1227 self.assertFalse(P().is_absolute()) 1228 self.assertFalse(P('a').is_absolute()) 1229 self.assertFalse(P('a/b/').is_absolute()) 1230 self.assertFalse(P('/').is_absolute()) 1231 self.assertFalse(P('/a').is_absolute()) 1232 self.assertFalse(P('/a/b/').is_absolute()) 1233 self.assertFalse(P('c:').is_absolute()) 1234 self.assertFalse(P('c:a').is_absolute()) 1235 self.assertFalse(P('c:a/b/').is_absolute()) 1236 self.assertTrue(P('c:/').is_absolute()) 1237 self.assertTrue(P('c:/a').is_absolute()) 1238 self.assertTrue(P('c:/a/b/').is_absolute()) 1239 # UNC paths are absolute by definition. 1240 self.assertTrue(P('//a/b').is_absolute()) 1241 self.assertTrue(P('//a/b/').is_absolute()) 1242 self.assertTrue(P('//a/b/c').is_absolute()) 1243 self.assertTrue(P('//a/b/c/d').is_absolute()) 1244 1245 def test_join(self): 1246 P = self.cls 1247 p = P('C:/a/b') 1248 pp = p.joinpath('x/y') 1249 self.assertEqual(pp, P('C:/a/b/x/y')) 1250 pp = p.joinpath('/x/y') 1251 self.assertEqual(pp, P('C:/x/y')) 1252 # Joining with a different drive => the first path is ignored, even 1253 # if the second path is relative. 1254 pp = p.joinpath('D:x/y') 1255 self.assertEqual(pp, P('D:x/y')) 1256 pp = p.joinpath('D:/x/y') 1257 self.assertEqual(pp, P('D:/x/y')) 1258 pp = p.joinpath('//host/share/x/y') 1259 self.assertEqual(pp, P('//host/share/x/y')) 1260 # Joining with the same drive => the first path is appended to if 1261 # the second path is relative. 1262 pp = p.joinpath('c:x/y') 1263 self.assertEqual(pp, P('C:/a/b/x/y')) 1264 pp = p.joinpath('c:/x/y') 1265 self.assertEqual(pp, P('C:/x/y')) 1266 1267 def test_div(self): 1268 # Basically the same as joinpath(). 1269 P = self.cls 1270 p = P('C:/a/b') 1271 self.assertEqual(p / 'x/y', P('C:/a/b/x/y')) 1272 self.assertEqual(p / 'x' / 'y', P('C:/a/b/x/y')) 1273 self.assertEqual(p / '/x/y', P('C:/x/y')) 1274 self.assertEqual(p / '/x' / 'y', P('C:/x/y')) 1275 # Joining with a different drive => the first path is ignored, even 1276 # if the second path is relative. 1277 self.assertEqual(p / 'D:x/y', P('D:x/y')) 1278 self.assertEqual(p / 'D:' / 'x/y', P('D:x/y')) 1279 self.assertEqual(p / 'D:/x/y', P('D:/x/y')) 1280 self.assertEqual(p / 'D:' / '/x/y', P('D:/x/y')) 1281 self.assertEqual(p / '//host/share/x/y', P('//host/share/x/y')) 1282 # Joining with the same drive => the first path is appended to if 1283 # the second path is relative. 1284 self.assertEqual(p / 'c:x/y', P('C:/a/b/x/y')) 1285 self.assertEqual(p / 'c:/x/y', P('C:/x/y')) 1286 1287 def test_is_reserved(self): 1288 P = self.cls 1289 self.assertIs(False, P('').is_reserved()) 1290 self.assertIs(False, P('/').is_reserved()) 1291 self.assertIs(False, P('/foo/bar').is_reserved()) 1292 # UNC paths are never reserved. 1293 self.assertIs(False, P('//my/share/nul/con/aux').is_reserved()) 1294 # Case-insensitive DOS-device names are reserved. 1295 self.assertIs(True, P('nul').is_reserved()) 1296 self.assertIs(True, P('aux').is_reserved()) 1297 self.assertIs(True, P('prn').is_reserved()) 1298 self.assertIs(True, P('con').is_reserved()) 1299 self.assertIs(True, P('conin$').is_reserved()) 1300 self.assertIs(True, P('conout$').is_reserved()) 1301 # COM/LPT + 1-9 or + superscript 1-3 are reserved. 1302 self.assertIs(True, P('COM1').is_reserved()) 1303 self.assertIs(True, P('LPT9').is_reserved()) 1304 self.assertIs(True, P('com\xb9').is_reserved()) 1305 self.assertIs(True, P('com\xb2').is_reserved()) 1306 self.assertIs(True, P('lpt\xb3').is_reserved()) 1307 # DOS-device name mataching ignores characters after a dot or 1308 # a colon and also ignores trailing spaces. 1309 self.assertIs(True, P('NUL.txt').is_reserved()) 1310 self.assertIs(True, P('PRN ').is_reserved()) 1311 self.assertIs(True, P('AUX .txt').is_reserved()) 1312 self.assertIs(True, P('COM1:bar').is_reserved()) 1313 self.assertIs(True, P('LPT9 :bar').is_reserved()) 1314 # DOS-device names are only matched at the beginning 1315 # of a path component. 1316 self.assertIs(False, P('bar.com9').is_reserved()) 1317 self.assertIs(False, P('bar.lpt9').is_reserved()) 1318 # Only the last path component matters. 1319 self.assertIs(True, P('c:/baz/con/NUL').is_reserved()) 1320 self.assertIs(False, P('c:/NUL/con/baz').is_reserved()) 1321 1322class PurePathTest(_BasePurePathTest, unittest.TestCase): 1323 cls = pathlib.PurePath 1324 1325 def test_concrete_class(self): 1326 p = self.cls('a') 1327 self.assertIs(type(p), 1328 pathlib.PureWindowsPath if os.name == 'nt' else pathlib.PurePosixPath) 1329 1330 def test_different_flavours_unequal(self): 1331 p = pathlib.PurePosixPath('a') 1332 q = pathlib.PureWindowsPath('a') 1333 self.assertNotEqual(p, q) 1334 1335 def test_different_flavours_unordered(self): 1336 p = pathlib.PurePosixPath('a') 1337 q = pathlib.PureWindowsPath('a') 1338 with self.assertRaises(TypeError): 1339 p < q 1340 with self.assertRaises(TypeError): 1341 p <= q 1342 with self.assertRaises(TypeError): 1343 p > q 1344 with self.assertRaises(TypeError): 1345 p >= q 1346 1347 1348# 1349# Tests for the concrete classes. 1350# 1351 1352# Make sure any symbolic links in the base test path are resolved. 1353BASE = os.path.realpath(TESTFN) 1354join = lambda *x: os.path.join(BASE, *x) 1355rel_join = lambda *x: os.path.join(TESTFN, *x) 1356 1357only_nt = unittest.skipIf(os.name != 'nt', 1358 'test requires a Windows-compatible system') 1359only_posix = unittest.skipIf(os.name == 'nt', 1360 'test requires a POSIX-compatible system') 1361 1362@only_posix 1363class PosixPathAsPureTest(PurePosixPathTest): 1364 cls = pathlib.PosixPath 1365 1366@only_nt 1367class WindowsPathAsPureTest(PureWindowsPathTest): 1368 cls = pathlib.WindowsPath 1369 1370 def test_owner(self): 1371 P = self.cls 1372 with self.assertRaises(NotImplementedError): 1373 P('c:/').owner() 1374 1375 def test_group(self): 1376 P = self.cls 1377 with self.assertRaises(NotImplementedError): 1378 P('c:/').group() 1379 1380 1381class _BasePathTest(object): 1382 """Tests for the FS-accessing functionalities of the Path classes.""" 1383 1384 # (BASE) 1385 # | 1386 # |-- brokenLink -> non-existing 1387 # |-- dirA 1388 # | `-- linkC -> ../dirB 1389 # |-- dirB 1390 # | |-- fileB 1391 # | `-- linkD -> ../dirB 1392 # |-- dirC 1393 # | |-- dirD 1394 # | | `-- fileD 1395 # | `-- fileC 1396 # | `-- novel.txt 1397 # |-- dirE # No permissions 1398 # |-- fileA 1399 # |-- linkA -> fileA 1400 # |-- linkB -> dirB 1401 # `-- brokenLinkLoop -> brokenLinkLoop 1402 # 1403 1404 def setUp(self): 1405 def cleanup(): 1406 os.chmod(join('dirE'), 0o777) 1407 os_helper.rmtree(BASE) 1408 self.addCleanup(cleanup) 1409 os.mkdir(BASE) 1410 os.mkdir(join('dirA')) 1411 os.mkdir(join('dirB')) 1412 os.mkdir(join('dirC')) 1413 os.mkdir(join('dirC', 'dirD')) 1414 os.mkdir(join('dirE')) 1415 with open(join('fileA'), 'wb') as f: 1416 f.write(b"this is file A\n") 1417 with open(join('dirB', 'fileB'), 'wb') as f: 1418 f.write(b"this is file B\n") 1419 with open(join('dirC', 'fileC'), 'wb') as f: 1420 f.write(b"this is file C\n") 1421 with open(join('dirC', 'novel.txt'), 'wb') as f: 1422 f.write(b"this is a novel\n") 1423 with open(join('dirC', 'dirD', 'fileD'), 'wb') as f: 1424 f.write(b"this is file D\n") 1425 os.chmod(join('dirE'), 0) 1426 if os_helper.can_symlink(): 1427 # Relative symlinks. 1428 os.symlink('fileA', join('linkA')) 1429 os.symlink('non-existing', join('brokenLink')) 1430 self.dirlink('dirB', join('linkB')) 1431 self.dirlink(os.path.join('..', 'dirB'), join('dirA', 'linkC')) 1432 # This one goes upwards, creating a loop. 1433 self.dirlink(os.path.join('..', 'dirB'), join('dirB', 'linkD')) 1434 # Broken symlink (pointing to itself). 1435 os.symlink('brokenLinkLoop', join('brokenLinkLoop')) 1436 1437 if os.name == 'nt': 1438 # Workaround for http://bugs.python.org/issue13772. 1439 def dirlink(self, src, dest): 1440 os.symlink(src, dest, target_is_directory=True) 1441 else: 1442 def dirlink(self, src, dest): 1443 os.symlink(src, dest) 1444 1445 def assertSame(self, path_a, path_b): 1446 self.assertTrue(os.path.samefile(str(path_a), str(path_b)), 1447 "%r and %r don't point to the same file" % 1448 (path_a, path_b)) 1449 1450 def assertFileNotFound(self, func, *args, **kwargs): 1451 with self.assertRaises(FileNotFoundError) as cm: 1452 func(*args, **kwargs) 1453 self.assertEqual(cm.exception.errno, errno.ENOENT) 1454 1455 def assertEqualNormCase(self, path_a, path_b): 1456 self.assertEqual(os.path.normcase(path_a), os.path.normcase(path_b)) 1457 1458 def _test_cwd(self, p): 1459 q = self.cls(os.getcwd()) 1460 self.assertEqual(p, q) 1461 self.assertEqualNormCase(str(p), str(q)) 1462 self.assertIs(type(p), type(q)) 1463 self.assertTrue(p.is_absolute()) 1464 1465 def test_cwd(self): 1466 p = self.cls.cwd() 1467 self._test_cwd(p) 1468 1469 def test_absolute_common(self): 1470 P = self.cls 1471 1472 with mock.patch("os.getcwd") as getcwd: 1473 getcwd.return_value = BASE 1474 1475 # Simple relative paths. 1476 self.assertEqual(str(P().absolute()), BASE) 1477 self.assertEqual(str(P('.').absolute()), BASE) 1478 self.assertEqual(str(P('a').absolute()), os.path.join(BASE, 'a')) 1479 self.assertEqual(str(P('a', 'b', 'c').absolute()), os.path.join(BASE, 'a', 'b', 'c')) 1480 1481 # Symlinks should not be resolved. 1482 self.assertEqual(str(P('linkB', 'fileB').absolute()), os.path.join(BASE, 'linkB', 'fileB')) 1483 self.assertEqual(str(P('brokenLink').absolute()), os.path.join(BASE, 'brokenLink')) 1484 self.assertEqual(str(P('brokenLinkLoop').absolute()), os.path.join(BASE, 'brokenLinkLoop')) 1485 1486 # '..' entries should be preserved and not normalised. 1487 self.assertEqual(str(P('..').absolute()), os.path.join(BASE, '..')) 1488 self.assertEqual(str(P('a', '..').absolute()), os.path.join(BASE, 'a', '..')) 1489 self.assertEqual(str(P('..', 'b').absolute()), os.path.join(BASE, '..', 'b')) 1490 1491 def _test_home(self, p): 1492 q = self.cls(os.path.expanduser('~')) 1493 self.assertEqual(p, q) 1494 self.assertEqualNormCase(str(p), str(q)) 1495 self.assertIs(type(p), type(q)) 1496 self.assertTrue(p.is_absolute()) 1497 1498 @unittest.skipIf( 1499 pwd is None, reason="Test requires pwd module to get homedir." 1500 ) 1501 def test_home(self): 1502 with os_helper.EnvironmentVarGuard() as env: 1503 self._test_home(self.cls.home()) 1504 1505 env.clear() 1506 env['USERPROFILE'] = os.path.join(BASE, 'userprofile') 1507 self._test_home(self.cls.home()) 1508 1509 # bpo-38883: ignore `HOME` when set on windows 1510 env['HOME'] = os.path.join(BASE, 'home') 1511 self._test_home(self.cls.home()) 1512 1513 def test_samefile(self): 1514 fileA_path = os.path.join(BASE, 'fileA') 1515 fileB_path = os.path.join(BASE, 'dirB', 'fileB') 1516 p = self.cls(fileA_path) 1517 pp = self.cls(fileA_path) 1518 q = self.cls(fileB_path) 1519 self.assertTrue(p.samefile(fileA_path)) 1520 self.assertTrue(p.samefile(pp)) 1521 self.assertFalse(p.samefile(fileB_path)) 1522 self.assertFalse(p.samefile(q)) 1523 # Test the non-existent file case 1524 non_existent = os.path.join(BASE, 'foo') 1525 r = self.cls(non_existent) 1526 self.assertRaises(FileNotFoundError, p.samefile, r) 1527 self.assertRaises(FileNotFoundError, p.samefile, non_existent) 1528 self.assertRaises(FileNotFoundError, r.samefile, p) 1529 self.assertRaises(FileNotFoundError, r.samefile, non_existent) 1530 self.assertRaises(FileNotFoundError, r.samefile, r) 1531 self.assertRaises(FileNotFoundError, r.samefile, non_existent) 1532 1533 def test_empty_path(self): 1534 # The empty path points to '.' 1535 p = self.cls('') 1536 self.assertEqual(p.stat(), os.stat('.')) 1537 1538 @unittest.skipIf(is_wasi, "WASI has no user accounts.") 1539 def test_expanduser_common(self): 1540 P = self.cls 1541 p = P('~') 1542 self.assertEqual(p.expanduser(), P(os.path.expanduser('~'))) 1543 p = P('foo') 1544 self.assertEqual(p.expanduser(), p) 1545 p = P('/~') 1546 self.assertEqual(p.expanduser(), p) 1547 p = P('../~') 1548 self.assertEqual(p.expanduser(), p) 1549 p = P(P('').absolute().anchor) / '~' 1550 self.assertEqual(p.expanduser(), p) 1551 1552 def test_exists(self): 1553 P = self.cls 1554 p = P(BASE) 1555 self.assertIs(True, p.exists()) 1556 self.assertIs(True, (p / 'dirA').exists()) 1557 self.assertIs(True, (p / 'fileA').exists()) 1558 self.assertIs(False, (p / 'fileA' / 'bah').exists()) 1559 if os_helper.can_symlink(): 1560 self.assertIs(True, (p / 'linkA').exists()) 1561 self.assertIs(True, (p / 'linkB').exists()) 1562 self.assertIs(True, (p / 'linkB' / 'fileB').exists()) 1563 self.assertIs(False, (p / 'linkA' / 'bah').exists()) 1564 self.assertIs(False, (p / 'foo').exists()) 1565 self.assertIs(False, P('/xyzzy').exists()) 1566 self.assertIs(False, P(BASE + '\udfff').exists()) 1567 self.assertIs(False, P(BASE + '\x00').exists()) 1568 1569 def test_open_common(self): 1570 p = self.cls(BASE) 1571 with (p / 'fileA').open('r') as f: 1572 self.assertIsInstance(f, io.TextIOBase) 1573 self.assertEqual(f.read(), "this is file A\n") 1574 with (p / 'fileA').open('rb') as f: 1575 self.assertIsInstance(f, io.BufferedIOBase) 1576 self.assertEqual(f.read().strip(), b"this is file A") 1577 with (p / 'fileA').open('rb', buffering=0) as f: 1578 self.assertIsInstance(f, io.RawIOBase) 1579 self.assertEqual(f.read().strip(), b"this is file A") 1580 1581 def test_read_write_bytes(self): 1582 p = self.cls(BASE) 1583 (p / 'fileA').write_bytes(b'abcdefg') 1584 self.assertEqual((p / 'fileA').read_bytes(), b'abcdefg') 1585 # Check that trying to write str does not truncate the file. 1586 self.assertRaises(TypeError, (p / 'fileA').write_bytes, 'somestr') 1587 self.assertEqual((p / 'fileA').read_bytes(), b'abcdefg') 1588 1589 def test_read_write_text(self): 1590 p = self.cls(BASE) 1591 (p / 'fileA').write_text('äbcdefg', encoding='latin-1') 1592 self.assertEqual((p / 'fileA').read_text( 1593 encoding='utf-8', errors='ignore'), 'bcdefg') 1594 # Check that trying to write bytes does not truncate the file. 1595 self.assertRaises(TypeError, (p / 'fileA').write_text, b'somebytes') 1596 self.assertEqual((p / 'fileA').read_text(encoding='latin-1'), 'äbcdefg') 1597 1598 def test_write_text_with_newlines(self): 1599 p = self.cls(BASE) 1600 # Check that `\n` character change nothing 1601 (p / 'fileA').write_text('abcde\r\nfghlk\n\rmnopq', newline='\n') 1602 self.assertEqual((p / 'fileA').read_bytes(), 1603 b'abcde\r\nfghlk\n\rmnopq') 1604 # Check that `\r` character replaces `\n` 1605 (p / 'fileA').write_text('abcde\r\nfghlk\n\rmnopq', newline='\r') 1606 self.assertEqual((p / 'fileA').read_bytes(), 1607 b'abcde\r\rfghlk\r\rmnopq') 1608 # Check that `\r\n` character replaces `\n` 1609 (p / 'fileA').write_text('abcde\r\nfghlk\n\rmnopq', newline='\r\n') 1610 self.assertEqual((p / 'fileA').read_bytes(), 1611 b'abcde\r\r\nfghlk\r\n\rmnopq') 1612 # Check that no argument passed will change `\n` to `os.linesep` 1613 os_linesep_byte = bytes(os.linesep, encoding='ascii') 1614 (p / 'fileA').write_text('abcde\nfghlk\n\rmnopq') 1615 self.assertEqual((p / 'fileA').read_bytes(), 1616 b'abcde' + os_linesep_byte + b'fghlk' + os_linesep_byte + b'\rmnopq') 1617 1618 def test_iterdir(self): 1619 P = self.cls 1620 p = P(BASE) 1621 it = p.iterdir() 1622 paths = set(it) 1623 expected = ['dirA', 'dirB', 'dirC', 'dirE', 'fileA'] 1624 if os_helper.can_symlink(): 1625 expected += ['linkA', 'linkB', 'brokenLink', 'brokenLinkLoop'] 1626 self.assertEqual(paths, { P(BASE, q) for q in expected }) 1627 1628 @os_helper.skip_unless_symlink 1629 def test_iterdir_symlink(self): 1630 # __iter__ on a symlink to a directory. 1631 P = self.cls 1632 p = P(BASE, 'linkB') 1633 paths = set(p.iterdir()) 1634 expected = { P(BASE, 'linkB', q) for q in ['fileB', 'linkD'] } 1635 self.assertEqual(paths, expected) 1636 1637 def test_iterdir_nodir(self): 1638 # __iter__ on something that is not a directory. 1639 p = self.cls(BASE, 'fileA') 1640 with self.assertRaises(OSError) as cm: 1641 next(p.iterdir()) 1642 # ENOENT or EINVAL under Windows, ENOTDIR otherwise 1643 # (see issue #12802). 1644 self.assertIn(cm.exception.errno, (errno.ENOTDIR, 1645 errno.ENOENT, errno.EINVAL)) 1646 1647 def test_glob_common(self): 1648 def _check(glob, expected): 1649 self.assertEqual(set(glob), { P(BASE, q) for q in expected }) 1650 P = self.cls 1651 p = P(BASE) 1652 it = p.glob("fileA") 1653 self.assertIsInstance(it, collections.abc.Iterator) 1654 _check(it, ["fileA"]) 1655 _check(p.glob("fileB"), []) 1656 _check(p.glob("dir*/file*"), ["dirB/fileB", "dirC/fileC"]) 1657 if not os_helper.can_symlink(): 1658 _check(p.glob("*A"), ['dirA', 'fileA']) 1659 else: 1660 _check(p.glob("*A"), ['dirA', 'fileA', 'linkA']) 1661 if not os_helper.can_symlink(): 1662 _check(p.glob("*B/*"), ['dirB/fileB']) 1663 else: 1664 _check(p.glob("*B/*"), ['dirB/fileB', 'dirB/linkD', 1665 'linkB/fileB', 'linkB/linkD']) 1666 if not os_helper.can_symlink(): 1667 _check(p.glob("*/fileB"), ['dirB/fileB']) 1668 else: 1669 _check(p.glob("*/fileB"), ['dirB/fileB', 'linkB/fileB']) 1670 1671 if not os_helper.can_symlink(): 1672 _check(p.glob("*/"), ["dirA", "dirB", "dirC", "dirE"]) 1673 else: 1674 _check(p.glob("*/"), ["dirA", "dirB", "dirC", "dirE", "linkB"]) 1675 1676 def test_rglob_common(self): 1677 def _check(glob, expected): 1678 self.assertEqual(set(glob), { P(BASE, q) for q in expected }) 1679 P = self.cls 1680 p = P(BASE) 1681 it = p.rglob("fileA") 1682 self.assertIsInstance(it, collections.abc.Iterator) 1683 _check(it, ["fileA"]) 1684 _check(p.rglob("fileB"), ["dirB/fileB"]) 1685 _check(p.rglob("*/fileA"), []) 1686 if not os_helper.can_symlink(): 1687 _check(p.rglob("*/fileB"), ["dirB/fileB"]) 1688 else: 1689 _check(p.rglob("*/fileB"), ["dirB/fileB", "dirB/linkD/fileB", 1690 "linkB/fileB", "dirA/linkC/fileB"]) 1691 _check(p.rglob("file*"), ["fileA", "dirB/fileB", 1692 "dirC/fileC", "dirC/dirD/fileD"]) 1693 if not os_helper.can_symlink(): 1694 _check(p.rglob("*/"), [ 1695 "dirA", "dirB", "dirC", "dirC/dirD", "dirE", 1696 ]) 1697 else: 1698 _check(p.rglob("*/"), [ 1699 "dirA", "dirA/linkC", "dirB", "dirB/linkD", "dirC", 1700 "dirC/dirD", "dirE", "linkB", 1701 ]) 1702 _check(p.rglob(""), ["", "dirA", "dirB", "dirC", "dirE", "dirC/dirD"]) 1703 1704 p = P(BASE, "dirC") 1705 _check(p.rglob("*"), ["dirC/fileC", "dirC/novel.txt", 1706 "dirC/dirD", "dirC/dirD/fileD"]) 1707 _check(p.rglob("file*"), ["dirC/fileC", "dirC/dirD/fileD"]) 1708 _check(p.rglob("*/*"), ["dirC/dirD/fileD"]) 1709 _check(p.rglob("*/"), ["dirC/dirD"]) 1710 _check(p.rglob(""), ["dirC", "dirC/dirD"]) 1711 # gh-91616, a re module regression 1712 _check(p.rglob("*.txt"), ["dirC/novel.txt"]) 1713 _check(p.rglob("*.*"), ["dirC/novel.txt"]) 1714 1715 @os_helper.skip_unless_symlink 1716 def test_rglob_symlink_loop(self): 1717 # Don't get fooled by symlink loops (Issue #26012). 1718 P = self.cls 1719 p = P(BASE) 1720 given = set(p.rglob('*')) 1721 expect = {'brokenLink', 1722 'dirA', 'dirA/linkC', 1723 'dirB', 'dirB/fileB', 'dirB/linkD', 1724 'dirC', 'dirC/dirD', 'dirC/dirD/fileD', 1725 'dirC/fileC', 'dirC/novel.txt', 1726 'dirE', 1727 'fileA', 1728 'linkA', 1729 'linkB', 1730 'brokenLinkLoop', 1731 } 1732 self.assertEqual(given, {p / x for x in expect}) 1733 1734 def test_glob_many_open_files(self): 1735 depth = 30 1736 P = self.cls 1737 base = P(BASE) / 'deep' 1738 p = P(base, *(['d']*depth)) 1739 p.mkdir(parents=True) 1740 pattern = '/'.join(['*'] * depth) 1741 iters = [base.glob(pattern) for j in range(100)] 1742 for it in iters: 1743 self.assertEqual(next(it), p) 1744 iters = [base.rglob('d') for j in range(100)] 1745 p = base 1746 for i in range(depth): 1747 p = p / 'd' 1748 for it in iters: 1749 self.assertEqual(next(it), p) 1750 1751 def test_glob_dotdot(self): 1752 # ".." is not special in globs. 1753 P = self.cls 1754 p = P(BASE) 1755 self.assertEqual(set(p.glob("..")), { P(BASE, "..") }) 1756 self.assertEqual(set(p.glob("dirA/../file*")), { P(BASE, "dirA/../fileA") }) 1757 self.assertEqual(set(p.glob("../xyzzy")), set()) 1758 1759 @os_helper.skip_unless_symlink 1760 def test_glob_permissions(self): 1761 # See bpo-38894 1762 P = self.cls 1763 base = P(BASE) / 'permissions' 1764 base.mkdir() 1765 1766 file1 = base / "file1" 1767 file1.touch() 1768 file2 = base / "file2" 1769 file2.touch() 1770 1771 subdir = base / "subdir" 1772 1773 file3 = base / "file3" 1774 file3.symlink_to(subdir / "other") 1775 1776 # Patching is needed to avoid relying on the filesystem 1777 # to return the order of the files as the error will not 1778 # happen if the symlink is the last item. 1779 real_scandir = os.scandir 1780 def my_scandir(path): 1781 with real_scandir(path) as scandir_it: 1782 entries = list(scandir_it) 1783 entries.sort(key=lambda entry: entry.name) 1784 return contextlib.nullcontext(entries) 1785 1786 with mock.patch("os.scandir", my_scandir): 1787 self.assertEqual(len(set(base.glob("*"))), 3) 1788 subdir.mkdir() 1789 self.assertEqual(len(set(base.glob("*"))), 4) 1790 subdir.chmod(000) 1791 self.assertEqual(len(set(base.glob("*"))), 4) 1792 1793 @os_helper.skip_unless_symlink 1794 def test_glob_long_symlink(self): 1795 # See gh-87695 1796 base = self.cls(BASE) / 'long_symlink' 1797 base.mkdir() 1798 bad_link = base / 'bad_link' 1799 bad_link.symlink_to("bad" * 200) 1800 self.assertEqual(sorted(base.glob('**/*')), [bad_link]) 1801 1802 def _check_resolve(self, p, expected, strict=True): 1803 q = p.resolve(strict) 1804 self.assertEqual(q, expected) 1805 1806 # This can be used to check both relative and absolute resolutions. 1807 _check_resolve_relative = _check_resolve_absolute = _check_resolve 1808 1809 @os_helper.skip_unless_symlink 1810 def test_resolve_common(self): 1811 P = self.cls 1812 p = P(BASE, 'foo') 1813 with self.assertRaises(OSError) as cm: 1814 p.resolve(strict=True) 1815 self.assertEqual(cm.exception.errno, errno.ENOENT) 1816 # Non-strict 1817 self.assertEqualNormCase(str(p.resolve(strict=False)), 1818 os.path.join(BASE, 'foo')) 1819 p = P(BASE, 'foo', 'in', 'spam') 1820 self.assertEqualNormCase(str(p.resolve(strict=False)), 1821 os.path.join(BASE, 'foo', 'in', 'spam')) 1822 p = P(BASE, '..', 'foo', 'in', 'spam') 1823 self.assertEqualNormCase(str(p.resolve(strict=False)), 1824 os.path.abspath(os.path.join('foo', 'in', 'spam'))) 1825 # These are all relative symlinks. 1826 p = P(BASE, 'dirB', 'fileB') 1827 self._check_resolve_relative(p, p) 1828 p = P(BASE, 'linkA') 1829 self._check_resolve_relative(p, P(BASE, 'fileA')) 1830 p = P(BASE, 'dirA', 'linkC', 'fileB') 1831 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB')) 1832 p = P(BASE, 'dirB', 'linkD', 'fileB') 1833 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB')) 1834 # Non-strict 1835 p = P(BASE, 'dirA', 'linkC', 'fileB', 'foo', 'in', 'spam') 1836 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB', 'foo', 'in', 1837 'spam'), False) 1838 p = P(BASE, 'dirA', 'linkC', '..', 'foo', 'in', 'spam') 1839 if os.name == 'nt': 1840 # In Windows, if linkY points to dirB, 'dirA\linkY\..' 1841 # resolves to 'dirA' without resolving linkY first. 1842 self._check_resolve_relative(p, P(BASE, 'dirA', 'foo', 'in', 1843 'spam'), False) 1844 else: 1845 # In Posix, if linkY points to dirB, 'dirA/linkY/..' 1846 # resolves to 'dirB/..' first before resolving to parent of dirB. 1847 self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False) 1848 # Now create absolute symlinks. 1849 d = os_helper._longpath(tempfile.mkdtemp(suffix='-dirD', 1850 dir=os.getcwd())) 1851 self.addCleanup(os_helper.rmtree, d) 1852 os.symlink(os.path.join(d), join('dirA', 'linkX')) 1853 os.symlink(join('dirB'), os.path.join(d, 'linkY')) 1854 p = P(BASE, 'dirA', 'linkX', 'linkY', 'fileB') 1855 self._check_resolve_absolute(p, P(BASE, 'dirB', 'fileB')) 1856 # Non-strict 1857 p = P(BASE, 'dirA', 'linkX', 'linkY', 'foo', 'in', 'spam') 1858 self._check_resolve_relative(p, P(BASE, 'dirB', 'foo', 'in', 'spam'), 1859 False) 1860 p = P(BASE, 'dirA', 'linkX', 'linkY', '..', 'foo', 'in', 'spam') 1861 if os.name == 'nt': 1862 # In Windows, if linkY points to dirB, 'dirA\linkY\..' 1863 # resolves to 'dirA' without resolving linkY first. 1864 self._check_resolve_relative(p, P(d, 'foo', 'in', 'spam'), False) 1865 else: 1866 # In Posix, if linkY points to dirB, 'dirA/linkY/..' 1867 # resolves to 'dirB/..' first before resolving to parent of dirB. 1868 self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False) 1869 1870 @os_helper.skip_unless_symlink 1871 def test_resolve_dot(self): 1872 # See https://bitbucket.org/pitrou/pathlib/issue/9/pathresolve-fails-on-complex-symlinks 1873 p = self.cls(BASE) 1874 self.dirlink('.', join('0')) 1875 self.dirlink(os.path.join('0', '0'), join('1')) 1876 self.dirlink(os.path.join('1', '1'), join('2')) 1877 q = p / '2' 1878 self.assertEqual(q.resolve(strict=True), p) 1879 r = q / '3' / '4' 1880 self.assertRaises(FileNotFoundError, r.resolve, strict=True) 1881 # Non-strict 1882 self.assertEqual(r.resolve(strict=False), p / '3' / '4') 1883 1884 def test_resolve_nonexist_relative_issue38671(self): 1885 p = self.cls('non', 'exist') 1886 1887 old_cwd = os.getcwd() 1888 os.chdir(BASE) 1889 try: 1890 self.assertEqual(p.resolve(), self.cls(BASE, p)) 1891 finally: 1892 os.chdir(old_cwd) 1893 1894 def test_with(self): 1895 p = self.cls(BASE) 1896 it = p.iterdir() 1897 it2 = p.iterdir() 1898 next(it2) 1899 # bpo-46556: path context managers are deprecated in Python 3.11. 1900 with self.assertWarns(DeprecationWarning): 1901 with p: 1902 pass 1903 # Using a path as a context manager is a no-op, thus the following 1904 # operations should still succeed after the context manage exits. 1905 next(it) 1906 next(it2) 1907 p.exists() 1908 p.resolve() 1909 p.absolute() 1910 with self.assertWarns(DeprecationWarning): 1911 with p: 1912 pass 1913 1914 @os_helper.skip_unless_working_chmod 1915 def test_chmod(self): 1916 p = self.cls(BASE) / 'fileA' 1917 mode = p.stat().st_mode 1918 # Clear writable bit. 1919 new_mode = mode & ~0o222 1920 p.chmod(new_mode) 1921 self.assertEqual(p.stat().st_mode, new_mode) 1922 # Set writable bit. 1923 new_mode = mode | 0o222 1924 p.chmod(new_mode) 1925 self.assertEqual(p.stat().st_mode, new_mode) 1926 1927 # On Windows, os.chmod does not follow symlinks (issue #15411) 1928 @only_posix 1929 @os_helper.skip_unless_working_chmod 1930 def test_chmod_follow_symlinks_true(self): 1931 p = self.cls(BASE) / 'linkA' 1932 q = p.resolve() 1933 mode = q.stat().st_mode 1934 # Clear writable bit. 1935 new_mode = mode & ~0o222 1936 p.chmod(new_mode, follow_symlinks=True) 1937 self.assertEqual(q.stat().st_mode, new_mode) 1938 # Set writable bit 1939 new_mode = mode | 0o222 1940 p.chmod(new_mode, follow_symlinks=True) 1941 self.assertEqual(q.stat().st_mode, new_mode) 1942 1943 # XXX also need a test for lchmod. 1944 1945 @os_helper.skip_unless_working_chmod 1946 def test_stat(self): 1947 p = self.cls(BASE) / 'fileA' 1948 st = p.stat() 1949 self.assertEqual(p.stat(), st) 1950 # Change file mode by flipping write bit. 1951 p.chmod(st.st_mode ^ 0o222) 1952 self.addCleanup(p.chmod, st.st_mode) 1953 self.assertNotEqual(p.stat(), st) 1954 1955 @os_helper.skip_unless_symlink 1956 def test_stat_no_follow_symlinks(self): 1957 p = self.cls(BASE) / 'linkA' 1958 st = p.stat() 1959 self.assertNotEqual(st, p.stat(follow_symlinks=False)) 1960 1961 def test_stat_no_follow_symlinks_nosymlink(self): 1962 p = self.cls(BASE) / 'fileA' 1963 st = p.stat() 1964 self.assertEqual(st, p.stat(follow_symlinks=False)) 1965 1966 @os_helper.skip_unless_symlink 1967 def test_lstat(self): 1968 p = self.cls(BASE)/ 'linkA' 1969 st = p.stat() 1970 self.assertNotEqual(st, p.lstat()) 1971 1972 def test_lstat_nosymlink(self): 1973 p = self.cls(BASE) / 'fileA' 1974 st = p.stat() 1975 self.assertEqual(st, p.lstat()) 1976 1977 @unittest.skipUnless(pwd, "the pwd module is needed for this test") 1978 def test_owner(self): 1979 p = self.cls(BASE) / 'fileA' 1980 uid = p.stat().st_uid 1981 try: 1982 name = pwd.getpwuid(uid).pw_name 1983 except KeyError: 1984 self.skipTest( 1985 "user %d doesn't have an entry in the system database" % uid) 1986 self.assertEqual(name, p.owner()) 1987 1988 @unittest.skipUnless(grp, "the grp module is needed for this test") 1989 def test_group(self): 1990 p = self.cls(BASE) / 'fileA' 1991 gid = p.stat().st_gid 1992 try: 1993 name = grp.getgrgid(gid).gr_name 1994 except KeyError: 1995 self.skipTest( 1996 "group %d doesn't have an entry in the system database" % gid) 1997 self.assertEqual(name, p.group()) 1998 1999 def test_unlink(self): 2000 p = self.cls(BASE) / 'fileA' 2001 p.unlink() 2002 self.assertFileNotFound(p.stat) 2003 self.assertFileNotFound(p.unlink) 2004 2005 def test_unlink_missing_ok(self): 2006 p = self.cls(BASE) / 'fileAAA' 2007 self.assertFileNotFound(p.unlink) 2008 p.unlink(missing_ok=True) 2009 2010 def test_rmdir(self): 2011 p = self.cls(BASE) / 'dirA' 2012 for q in p.iterdir(): 2013 q.unlink() 2014 p.rmdir() 2015 self.assertFileNotFound(p.stat) 2016 self.assertFileNotFound(p.unlink) 2017 2018 @unittest.skipUnless(hasattr(os, "link"), "os.link() is not present") 2019 def test_link_to(self): 2020 P = self.cls(BASE) 2021 p = P / 'fileA' 2022 size = p.stat().st_size 2023 # linking to another path. 2024 q = P / 'dirA' / 'fileAA' 2025 try: 2026 with self.assertWarns(DeprecationWarning): 2027 p.link_to(q) 2028 except PermissionError as e: 2029 self.skipTest('os.link(): %s' % e) 2030 self.assertEqual(q.stat().st_size, size) 2031 self.assertEqual(os.path.samefile(p, q), True) 2032 self.assertTrue(p.stat) 2033 # Linking to a str of a relative path. 2034 r = rel_join('fileAAA') 2035 with self.assertWarns(DeprecationWarning): 2036 q.link_to(r) 2037 self.assertEqual(os.stat(r).st_size, size) 2038 self.assertTrue(q.stat) 2039 2040 @unittest.skipUnless(hasattr(os, "link"), "os.link() is not present") 2041 def test_hardlink_to(self): 2042 P = self.cls(BASE) 2043 target = P / 'fileA' 2044 size = target.stat().st_size 2045 # linking to another path. 2046 link = P / 'dirA' / 'fileAA' 2047 link.hardlink_to(target) 2048 self.assertEqual(link.stat().st_size, size) 2049 self.assertTrue(os.path.samefile(target, link)) 2050 self.assertTrue(target.exists()) 2051 # Linking to a str of a relative path. 2052 link2 = P / 'dirA' / 'fileAAA' 2053 target2 = rel_join('fileA') 2054 link2.hardlink_to(target2) 2055 self.assertEqual(os.stat(target2).st_size, size) 2056 self.assertTrue(link2.exists()) 2057 2058 @unittest.skipIf(hasattr(os, "link"), "os.link() is present") 2059 def test_link_to_not_implemented(self): 2060 P = self.cls(BASE) 2061 p = P / 'fileA' 2062 # linking to another path. 2063 q = P / 'dirA' / 'fileAA' 2064 with self.assertRaises(NotImplementedError): 2065 p.link_to(q) 2066 2067 def test_rename(self): 2068 P = self.cls(BASE) 2069 p = P / 'fileA' 2070 size = p.stat().st_size 2071 # Renaming to another path. 2072 q = P / 'dirA' / 'fileAA' 2073 renamed_p = p.rename(q) 2074 self.assertEqual(renamed_p, q) 2075 self.assertEqual(q.stat().st_size, size) 2076 self.assertFileNotFound(p.stat) 2077 # Renaming to a str of a relative path. 2078 r = rel_join('fileAAA') 2079 renamed_q = q.rename(r) 2080 self.assertEqual(renamed_q, self.cls(r)) 2081 self.assertEqual(os.stat(r).st_size, size) 2082 self.assertFileNotFound(q.stat) 2083 2084 def test_replace(self): 2085 P = self.cls(BASE) 2086 p = P / 'fileA' 2087 size = p.stat().st_size 2088 # Replacing a non-existing path. 2089 q = P / 'dirA' / 'fileAA' 2090 replaced_p = p.replace(q) 2091 self.assertEqual(replaced_p, q) 2092 self.assertEqual(q.stat().st_size, size) 2093 self.assertFileNotFound(p.stat) 2094 # Replacing another (existing) path. 2095 r = rel_join('dirB', 'fileB') 2096 replaced_q = q.replace(r) 2097 self.assertEqual(replaced_q, self.cls(r)) 2098 self.assertEqual(os.stat(r).st_size, size) 2099 self.assertFileNotFound(q.stat) 2100 2101 @os_helper.skip_unless_symlink 2102 def test_readlink(self): 2103 P = self.cls(BASE) 2104 self.assertEqual((P / 'linkA').readlink(), self.cls('fileA')) 2105 self.assertEqual((P / 'brokenLink').readlink(), 2106 self.cls('non-existing')) 2107 self.assertEqual((P / 'linkB').readlink(), self.cls('dirB')) 2108 with self.assertRaises(OSError): 2109 (P / 'fileA').readlink() 2110 2111 def test_touch_common(self): 2112 P = self.cls(BASE) 2113 p = P / 'newfileA' 2114 self.assertFalse(p.exists()) 2115 p.touch() 2116 self.assertTrue(p.exists()) 2117 st = p.stat() 2118 old_mtime = st.st_mtime 2119 old_mtime_ns = st.st_mtime_ns 2120 # Rewind the mtime sufficiently far in the past to work around 2121 # filesystem-specific timestamp granularity. 2122 os.utime(str(p), (old_mtime - 10, old_mtime - 10)) 2123 # The file mtime should be refreshed by calling touch() again. 2124 p.touch() 2125 st = p.stat() 2126 self.assertGreaterEqual(st.st_mtime_ns, old_mtime_ns) 2127 self.assertGreaterEqual(st.st_mtime, old_mtime) 2128 # Now with exist_ok=False. 2129 p = P / 'newfileB' 2130 self.assertFalse(p.exists()) 2131 p.touch(mode=0o700, exist_ok=False) 2132 self.assertTrue(p.exists()) 2133 self.assertRaises(OSError, p.touch, exist_ok=False) 2134 2135 def test_touch_nochange(self): 2136 P = self.cls(BASE) 2137 p = P / 'fileA' 2138 p.touch() 2139 with p.open('rb') as f: 2140 self.assertEqual(f.read().strip(), b"this is file A") 2141 2142 def test_mkdir(self): 2143 P = self.cls(BASE) 2144 p = P / 'newdirA' 2145 self.assertFalse(p.exists()) 2146 p.mkdir() 2147 self.assertTrue(p.exists()) 2148 self.assertTrue(p.is_dir()) 2149 with self.assertRaises(OSError) as cm: 2150 p.mkdir() 2151 self.assertEqual(cm.exception.errno, errno.EEXIST) 2152 2153 def test_mkdir_parents(self): 2154 # Creating a chain of directories. 2155 p = self.cls(BASE, 'newdirB', 'newdirC') 2156 self.assertFalse(p.exists()) 2157 with self.assertRaises(OSError) as cm: 2158 p.mkdir() 2159 self.assertEqual(cm.exception.errno, errno.ENOENT) 2160 p.mkdir(parents=True) 2161 self.assertTrue(p.exists()) 2162 self.assertTrue(p.is_dir()) 2163 with self.assertRaises(OSError) as cm: 2164 p.mkdir(parents=True) 2165 self.assertEqual(cm.exception.errno, errno.EEXIST) 2166 # Test `mode` arg. 2167 mode = stat.S_IMODE(p.stat().st_mode) # Default mode. 2168 p = self.cls(BASE, 'newdirD', 'newdirE') 2169 p.mkdir(0o555, parents=True) 2170 self.assertTrue(p.exists()) 2171 self.assertTrue(p.is_dir()) 2172 if os.name != 'nt': 2173 # The directory's permissions follow the mode argument. 2174 self.assertEqual(stat.S_IMODE(p.stat().st_mode), 0o7555 & mode) 2175 # The parent's permissions follow the default process settings. 2176 self.assertEqual(stat.S_IMODE(p.parent.stat().st_mode), mode) 2177 2178 def test_mkdir_exist_ok(self): 2179 p = self.cls(BASE, 'dirB') 2180 st_ctime_first = p.stat().st_ctime 2181 self.assertTrue(p.exists()) 2182 self.assertTrue(p.is_dir()) 2183 with self.assertRaises(FileExistsError) as cm: 2184 p.mkdir() 2185 self.assertEqual(cm.exception.errno, errno.EEXIST) 2186 p.mkdir(exist_ok=True) 2187 self.assertTrue(p.exists()) 2188 self.assertEqual(p.stat().st_ctime, st_ctime_first) 2189 2190 def test_mkdir_exist_ok_with_parent(self): 2191 p = self.cls(BASE, 'dirC') 2192 self.assertTrue(p.exists()) 2193 with self.assertRaises(FileExistsError) as cm: 2194 p.mkdir() 2195 self.assertEqual(cm.exception.errno, errno.EEXIST) 2196 p = p / 'newdirC' 2197 p.mkdir(parents=True) 2198 st_ctime_first = p.stat().st_ctime 2199 self.assertTrue(p.exists()) 2200 with self.assertRaises(FileExistsError) as cm: 2201 p.mkdir(parents=True) 2202 self.assertEqual(cm.exception.errno, errno.EEXIST) 2203 p.mkdir(parents=True, exist_ok=True) 2204 self.assertTrue(p.exists()) 2205 self.assertEqual(p.stat().st_ctime, st_ctime_first) 2206 2207 @unittest.skipIf(is_emscripten, "FS root cannot be modified on Emscripten.") 2208 def test_mkdir_exist_ok_root(self): 2209 # Issue #25803: A drive root could raise PermissionError on Windows. 2210 self.cls('/').resolve().mkdir(exist_ok=True) 2211 self.cls('/').resolve().mkdir(parents=True, exist_ok=True) 2212 2213 @only_nt # XXX: not sure how to test this on POSIX. 2214 def test_mkdir_with_unknown_drive(self): 2215 for d in 'ZYXWVUTSRQPONMLKJIHGFEDCBA': 2216 p = self.cls(d + ':\\') 2217 if not p.is_dir(): 2218 break 2219 else: 2220 self.skipTest("cannot find a drive that doesn't exist") 2221 with self.assertRaises(OSError): 2222 (p / 'child' / 'path').mkdir(parents=True) 2223 2224 def test_mkdir_with_child_file(self): 2225 p = self.cls(BASE, 'dirB', 'fileB') 2226 self.assertTrue(p.exists()) 2227 # An exception is raised when the last path component is an existing 2228 # regular file, regardless of whether exist_ok is true or not. 2229 with self.assertRaises(FileExistsError) as cm: 2230 p.mkdir(parents=True) 2231 self.assertEqual(cm.exception.errno, errno.EEXIST) 2232 with self.assertRaises(FileExistsError) as cm: 2233 p.mkdir(parents=True, exist_ok=True) 2234 self.assertEqual(cm.exception.errno, errno.EEXIST) 2235 2236 def test_mkdir_no_parents_file(self): 2237 p = self.cls(BASE, 'fileA') 2238 self.assertTrue(p.exists()) 2239 # An exception is raised when the last path component is an existing 2240 # regular file, regardless of whether exist_ok is true or not. 2241 with self.assertRaises(FileExistsError) as cm: 2242 p.mkdir() 2243 self.assertEqual(cm.exception.errno, errno.EEXIST) 2244 with self.assertRaises(FileExistsError) as cm: 2245 p.mkdir(exist_ok=True) 2246 self.assertEqual(cm.exception.errno, errno.EEXIST) 2247 2248 def test_mkdir_concurrent_parent_creation(self): 2249 for pattern_num in range(32): 2250 p = self.cls(BASE, 'dirCPC%d' % pattern_num) 2251 self.assertFalse(p.exists()) 2252 2253 real_mkdir = os.mkdir 2254 def my_mkdir(path, mode=0o777): 2255 path = str(path) 2256 # Emulate another process that would create the directory 2257 # just before we try to create it ourselves. We do it 2258 # in all possible pattern combinations, assuming that this 2259 # function is called at most 5 times (dirCPC/dir1/dir2, 2260 # dirCPC/dir1, dirCPC, dirCPC/dir1, dirCPC/dir1/dir2). 2261 if pattern.pop(): 2262 real_mkdir(path, mode) # From another process. 2263 concurrently_created.add(path) 2264 real_mkdir(path, mode) # Our real call. 2265 2266 pattern = [bool(pattern_num & (1 << n)) for n in range(5)] 2267 concurrently_created = set() 2268 p12 = p / 'dir1' / 'dir2' 2269 try: 2270 with mock.patch("os.mkdir", my_mkdir): 2271 p12.mkdir(parents=True, exist_ok=False) 2272 except FileExistsError: 2273 self.assertIn(str(p12), concurrently_created) 2274 else: 2275 self.assertNotIn(str(p12), concurrently_created) 2276 self.assertTrue(p.exists()) 2277 2278 @os_helper.skip_unless_symlink 2279 def test_symlink_to(self): 2280 P = self.cls(BASE) 2281 target = P / 'fileA' 2282 # Symlinking a path target. 2283 link = P / 'dirA' / 'linkAA' 2284 link.symlink_to(target) 2285 self.assertEqual(link.stat(), target.stat()) 2286 self.assertNotEqual(link.lstat(), target.stat()) 2287 # Symlinking a str target. 2288 link = P / 'dirA' / 'linkAAA' 2289 link.symlink_to(str(target)) 2290 self.assertEqual(link.stat(), target.stat()) 2291 self.assertNotEqual(link.lstat(), target.stat()) 2292 self.assertFalse(link.is_dir()) 2293 # Symlinking to a directory. 2294 target = P / 'dirB' 2295 link = P / 'dirA' / 'linkAAAA' 2296 link.symlink_to(target, target_is_directory=True) 2297 self.assertEqual(link.stat(), target.stat()) 2298 self.assertNotEqual(link.lstat(), target.stat()) 2299 self.assertTrue(link.is_dir()) 2300 self.assertTrue(list(link.iterdir())) 2301 2302 def test_is_dir(self): 2303 P = self.cls(BASE) 2304 self.assertTrue((P / 'dirA').is_dir()) 2305 self.assertFalse((P / 'fileA').is_dir()) 2306 self.assertFalse((P / 'non-existing').is_dir()) 2307 self.assertFalse((P / 'fileA' / 'bah').is_dir()) 2308 if os_helper.can_symlink(): 2309 self.assertFalse((P / 'linkA').is_dir()) 2310 self.assertTrue((P / 'linkB').is_dir()) 2311 self.assertFalse((P/ 'brokenLink').is_dir(), False) 2312 self.assertIs((P / 'dirA\udfff').is_dir(), False) 2313 self.assertIs((P / 'dirA\x00').is_dir(), False) 2314 2315 def test_is_file(self): 2316 P = self.cls(BASE) 2317 self.assertTrue((P / 'fileA').is_file()) 2318 self.assertFalse((P / 'dirA').is_file()) 2319 self.assertFalse((P / 'non-existing').is_file()) 2320 self.assertFalse((P / 'fileA' / 'bah').is_file()) 2321 if os_helper.can_symlink(): 2322 self.assertTrue((P / 'linkA').is_file()) 2323 self.assertFalse((P / 'linkB').is_file()) 2324 self.assertFalse((P/ 'brokenLink').is_file()) 2325 self.assertIs((P / 'fileA\udfff').is_file(), False) 2326 self.assertIs((P / 'fileA\x00').is_file(), False) 2327 2328 @only_posix 2329 def test_is_mount(self): 2330 P = self.cls(BASE) 2331 R = self.cls('/') # TODO: Work out Windows. 2332 self.assertFalse((P / 'fileA').is_mount()) 2333 self.assertFalse((P / 'dirA').is_mount()) 2334 self.assertFalse((P / 'non-existing').is_mount()) 2335 self.assertFalse((P / 'fileA' / 'bah').is_mount()) 2336 self.assertTrue(R.is_mount()) 2337 if os_helper.can_symlink(): 2338 self.assertFalse((P / 'linkA').is_mount()) 2339 self.assertIs(self.cls('/\udfff').is_mount(), False) 2340 self.assertIs(self.cls('/\x00').is_mount(), False) 2341 2342 def test_is_symlink(self): 2343 P = self.cls(BASE) 2344 self.assertFalse((P / 'fileA').is_symlink()) 2345 self.assertFalse((P / 'dirA').is_symlink()) 2346 self.assertFalse((P / 'non-existing').is_symlink()) 2347 self.assertFalse((P / 'fileA' / 'bah').is_symlink()) 2348 if os_helper.can_symlink(): 2349 self.assertTrue((P / 'linkA').is_symlink()) 2350 self.assertTrue((P / 'linkB').is_symlink()) 2351 self.assertTrue((P/ 'brokenLink').is_symlink()) 2352 self.assertIs((P / 'fileA\udfff').is_file(), False) 2353 self.assertIs((P / 'fileA\x00').is_file(), False) 2354 if os_helper.can_symlink(): 2355 self.assertIs((P / 'linkA\udfff').is_file(), False) 2356 self.assertIs((P / 'linkA\x00').is_file(), False) 2357 2358 def test_is_fifo_false(self): 2359 P = self.cls(BASE) 2360 self.assertFalse((P / 'fileA').is_fifo()) 2361 self.assertFalse((P / 'dirA').is_fifo()) 2362 self.assertFalse((P / 'non-existing').is_fifo()) 2363 self.assertFalse((P / 'fileA' / 'bah').is_fifo()) 2364 self.assertIs((P / 'fileA\udfff').is_fifo(), False) 2365 self.assertIs((P / 'fileA\x00').is_fifo(), False) 2366 2367 @unittest.skipUnless(hasattr(os, "mkfifo"), "os.mkfifo() required") 2368 @unittest.skipIf(sys.platform == "vxworks", 2369 "fifo requires special path on VxWorks") 2370 def test_is_fifo_true(self): 2371 P = self.cls(BASE, 'myfifo') 2372 try: 2373 os.mkfifo(str(P)) 2374 except PermissionError as e: 2375 self.skipTest('os.mkfifo(): %s' % e) 2376 self.assertTrue(P.is_fifo()) 2377 self.assertFalse(P.is_socket()) 2378 self.assertFalse(P.is_file()) 2379 self.assertIs(self.cls(BASE, 'myfifo\udfff').is_fifo(), False) 2380 self.assertIs(self.cls(BASE, 'myfifo\x00').is_fifo(), False) 2381 2382 def test_is_socket_false(self): 2383 P = self.cls(BASE) 2384 self.assertFalse((P / 'fileA').is_socket()) 2385 self.assertFalse((P / 'dirA').is_socket()) 2386 self.assertFalse((P / 'non-existing').is_socket()) 2387 self.assertFalse((P / 'fileA' / 'bah').is_socket()) 2388 self.assertIs((P / 'fileA\udfff').is_socket(), False) 2389 self.assertIs((P / 'fileA\x00').is_socket(), False) 2390 2391 @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") 2392 @unittest.skipIf( 2393 is_emscripten, "Unix sockets are not implemented on Emscripten." 2394 ) 2395 @unittest.skipIf( 2396 is_wasi, "Cannot create socket on WASI." 2397 ) 2398 def test_is_socket_true(self): 2399 P = self.cls(BASE, 'mysock') 2400 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 2401 self.addCleanup(sock.close) 2402 try: 2403 sock.bind(str(P)) 2404 except OSError as e: 2405 if (isinstance(e, PermissionError) or 2406 "AF_UNIX path too long" in str(e)): 2407 self.skipTest("cannot bind Unix socket: " + str(e)) 2408 self.assertTrue(P.is_socket()) 2409 self.assertFalse(P.is_fifo()) 2410 self.assertFalse(P.is_file()) 2411 self.assertIs(self.cls(BASE, 'mysock\udfff').is_socket(), False) 2412 self.assertIs(self.cls(BASE, 'mysock\x00').is_socket(), False) 2413 2414 def test_is_block_device_false(self): 2415 P = self.cls(BASE) 2416 self.assertFalse((P / 'fileA').is_block_device()) 2417 self.assertFalse((P / 'dirA').is_block_device()) 2418 self.assertFalse((P / 'non-existing').is_block_device()) 2419 self.assertFalse((P / 'fileA' / 'bah').is_block_device()) 2420 self.assertIs((P / 'fileA\udfff').is_block_device(), False) 2421 self.assertIs((P / 'fileA\x00').is_block_device(), False) 2422 2423 def test_is_char_device_false(self): 2424 P = self.cls(BASE) 2425 self.assertFalse((P / 'fileA').is_char_device()) 2426 self.assertFalse((P / 'dirA').is_char_device()) 2427 self.assertFalse((P / 'non-existing').is_char_device()) 2428 self.assertFalse((P / 'fileA' / 'bah').is_char_device()) 2429 self.assertIs((P / 'fileA\udfff').is_char_device(), False) 2430 self.assertIs((P / 'fileA\x00').is_char_device(), False) 2431 2432 def test_is_char_device_true(self): 2433 # Under Unix, /dev/null should generally be a char device. 2434 P = self.cls('/dev/null') 2435 if not P.exists(): 2436 self.skipTest("/dev/null required") 2437 self.assertTrue(P.is_char_device()) 2438 self.assertFalse(P.is_block_device()) 2439 self.assertFalse(P.is_file()) 2440 self.assertIs(self.cls('/dev/null\udfff').is_char_device(), False) 2441 self.assertIs(self.cls('/dev/null\x00').is_char_device(), False) 2442 2443 def test_pickling_common(self): 2444 p = self.cls(BASE, 'fileA') 2445 for proto in range(0, pickle.HIGHEST_PROTOCOL + 1): 2446 dumped = pickle.dumps(p, proto) 2447 pp = pickle.loads(dumped) 2448 self.assertEqual(pp.stat(), p.stat()) 2449 2450 def test_parts_interning(self): 2451 P = self.cls 2452 p = P('/usr/bin/foo') 2453 q = P('/usr/local/bin') 2454 # 'usr' 2455 self.assertIs(p.parts[1], q.parts[1]) 2456 # 'bin' 2457 self.assertIs(p.parts[2], q.parts[3]) 2458 2459 def _check_complex_symlinks(self, link0_target): 2460 # Test solving a non-looping chain of symlinks (issue #19887). 2461 P = self.cls(BASE) 2462 self.dirlink(os.path.join('link0', 'link0'), join('link1')) 2463 self.dirlink(os.path.join('link1', 'link1'), join('link2')) 2464 self.dirlink(os.path.join('link2', 'link2'), join('link3')) 2465 self.dirlink(link0_target, join('link0')) 2466 2467 # Resolve absolute paths. 2468 p = (P / 'link0').resolve() 2469 self.assertEqual(p, P) 2470 self.assertEqualNormCase(str(p), BASE) 2471 p = (P / 'link1').resolve() 2472 self.assertEqual(p, P) 2473 self.assertEqualNormCase(str(p), BASE) 2474 p = (P / 'link2').resolve() 2475 self.assertEqual(p, P) 2476 self.assertEqualNormCase(str(p), BASE) 2477 p = (P / 'link3').resolve() 2478 self.assertEqual(p, P) 2479 self.assertEqualNormCase(str(p), BASE) 2480 2481 # Resolve relative paths. 2482 old_path = os.getcwd() 2483 os.chdir(BASE) 2484 try: 2485 p = self.cls('link0').resolve() 2486 self.assertEqual(p, P) 2487 self.assertEqualNormCase(str(p), BASE) 2488 p = self.cls('link1').resolve() 2489 self.assertEqual(p, P) 2490 self.assertEqualNormCase(str(p), BASE) 2491 p = self.cls('link2').resolve() 2492 self.assertEqual(p, P) 2493 self.assertEqualNormCase(str(p), BASE) 2494 p = self.cls('link3').resolve() 2495 self.assertEqual(p, P) 2496 self.assertEqualNormCase(str(p), BASE) 2497 finally: 2498 os.chdir(old_path) 2499 2500 @os_helper.skip_unless_symlink 2501 def test_complex_symlinks_absolute(self): 2502 self._check_complex_symlinks(BASE) 2503 2504 @os_helper.skip_unless_symlink 2505 def test_complex_symlinks_relative(self): 2506 self._check_complex_symlinks('.') 2507 2508 @os_helper.skip_unless_symlink 2509 def test_complex_symlinks_relative_dot_dot(self): 2510 self._check_complex_symlinks(os.path.join('dirA', '..')) 2511 2512 2513class PathTest(_BasePathTest, unittest.TestCase): 2514 cls = pathlib.Path 2515 2516 def test_concrete_class(self): 2517 p = self.cls('a') 2518 self.assertIs(type(p), 2519 pathlib.WindowsPath if os.name == 'nt' else pathlib.PosixPath) 2520 2521 def test_unsupported_flavour(self): 2522 if os.name == 'nt': 2523 self.assertRaises(NotImplementedError, pathlib.PosixPath) 2524 else: 2525 self.assertRaises(NotImplementedError, pathlib.WindowsPath) 2526 2527 def test_glob_empty_pattern(self): 2528 p = self.cls() 2529 with self.assertRaisesRegex(ValueError, 'Unacceptable pattern'): 2530 list(p.glob('')) 2531 2532 2533@only_posix 2534class PosixPathTest(_BasePathTest, unittest.TestCase): 2535 cls = pathlib.PosixPath 2536 2537 def test_absolute(self): 2538 P = self.cls 2539 self.assertEqual(str(P('/').absolute()), '/') 2540 self.assertEqual(str(P('/a').absolute()), '/a') 2541 self.assertEqual(str(P('/a/b').absolute()), '/a/b') 2542 2543 # '//'-prefixed absolute path (supported by POSIX). 2544 self.assertEqual(str(P('//').absolute()), '//') 2545 self.assertEqual(str(P('//a').absolute()), '//a') 2546 self.assertEqual(str(P('//a/b').absolute()), '//a/b') 2547 2548 def _check_symlink_loop(self, *args, strict=True): 2549 path = self.cls(*args) 2550 with self.assertRaises(RuntimeError): 2551 print(path.resolve(strict)) 2552 2553 @unittest.skipIf( 2554 is_emscripten or is_wasi, 2555 "umask is not implemented on Emscripten/WASI." 2556 ) 2557 def test_open_mode(self): 2558 old_mask = os.umask(0) 2559 self.addCleanup(os.umask, old_mask) 2560 p = self.cls(BASE) 2561 with (p / 'new_file').open('wb'): 2562 pass 2563 st = os.stat(join('new_file')) 2564 self.assertEqual(stat.S_IMODE(st.st_mode), 0o666) 2565 os.umask(0o022) 2566 with (p / 'other_new_file').open('wb'): 2567 pass 2568 st = os.stat(join('other_new_file')) 2569 self.assertEqual(stat.S_IMODE(st.st_mode), 0o644) 2570 2571 def test_resolve_root(self): 2572 current_directory = os.getcwd() 2573 try: 2574 os.chdir('/') 2575 p = self.cls('spam') 2576 self.assertEqual(str(p.resolve()), '/spam') 2577 finally: 2578 os.chdir(current_directory) 2579 2580 @unittest.skipIf( 2581 is_emscripten or is_wasi, 2582 "umask is not implemented on Emscripten/WASI." 2583 ) 2584 def test_touch_mode(self): 2585 old_mask = os.umask(0) 2586 self.addCleanup(os.umask, old_mask) 2587 p = self.cls(BASE) 2588 (p / 'new_file').touch() 2589 st = os.stat(join('new_file')) 2590 self.assertEqual(stat.S_IMODE(st.st_mode), 0o666) 2591 os.umask(0o022) 2592 (p / 'other_new_file').touch() 2593 st = os.stat(join('other_new_file')) 2594 self.assertEqual(stat.S_IMODE(st.st_mode), 0o644) 2595 (p / 'masked_new_file').touch(mode=0o750) 2596 st = os.stat(join('masked_new_file')) 2597 self.assertEqual(stat.S_IMODE(st.st_mode), 0o750) 2598 2599 @os_helper.skip_unless_symlink 2600 def test_resolve_loop(self): 2601 # Loops with relative symlinks. 2602 os.symlink('linkX/inside', join('linkX')) 2603 self._check_symlink_loop(BASE, 'linkX') 2604 os.symlink('linkY', join('linkY')) 2605 self._check_symlink_loop(BASE, 'linkY') 2606 os.symlink('linkZ/../linkZ', join('linkZ')) 2607 self._check_symlink_loop(BASE, 'linkZ') 2608 # Non-strict 2609 self._check_symlink_loop(BASE, 'linkZ', 'foo', strict=False) 2610 # Loops with absolute symlinks. 2611 os.symlink(join('linkU/inside'), join('linkU')) 2612 self._check_symlink_loop(BASE, 'linkU') 2613 os.symlink(join('linkV'), join('linkV')) 2614 self._check_symlink_loop(BASE, 'linkV') 2615 os.symlink(join('linkW/../linkW'), join('linkW')) 2616 self._check_symlink_loop(BASE, 'linkW') 2617 # Non-strict 2618 self._check_symlink_loop(BASE, 'linkW', 'foo', strict=False) 2619 2620 def test_glob(self): 2621 P = self.cls 2622 p = P(BASE) 2623 given = set(p.glob("FILEa")) 2624 expect = set() if not os_helper.fs_is_case_insensitive(BASE) else given 2625 self.assertEqual(given, expect) 2626 self.assertEqual(set(p.glob("FILEa*")), set()) 2627 2628 def test_rglob(self): 2629 P = self.cls 2630 p = P(BASE, "dirC") 2631 given = set(p.rglob("FILEd")) 2632 expect = set() if not os_helper.fs_is_case_insensitive(BASE) else given 2633 self.assertEqual(given, expect) 2634 self.assertEqual(set(p.rglob("FILEd*")), set()) 2635 2636 @unittest.skipUnless(hasattr(pwd, 'getpwall'), 2637 'pwd module does not expose getpwall()') 2638 @unittest.skipIf(sys.platform == "vxworks", 2639 "no home directory on VxWorks") 2640 def test_expanduser(self): 2641 P = self.cls 2642 import_helper.import_module('pwd') 2643 import pwd 2644 pwdent = pwd.getpwuid(os.getuid()) 2645 username = pwdent.pw_name 2646 userhome = pwdent.pw_dir.rstrip('/') or '/' 2647 # Find arbitrary different user (if exists). 2648 for pwdent in pwd.getpwall(): 2649 othername = pwdent.pw_name 2650 otherhome = pwdent.pw_dir.rstrip('/') 2651 if othername != username and otherhome: 2652 break 2653 else: 2654 othername = username 2655 otherhome = userhome 2656 2657 fakename = 'fakeuser' 2658 # This user can theoretically exist on a test runner. Create unique name: 2659 try: 2660 while pwd.getpwnam(fakename): 2661 fakename += '1' 2662 except KeyError: 2663 pass # Non-existent name found 2664 2665 p1 = P('~/Documents') 2666 p2 = P(f'~{username}/Documents') 2667 p3 = P(f'~{othername}/Documents') 2668 p4 = P(f'../~{username}/Documents') 2669 p5 = P(f'/~{username}/Documents') 2670 p6 = P('') 2671 p7 = P(f'~{fakename}/Documents') 2672 2673 with os_helper.EnvironmentVarGuard() as env: 2674 env.pop('HOME', None) 2675 2676 self.assertEqual(p1.expanduser(), P(userhome) / 'Documents') 2677 self.assertEqual(p2.expanduser(), P(userhome) / 'Documents') 2678 self.assertEqual(p3.expanduser(), P(otherhome) / 'Documents') 2679 self.assertEqual(p4.expanduser(), p4) 2680 self.assertEqual(p5.expanduser(), p5) 2681 self.assertEqual(p6.expanduser(), p6) 2682 self.assertRaises(RuntimeError, p7.expanduser) 2683 2684 env['HOME'] = '/tmp' 2685 self.assertEqual(p1.expanduser(), P('/tmp/Documents')) 2686 self.assertEqual(p2.expanduser(), P(userhome) / 'Documents') 2687 self.assertEqual(p3.expanduser(), P(otherhome) / 'Documents') 2688 self.assertEqual(p4.expanduser(), p4) 2689 self.assertEqual(p5.expanduser(), p5) 2690 self.assertEqual(p6.expanduser(), p6) 2691 self.assertRaises(RuntimeError, p7.expanduser) 2692 2693 @unittest.skipIf(sys.platform != "darwin", 2694 "Bad file descriptor in /dev/fd affects only macOS") 2695 def test_handling_bad_descriptor(self): 2696 try: 2697 file_descriptors = list(pathlib.Path('/dev/fd').rglob("*"))[3:] 2698 if not file_descriptors: 2699 self.skipTest("no file descriptors - issue was not reproduced") 2700 # Checking all file descriptors because there is no guarantee 2701 # which one will fail. 2702 for f in file_descriptors: 2703 f.exists() 2704 f.is_dir() 2705 f.is_file() 2706 f.is_symlink() 2707 f.is_block_device() 2708 f.is_char_device() 2709 f.is_fifo() 2710 f.is_socket() 2711 except OSError as e: 2712 if e.errno == errno.EBADF: 2713 self.fail("Bad file descriptor not handled.") 2714 raise 2715 2716 2717@only_nt 2718class WindowsPathTest(_BasePathTest, unittest.TestCase): 2719 cls = pathlib.WindowsPath 2720 2721 def test_absolute(self): 2722 P = self.cls 2723 2724 # Simple absolute paths. 2725 self.assertEqual(str(P('c:\\').absolute()), 'c:\\') 2726 self.assertEqual(str(P('c:\\a').absolute()), 'c:\\a') 2727 self.assertEqual(str(P('c:\\a\\b').absolute()), 'c:\\a\\b') 2728 2729 # UNC absolute paths. 2730 share = '\\\\server\\share\\' 2731 self.assertEqual(str(P(share).absolute()), share) 2732 self.assertEqual(str(P(share + 'a').absolute()), share + 'a') 2733 self.assertEqual(str(P(share + 'a\\b').absolute()), share + 'a\\b') 2734 2735 # UNC relative paths. 2736 with mock.patch("os.getcwd") as getcwd: 2737 getcwd.return_value = share 2738 2739 self.assertEqual(str(P().absolute()), share) 2740 self.assertEqual(str(P('.').absolute()), share) 2741 self.assertEqual(str(P('a').absolute()), os.path.join(share, 'a')) 2742 self.assertEqual(str(P('a', 'b', 'c').absolute()), 2743 os.path.join(share, 'a', 'b', 'c')) 2744 2745 2746 def test_glob(self): 2747 P = self.cls 2748 p = P(BASE) 2749 self.assertEqual(set(p.glob("FILEa")), { P(BASE, "fileA") }) 2750 self.assertEqual(set(p.glob("*a\\")), { P(BASE, "dirA") }) 2751 self.assertEqual(set(p.glob("F*a")), { P(BASE, "fileA") }) 2752 self.assertEqual(set(map(str, p.glob("FILEa"))), {f"{p}\\FILEa"}) 2753 self.assertEqual(set(map(str, p.glob("F*a"))), {f"{p}\\fileA"}) 2754 2755 def test_rglob(self): 2756 P = self.cls 2757 p = P(BASE, "dirC") 2758 self.assertEqual(set(p.rglob("FILEd")), { P(BASE, "dirC/dirD/fileD") }) 2759 self.assertEqual(set(p.rglob("*\\")), { P(BASE, "dirC/dirD") }) 2760 self.assertEqual(set(map(str, p.rglob("FILEd"))), {f"{p}\\dirD\\FILEd"}) 2761 2762 def test_expanduser(self): 2763 P = self.cls 2764 with os_helper.EnvironmentVarGuard() as env: 2765 env.pop('HOME', None) 2766 env.pop('USERPROFILE', None) 2767 env.pop('HOMEPATH', None) 2768 env.pop('HOMEDRIVE', None) 2769 env['USERNAME'] = 'alice' 2770 2771 # test that the path returns unchanged 2772 p1 = P('~/My Documents') 2773 p2 = P('~alice/My Documents') 2774 p3 = P('~bob/My Documents') 2775 p4 = P('/~/My Documents') 2776 p5 = P('d:~/My Documents') 2777 p6 = P('') 2778 self.assertRaises(RuntimeError, p1.expanduser) 2779 self.assertRaises(RuntimeError, p2.expanduser) 2780 self.assertRaises(RuntimeError, p3.expanduser) 2781 self.assertEqual(p4.expanduser(), p4) 2782 self.assertEqual(p5.expanduser(), p5) 2783 self.assertEqual(p6.expanduser(), p6) 2784 2785 def check(): 2786 env.pop('USERNAME', None) 2787 self.assertEqual(p1.expanduser(), 2788 P('C:/Users/alice/My Documents')) 2789 self.assertRaises(RuntimeError, p2.expanduser) 2790 env['USERNAME'] = 'alice' 2791 self.assertEqual(p2.expanduser(), 2792 P('C:/Users/alice/My Documents')) 2793 self.assertEqual(p3.expanduser(), 2794 P('C:/Users/bob/My Documents')) 2795 self.assertEqual(p4.expanduser(), p4) 2796 self.assertEqual(p5.expanduser(), p5) 2797 self.assertEqual(p6.expanduser(), p6) 2798 2799 env['HOMEPATH'] = 'C:\\Users\\alice' 2800 check() 2801 2802 env['HOMEDRIVE'] = 'C:\\' 2803 env['HOMEPATH'] = 'Users\\alice' 2804 check() 2805 2806 env.pop('HOMEDRIVE', None) 2807 env.pop('HOMEPATH', None) 2808 env['USERPROFILE'] = 'C:\\Users\\alice' 2809 check() 2810 2811 # bpo-38883: ignore `HOME` when set on windows 2812 env['HOME'] = 'C:\\Users\\eve' 2813 check() 2814 2815 2816class CompatiblePathTest(unittest.TestCase): 2817 """ 2818 Test that a type can be made compatible with PurePath 2819 derivatives by implementing division operator overloads. 2820 """ 2821 2822 class CompatPath: 2823 """ 2824 Minimum viable class to test PurePath compatibility. 2825 Simply uses the division operator to join a given 2826 string and the string value of another object with 2827 a forward slash. 2828 """ 2829 def __init__(self, string): 2830 self.string = string 2831 2832 def __truediv__(self, other): 2833 return type(self)(f"{self.string}/{other}") 2834 2835 def __rtruediv__(self, other): 2836 return type(self)(f"{other}/{self.string}") 2837 2838 def test_truediv(self): 2839 result = pathlib.PurePath("test") / self.CompatPath("right") 2840 self.assertIsInstance(result, self.CompatPath) 2841 self.assertEqual(result.string, "test/right") 2842 2843 with self.assertRaises(TypeError): 2844 # Verify improper operations still raise a TypeError 2845 pathlib.PurePath("test") / 10 2846 2847 def test_rtruediv(self): 2848 result = self.CompatPath("left") / pathlib.PurePath("test") 2849 self.assertIsInstance(result, self.CompatPath) 2850 self.assertEqual(result.string, "left/test") 2851 2852 with self.assertRaises(TypeError): 2853 # Verify improper operations still raise a TypeError 2854 10 / pathlib.PurePath("test") 2855 2856 2857if __name__ == "__main__": 2858 unittest.main() 2859