1 import collections 2 import io 3 import os 4 import errno 5 import pathlib 6 import pickle 7 import socket 8 import stat 9 import tempfile 10 import unittest 11 12 from test import support 13 android_not_root = support.android_not_root 14 TESTFN = support.TESTFN 15 16 try: 17 import grp, pwd 18 except ImportError: 19 grp = pwd = None 20 21 22 class _BaseFlavourTest(object): 23 24 def _check_parse_parts(self, arg, expected): 25 f = self.flavour.parse_parts 26 sep = self.flavour.sep 27 altsep = self.flavour.altsep 28 actual = f([x.replace('/', sep) for x in arg]) 29 self.assertEqual(actual, expected) 30 if altsep: 31 actual = f([x.replace('/', altsep) for x in arg]) 32 self.assertEqual(actual, expected) 33 34 def test_parse_parts_common(self): 35 check = self._check_parse_parts 36 sep = self.flavour.sep 37 # Unanchored parts 38 check([], ('', '', [])) 39 check(['a'], ('', '', ['a'])) 40 check(['a/'], ('', '', ['a'])) 41 check(['a', 'b'], ('', '', ['a', 'b'])) 42 # Expansion 43 check(['a/b'], ('', '', ['a', 'b'])) 44 check(['a/b/'], ('', '', ['a', 'b'])) 45 check(['a', 'b/c', 'd'], ('', '', ['a', 'b', 'c', 'd'])) 46 # Collapsing and stripping excess slashes 47 check(['a', 'b//c', 'd'], ('', '', ['a', 'b', 'c', 'd'])) 48 check(['a', 'b/c/', 'd'], ('', '', ['a', 'b', 'c', 'd'])) 49 # Eliminating standalone dots 50 check(['.'], ('', '', [])) 51 check(['.', '.', 'b'], ('', '', ['b'])) 52 check(['a', '.', 'b'], ('', '', ['a', 'b'])) 53 check(['a', '.', '.'], ('', '', ['a'])) 54 # The first part is anchored 55 check(['/a/b'], ('', sep, [sep, 'a', 'b'])) 56 check(['/a', 'b'], ('', sep, [sep, 'a', 'b'])) 57 check(['/a/', 'b'], ('', sep, [sep, 'a', 'b'])) 58 # Ignoring parts before an anchored part 59 check(['a', '/b', 'c'], ('', sep, [sep, 'b', 'c'])) 60 check(['a', '/b', '/c'], ('', sep, [sep, 'c'])) 61 62 63 class PosixFlavourTest(_BaseFlavourTest, unittest.TestCase): 64 flavour = pathlib._posix_flavour 65 66 def test_parse_parts(self): 67 check = self._check_parse_parts 68 # Collapsing of excess leading slashes, except for the double-slash 69 # special case. 70 check(['//a', 'b'], ('', '//', ['//', 'a', 'b'])) 71 check(['///a', 'b'], ('', '/', ['/', 'a', 'b'])) 72 check(['////a', 'b'], ('', '/', ['/', 'a', 'b'])) 73 # Paths which look like NT paths aren't treated specially 74 check(['c:a'], ('', '', ['c:a'])) 75 check(['c:\\a'], ('', '', ['c:\\a'])) 76 check(['\\a'], ('', '', ['\\a'])) 77 78 def test_splitroot(self): 79 f = self.flavour.splitroot 80 self.assertEqual(f(''), ('', '', '')) 81 self.assertEqual(f('a'), ('', '', 'a')) 82 self.assertEqual(f('a/b'), ('', '', 'a/b')) 83 self.assertEqual(f('a/b/'), ('', '', 'a/b/')) 84 self.assertEqual(f('/a'), ('', '/', 'a')) 85 self.assertEqual(f('/a/b'), ('', '/', 'a/b')) 86 self.assertEqual(f('/a/b/'), ('', '/', 'a/b/')) 87 # The root is collapsed when there are redundant slashes 88 # except when there are exactly two leading slashes, which 89 # is a special case in POSIX. 90 self.assertEqual(f('//a'), ('', '//', 'a')) 91 self.assertEqual(f('///a'), ('', '/', 'a')) 92 self.assertEqual(f('///a/b'), ('', '/', 'a/b')) 93 # Paths which look like NT paths aren't treated specially 94 self.assertEqual(f('c:/a/b'), ('', '', 'c:/a/b')) 95 self.assertEqual(f('\\/a/b'), ('', '', '\\/a/b')) 96 self.assertEqual(f('\\a\\b'), ('', '', '\\a\\b')) 97 98 99 class NTFlavourTest(_BaseFlavourTest, unittest.TestCase): 100 flavour = pathlib._windows_flavour 101 102 def test_parse_parts(self): 103 check = self._check_parse_parts 104 # First part is anchored 105 check(['c:'], ('c:', '', ['c:'])) 106 check(['c:/'], ('c:', '\\', ['c:\\'])) 107 check(['/'], ('', '\\', ['\\'])) 108 check(['c:a'], ('c:', '', ['c:', 'a'])) 109 check(['c:/a'], ('c:', '\\', ['c:\\', 'a'])) 110 check(['/a'], ('', '\\', ['\\', 'a'])) 111 # UNC paths 112 check(['//a/b'], ('\\\\a\\b', '\\', ['\\\\a\\b\\'])) 113 check(['//a/b/'], ('\\\\a\\b', '\\', ['\\\\a\\b\\'])) 114 check(['//a/b/c'], ('\\\\a\\b', '\\', ['\\\\a\\b\\', 'c'])) 115 # Second part is anchored, so that the first part is ignored 116 check(['a', 'Z:b', 'c'], ('Z:', '', ['Z:', 'b', 'c'])) 117 check(['a', 'Z:/b', 'c'], ('Z:', '\\', ['Z:\\', 'b', 'c'])) 118 # UNC paths 119 check(['a', '//b/c', 'd'], ('\\\\b\\c', '\\', ['\\\\b\\c\\', 'd'])) 120 # Collapsing and stripping excess slashes 121 check(['a', 'Z://b//c/', 'd/'], ('Z:', '\\', ['Z:\\', 'b', 'c', 'd'])) 122 # UNC paths 123 check(['a', '//b/c//', 'd'], ('\\\\b\\c', '\\', ['\\\\b\\c\\', 'd'])) 124 # Extended paths 125 check(['//?/c:/'], ('\\\\?\\c:', '\\', ['\\\\?\\c:\\'])) 126 check(['//?/c:/a'], ('\\\\?\\c:', '\\', ['\\\\?\\c:\\', 'a'])) 127 check(['//?/c:/a', '/b'], ('\\\\?\\c:', '\\', ['\\\\?\\c:\\', 'b'])) 128 # Extended UNC paths (format is "\\?\UNC\server\share") 129 check(['//?/UNC/b/c'], ('\\\\?\\UNC\\b\\c', '\\', ['\\\\?\\UNC\\b\\c\\'])) 130 check(['//?/UNC/b/c/d'], ('\\\\?\\UNC\\b\\c', '\\', ['\\\\?\\UNC\\b\\c\\', 'd'])) 131 # Second part has a root but not drive 132 check(['a', '/b', 'c'], ('', '\\', ['\\', 'b', 'c'])) 133 check(['Z:/a', '/b', 'c'], ('Z:', '\\', ['Z:\\', 'b', 'c'])) 134 check(['//?/Z:/a', '/b', 'c'], ('\\\\?\\Z:', '\\', ['\\\\?\\Z:\\', 'b', 'c'])) 135 136 def test_splitroot(self): 137 f = self.flavour.splitroot 138 self.assertEqual(f(''), ('', '', '')) 139 self.assertEqual(f('a'), ('', '', 'a')) 140 self.assertEqual(f('a\\b'), ('', '', 'a\\b')) 141 self.assertEqual(f('\\a'), ('', '\\', 'a')) 142 self.assertEqual(f('\\a\\b'), ('', '\\', 'a\\b')) 143 self.assertEqual(f('c:a\\b'), ('c:', '', 'a\\b')) 144 self.assertEqual(f('c:\\a\\b'), ('c:', '\\', 'a\\b')) 145 # Redundant slashes in the root are collapsed 146 self.assertEqual(f('\\\\a'), ('', '\\', 'a')) 147 self.assertEqual(f('\\\\\\a/b'), ('', '\\', 'a/b')) 148 self.assertEqual(f('c:\\\\a'), ('c:', '\\', 'a')) 149 self.assertEqual(f('c:\\\\\\a/b'), ('c:', '\\', 'a/b')) 150 # Valid UNC paths 151 self.assertEqual(f('\\\\a\\b'), ('\\\\a\\b', '\\', '')) 152 self.assertEqual(f('\\\\a\\b\\'), ('\\\\a\\b', '\\', '')) 153 self.assertEqual(f('\\\\a\\b\\c\\d'), ('\\\\a\\b', '\\', 'c\\d')) 154 # These are non-UNC paths (according to ntpath.py and test_ntpath) 155 # However, command.com says such paths are invalid, so it's 156 # difficult to know what the right semantics are 157 self.assertEqual(f('\\\\\\a\\b'), ('', '\\', 'a\\b')) 158 self.assertEqual(f('\\\\a'), ('', '\\', 'a')) 159 160 161 # 162 # Tests for the pure classes 163 # 164 165 class _BasePurePathTest(object): 166 167 # keys are canonical paths, values are list of tuples of arguments 168 # supposed to produce equal paths 169 equivalences = { 170 'a/b': [ 171 ('a', 'b'), ('a/', 'b'), ('a', 'b/'), ('a/', 'b/'), 172 ('a/b/',), ('a//b',), ('a//b//',), 173 # empty components get removed 174 ('', 'a', 'b'), ('a', '', 'b'), ('a', 'b', ''), 175 ], 176 '/b/c/d': [ 177 ('a', '/b/c', 'd'), ('a', '///b//c', 'd/'), 178 ('/a', '/b/c', 'd'), 179 # empty components get removed 180 ('/', 'b', '', 'c/d'), ('/', '', 'b/c/d'), ('', '/b/c/d'), 181 ], 182 } 183 184 def setUp(self): 185 p = self.cls('a') 186 self.flavour = p._flavour 187 self.sep = self.flavour.sep 188 self.altsep = self.flavour.altsep 189 190 def test_constructor_common(self): 191 P = self.cls 192 p = P('a') 193 self.assertIsInstance(p, P) 194 class PathLike: 195 def __fspath__(self): 196 return "a/b/c" 197 P('a', 'b', 'c') 198 P('/a', 'b', 'c') 199 P('a/b/c') 200 P('/a/b/c') 201 P(PathLike()) 202 self.assertEqual(P(P('a')), P('a')) 203 self.assertEqual(P(P('a'), 'b'), P('a/b')) 204 self.assertEqual(P(P('a'), P('b')), P('a/b')) 205 self.assertEqual(P(P('a'), P('b'), P('c')), P(PathLike())) 206 207 def _check_str_subclass(self, *args): 208 # Issue #21127: it should be possible to construct a PurePath object 209 # from a str subclass instance, and it then gets converted to 210 # a pure str object. 211 class StrSubclass(str): 212 pass 213 P = self.cls 214 p = P(*(StrSubclass(x) for x in args)) 215 self.assertEqual(p, P(*args)) 216 for part in p.parts: 217 self.assertIs(type(part), str) 218 219 def test_str_subclass_common(self): 220 self._check_str_subclass('') 221 self._check_str_subclass('.') 222 self._check_str_subclass('a') 223 self._check_str_subclass('a/b.txt') 224 self._check_str_subclass('/a/b.txt') 225 226 def test_join_common(self): 227 P = self.cls 228 p = P('a/b') 229 pp = p.joinpath('c') 230 self.assertEqual(pp, P('a/b/c')) 231 self.assertIs(type(pp), type(p)) 232 pp = p.joinpath('c', 'd') 233 self.assertEqual(pp, P('a/b/c/d')) 234 pp = p.joinpath(P('c')) 235 self.assertEqual(pp, P('a/b/c')) 236 pp = p.joinpath('/c') 237 self.assertEqual(pp, P('/c')) 238 239 def test_div_common(self): 240 # Basically the same as joinpath() 241 P = self.cls 242 p = P('a/b') 243 pp = p / 'c' 244 self.assertEqual(pp, P('a/b/c')) 245 self.assertIs(type(pp), type(p)) 246 pp = p / 'c/d' 247 self.assertEqual(pp, P('a/b/c/d')) 248 pp = p / 'c' / 'd' 249 self.assertEqual(pp, P('a/b/c/d')) 250 pp = 'c' / p / 'd' 251 self.assertEqual(pp, P('c/a/b/d')) 252 pp = p / P('c') 253 self.assertEqual(pp, P('a/b/c')) 254 pp = p/ '/c' 255 self.assertEqual(pp, P('/c')) 256 257 def _check_str(self, expected, args): 258 p = self.cls(*args) 259 self.assertEqual(str(p), expected.replace('/', self.sep)) 260 261 def test_str_common(self): 262 # Canonicalized paths roundtrip 263 for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'): 264 self._check_str(pathstr, (pathstr,)) 265 # Special case for the empty path 266 self._check_str('.', ('',)) 267 # Other tests for str() are in test_equivalences() 268 269 def test_as_posix_common(self): 270 P = self.cls 271 for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'): 272 self.assertEqual(P(pathstr).as_posix(), pathstr) 273 # Other tests for as_posix() are in test_equivalences() 274 275 def test_as_bytes_common(self): 276 sep = os.fsencode(self.sep) 277 P = self.cls 278 self.assertEqual(bytes(P('a/b')), b'a' + sep + b'b') 279 280 def test_as_uri_common(self): 281 P = self.cls 282 with self.assertRaises(ValueError): 283 P('a').as_uri() 284 with self.assertRaises(ValueError): 285 P().as_uri() 286 287 def test_repr_common(self): 288 for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'): 289 p = self.cls(pathstr) 290 clsname = p.__class__.__name__ 291 r = repr(p) 292 # The repr() is in the form ClassName("forward-slashes path") 293 self.assertTrue(r.startswith(clsname + '('), r) 294 self.assertTrue(r.endswith(')'), r) 295 inner = r[len(clsname) + 1 : -1] 296 self.assertEqual(eval(inner), p.as_posix()) 297 # The repr() roundtrips 298 q = eval(r, pathlib.__dict__) 299 self.assertIs(q.__class__, p.__class__) 300 self.assertEqual(q, p) 301 self.assertEqual(repr(q), r) 302 303 def test_eq_common(self): 304 P = self.cls 305 self.assertEqual(P('a/b'), P('a/b')) 306 self.assertEqual(P('a/b'), P('a', 'b')) 307 self.assertNotEqual(P('a/b'), P('a')) 308 self.assertNotEqual(P('a/b'), P('/a/b')) 309 self.assertNotEqual(P('a/b'), P()) 310 self.assertNotEqual(P('/a/b'), P('/')) 311 self.assertNotEqual(P(), P('/')) 312 self.assertNotEqual(P(), "") 313 self.assertNotEqual(P(), {}) 314 self.assertNotEqual(P(), int) 315 316 def test_match_common(self): 317 P = self.cls 318 self.assertRaises(ValueError, P('a').match, '') 319 self.assertRaises(ValueError, P('a').match, '.') 320 # Simple relative pattern 321 self.assertTrue(P('b.py').match('b.py')) 322 self.assertTrue(P('a/b.py').match('b.py')) 323 self.assertTrue(P('/a/b.py').match('b.py')) 324 self.assertFalse(P('a.py').match('b.py')) 325 self.assertFalse(P('b/py').match('b.py')) 326 self.assertFalse(P('/a.py').match('b.py')) 327 self.assertFalse(P('b.py/c').match('b.py')) 328 # Wilcard relative pattern 329 self.assertTrue(P('b.py').match('*.py')) 330 self.assertTrue(P('a/b.py').match('*.py')) 331 self.assertTrue(P('/a/b.py').match('*.py')) 332 self.assertFalse(P('b.pyc').match('*.py')) 333 self.assertFalse(P('b./py').match('*.py')) 334 self.assertFalse(P('b.py/c').match('*.py')) 335 # Multi-part relative pattern 336 self.assertTrue(P('ab/c.py').match('a*/*.py')) 337 self.assertTrue(P('/d/ab/c.py').match('a*/*.py')) 338 self.assertFalse(P('a.py').match('a*/*.py')) 339 self.assertFalse(P('/dab/c.py').match('a*/*.py')) 340 self.assertFalse(P('ab/c.py/d').match('a*/*.py')) 341 # Absolute pattern 342 self.assertTrue(P('/b.py').match('/*.py')) 343 self.assertFalse(P('b.py').match('/*.py')) 344 self.assertFalse(P('a/b.py').match('/*.py')) 345 self.assertFalse(P('/a/b.py').match('/*.py')) 346 # Multi-part absolute pattern 347 self.assertTrue(P('/a/b.py').match('/a/*.py')) 348 self.assertFalse(P('/ab.py').match('/a/*.py')) 349 self.assertFalse(P('/a/b/c.py').match('/a/*.py')) 350 351 def test_ordering_common(self): 352 # Ordering is tuple-alike 353 def assertLess(a, b): 354 self.assertLess(a, b) 355 self.assertGreater(b, a) 356 P = self.cls 357 a = P('a') 358 b = P('a/b') 359 c = P('abc') 360 d = P('b') 361 assertLess(a, b) 362 assertLess(a, c) 363 assertLess(a, d) 364 assertLess(b, c) 365 assertLess(c, d) 366 P = self.cls 367 a = P('/a') 368 b = P('/a/b') 369 c = P('/abc') 370 d = P('/b') 371 assertLess(a, b) 372 assertLess(a, c) 373 assertLess(a, d) 374 assertLess(b, c) 375 assertLess(c, d) 376 with self.assertRaises(TypeError): 377 P() < {} 378 379 def test_parts_common(self): 380 # `parts` returns a tuple 381 sep = self.sep 382 P = self.cls 383 p = P('a/b') 384 parts = p.parts 385 self.assertEqual(parts, ('a', 'b')) 386 # The object gets reused 387 self.assertIs(parts, p.parts) 388 # When the path is absolute, the anchor is a separate part 389 p = P('/a/b') 390 parts = p.parts 391 self.assertEqual(parts, (sep, 'a', 'b')) 392 393 def test_fspath_common(self): 394 P = self.cls 395 p = P('a/b') 396 self._check_str(p.__fspath__(), ('a/b',)) 397 self._check_str(os.fspath(p), ('a/b',)) 398 399 def test_equivalences(self): 400 for k, tuples in self.equivalences.items(): 401 canon = k.replace('/', self.sep) 402 posix = k.replace(self.sep, '/') 403 if canon != posix: 404 tuples = tuples + [ 405 tuple(part.replace('/', self.sep) for part in t) 406 for t in tuples 407 ] 408 tuples.append((posix, )) 409 pcanon = self.cls(canon) 410 for t in tuples: 411 p = self.cls(*t) 412 self.assertEqual(p, pcanon, "failed with args {}".format(t)) 413 self.assertEqual(hash(p), hash(pcanon)) 414 self.assertEqual(str(p), canon) 415 self.assertEqual(p.as_posix(), posix) 416 417 def test_parent_common(self): 418 # Relative 419 P = self.cls 420 p = P('a/b/c') 421 self.assertEqual(p.parent, P('a/b')) 422 self.assertEqual(p.parent.parent, P('a')) 423 self.assertEqual(p.parent.parent.parent, P()) 424 self.assertEqual(p.parent.parent.parent.parent, P()) 425 # Anchored 426 p = P('/a/b/c') 427 self.assertEqual(p.parent, P('/a/b')) 428 self.assertEqual(p.parent.parent, P('/a')) 429 self.assertEqual(p.parent.parent.parent, P('/')) 430 self.assertEqual(p.parent.parent.parent.parent, P('/')) 431 432 def test_parents_common(self): 433 # Relative 434 P = self.cls 435 p = P('a/b/c') 436 par = p.parents 437 self.assertEqual(len(par), 3) 438 self.assertEqual(par[0], P('a/b')) 439 self.assertEqual(par[1], P('a')) 440 self.assertEqual(par[2], P('.')) 441 self.assertEqual(list(par), [P('a/b'), P('a'), P('.')]) 442 with self.assertRaises(IndexError): 443 par[-1] 444 with self.assertRaises(IndexError): 445 par[3] 446 with self.assertRaises(TypeError): 447 par[0] = p 448 # Anchored 449 p = P('/a/b/c') 450 par = p.parents 451 self.assertEqual(len(par), 3) 452 self.assertEqual(par[0], P('/a/b')) 453 self.assertEqual(par[1], P('/a')) 454 self.assertEqual(par[2], P('/')) 455 self.assertEqual(list(par), [P('/a/b'), P('/a'), P('/')]) 456 with self.assertRaises(IndexError): 457 par[3] 458 459 def test_drive_common(self): 460 P = self.cls 461 self.assertEqual(P('a/b').drive, '') 462 self.assertEqual(P('/a/b').drive, '') 463 self.assertEqual(P('').drive, '') 464 465 def test_root_common(self): 466 P = self.cls 467 sep = self.sep 468 self.assertEqual(P('').root, '') 469 self.assertEqual(P('a/b').root, '') 470 self.assertEqual(P('/').root, sep) 471 self.assertEqual(P('/a/b').root, sep) 472 473 def test_anchor_common(self): 474 P = self.cls 475 sep = self.sep 476 self.assertEqual(P('').anchor, '') 477 self.assertEqual(P('a/b').anchor, '') 478 self.assertEqual(P('/').anchor, sep) 479 self.assertEqual(P('/a/b').anchor, sep) 480 481 def test_name_common(self): 482 P = self.cls 483 self.assertEqual(P('').name, '') 484 self.assertEqual(P('.').name, '') 485 self.assertEqual(P('/').name, '') 486 self.assertEqual(P('a/b').name, 'b') 487 self.assertEqual(P('/a/b').name, 'b') 488 self.assertEqual(P('/a/b/.').name, 'b') 489 self.assertEqual(P('a/b.py').name, 'b.py') 490 self.assertEqual(P('/a/b.py').name, 'b.py') 491 492 def test_suffix_common(self): 493 P = self.cls 494 self.assertEqual(P('').suffix, '') 495 self.assertEqual(P('.').suffix, '') 496 self.assertEqual(P('..').suffix, '') 497 self.assertEqual(P('/').suffix, '') 498 self.assertEqual(P('a/b').suffix, '') 499 self.assertEqual(P('/a/b').suffix, '') 500 self.assertEqual(P('/a/b/.').suffix, '') 501 self.assertEqual(P('a/b.py').suffix, '.py') 502 self.assertEqual(P('/a/b.py').suffix, '.py') 503 self.assertEqual(P('a/.hgrc').suffix, '') 504 self.assertEqual(P('/a/.hgrc').suffix, '') 505 self.assertEqual(P('a/.hg.rc').suffix, '.rc') 506 self.assertEqual(P('/a/.hg.rc').suffix, '.rc') 507 self.assertEqual(P('a/b.tar.gz').suffix, '.gz') 508 self.assertEqual(P('/a/b.tar.gz').suffix, '.gz') 509 self.assertEqual(P('a/Some name. Ending with a dot.').suffix, '') 510 self.assertEqual(P('/a/Some name. Ending with a dot.').suffix, '') 511 512 def test_suffixes_common(self): 513 P = self.cls 514 self.assertEqual(P('').suffixes, []) 515 self.assertEqual(P('.').suffixes, []) 516 self.assertEqual(P('/').suffixes, []) 517 self.assertEqual(P('a/b').suffixes, []) 518 self.assertEqual(P('/a/b').suffixes, []) 519 self.assertEqual(P('/a/b/.').suffixes, []) 520 self.assertEqual(P('a/b.py').suffixes, ['.py']) 521 self.assertEqual(P('/a/b.py').suffixes, ['.py']) 522 self.assertEqual(P('a/.hgrc').suffixes, []) 523 self.assertEqual(P('/a/.hgrc').suffixes, []) 524 self.assertEqual(P('a/.hg.rc').suffixes, ['.rc']) 525 self.assertEqual(P('/a/.hg.rc').suffixes, ['.rc']) 526 self.assertEqual(P('a/b.tar.gz').suffixes, ['.tar', '.gz']) 527 self.assertEqual(P('/a/b.tar.gz').suffixes, ['.tar', '.gz']) 528 self.assertEqual(P('a/Some name. Ending with a dot.').suffixes, []) 529 self.assertEqual(P('/a/Some name. Ending with a dot.').suffixes, []) 530 531 def test_stem_common(self): 532 P = self.cls 533 self.assertEqual(P('').stem, '') 534 self.assertEqual(P('.').stem, '') 535 self.assertEqual(P('..').stem, '..') 536 self.assertEqual(P('/').stem, '') 537 self.assertEqual(P('a/b').stem, 'b') 538 self.assertEqual(P('a/b.py').stem, 'b') 539 self.assertEqual(P('a/.hgrc').stem, '.hgrc') 540 self.assertEqual(P('a/.hg.rc').stem, '.hg') 541 self.assertEqual(P('a/b.tar.gz').stem, 'b.tar') 542 self.assertEqual(P('a/Some name. Ending with a dot.').stem, 543 'Some name. Ending with a dot.') 544 545 def test_with_name_common(self): 546 P = self.cls 547 self.assertEqual(P('a/b').with_name('d.xml'), P('a/d.xml')) 548 self.assertEqual(P('/a/b').with_name('d.xml'), P('/a/d.xml')) 549 self.assertEqual(P('a/b.py').with_name('d.xml'), P('a/d.xml')) 550 self.assertEqual(P('/a/b.py').with_name('d.xml'), P('/a/d.xml')) 551 self.assertEqual(P('a/Dot ending.').with_name('d.xml'), P('a/d.xml')) 552 self.assertEqual(P('/a/Dot ending.').with_name('d.xml'), P('/a/d.xml')) 553 self.assertRaises(ValueError, P('').with_name, 'd.xml') 554 self.assertRaises(ValueError, P('.').with_name, 'd.xml') 555 self.assertRaises(ValueError, P('/').with_name, 'd.xml') 556 self.assertRaises(ValueError, P('a/b').with_name, '') 557 self.assertRaises(ValueError, P('a/b').with_name, '/c') 558 self.assertRaises(ValueError, P('a/b').with_name, 'c/') 559 self.assertRaises(ValueError, P('a/b').with_name, 'c/d') 560 561 def test_with_suffix_common(self): 562 P = self.cls 563 self.assertEqual(P('a/b').with_suffix('.gz'), P('a/b.gz')) 564 self.assertEqual(P('/a/b').with_suffix('.gz'), P('/a/b.gz')) 565 self.assertEqual(P('a/b.py').with_suffix('.gz'), P('a/b.gz')) 566 self.assertEqual(P('/a/b.py').with_suffix('.gz'), P('/a/b.gz')) 567 # Stripping suffix 568 self.assertEqual(P('a/b.py').with_suffix(''), P('a/b')) 569 self.assertEqual(P('/a/b').with_suffix(''), P('/a/b')) 570 # Path doesn't have a "filename" component 571 self.assertRaises(ValueError, P('').with_suffix, '.gz') 572 self.assertRaises(ValueError, P('.').with_suffix, '.gz') 573 self.assertRaises(ValueError, P('/').with_suffix, '.gz') 574 # Invalid suffix 575 self.assertRaises(ValueError, P('a/b').with_suffix, 'gz') 576 self.assertRaises(ValueError, P('a/b').with_suffix, '/') 577 self.assertRaises(ValueError, P('a/b').with_suffix, '.') 578 self.assertRaises(ValueError, P('a/b').with_suffix, '/.gz') 579 self.assertRaises(ValueError, P('a/b').with_suffix, 'c/d') 580 self.assertRaises(ValueError, P('a/b').with_suffix, '.c/.d') 581 self.assertRaises(ValueError, P('a/b').with_suffix, './.d') 582 self.assertRaises(ValueError, P('a/b').with_suffix, '.d/.') 583 584 def test_relative_to_common(self): 585 P = self.cls 586 p = P('a/b') 587 self.assertRaises(TypeError, p.relative_to) 588 self.assertRaises(TypeError, p.relative_to, b'a') 589 self.assertEqual(p.relative_to(P()), P('a/b')) 590 self.assertEqual(p.relative_to(''), P('a/b')) 591 self.assertEqual(p.relative_to(P('a')), P('b')) 592 self.assertEqual(p.relative_to('a'), P('b')) 593 self.assertEqual(p.relative_to('a/'), P('b')) 594 self.assertEqual(p.relative_to(P('a/b')), P()) 595 self.assertEqual(p.relative_to('a/b'), P()) 596 # With several args 597 self.assertEqual(p.relative_to('a', 'b'), P()) 598 # Unrelated paths 599 self.assertRaises(ValueError, p.relative_to, P('c')) 600 self.assertRaises(ValueError, p.relative_to, P('a/b/c')) 601 self.assertRaises(ValueError, p.relative_to, P('a/c')) 602 self.assertRaises(ValueError, p.relative_to, P('/a')) 603 p = P('/a/b') 604 self.assertEqual(p.relative_to(P('/')), P('a/b')) 605 self.assertEqual(p.relative_to('/'), P('a/b')) 606 self.assertEqual(p.relative_to(P('/a')), P('b')) 607 self.assertEqual(p.relative_to('/a'), P('b')) 608 self.assertEqual(p.relative_to('/a/'), P('b')) 609 self.assertEqual(p.relative_to(P('/a/b')), P()) 610 self.assertEqual(p.relative_to('/a/b'), P()) 611 # Unrelated paths 612 self.assertRaises(ValueError, p.relative_to, P('/c')) 613 self.assertRaises(ValueError, p.relative_to, P('/a/b/c')) 614 self.assertRaises(ValueError, p.relative_to, P('/a/c')) 615 self.assertRaises(ValueError, p.relative_to, P()) 616 self.assertRaises(ValueError, p.relative_to, '') 617 self.assertRaises(ValueError, p.relative_to, P('a')) 618 619 def test_pickling_common(self): 620 P = self.cls 621 p = P('/a/b') 622 for proto in range(0, pickle.HIGHEST_PROTOCOL + 1): 623 dumped = pickle.dumps(p, proto) 624 pp = pickle.loads(dumped) 625 self.assertIs(pp.__class__, p.__class__) 626 self.assertEqual(pp, p) 627 self.assertEqual(hash(pp), hash(p)) 628 self.assertEqual(str(pp), str(p)) 629 630 631 class PurePosixPathTest(_BasePurePathTest, unittest.TestCase): 632 cls = pathlib.PurePosixPath 633 634 def test_root(self): 635 P = self.cls 636 self.assertEqual(P('/a/b').root, '/') 637 self.assertEqual(P('///a/b').root, '/') 638 # POSIX special case for two leading slashes 639 self.assertEqual(P('//a/b').root, '//') 640 641 def test_eq(self): 642 P = self.cls 643 self.assertNotEqual(P('a/b'), P('A/b')) 644 self.assertEqual(P('/a'), P('///a')) 645 self.assertNotEqual(P('/a'), P('//a')) 646 647 def test_as_uri(self): 648 P = self.cls 649 self.assertEqual(P('/').as_uri(), 'file:///') 650 self.assertEqual(P('/a/b.c').as_uri(), 'file:///a/b.c') 651 self.assertEqual(P('/a/b%#c').as_uri(), 'file:///a/b%25%23c') 652 653 def test_as_uri_non_ascii(self): 654 from urllib.parse import quote_from_bytes 655 P = self.cls 656 try: 657 os.fsencode('\xe9') 658 except UnicodeEncodeError: 659 self.skipTest("\\xe9 cannot be encoded to the filesystem encoding") 660 self.assertEqual(P('/a/b\xe9').as_uri(), 661 'file:///a/b' + quote_from_bytes(os.fsencode('\xe9'))) 662 663 def test_match(self): 664 P = self.cls 665 self.assertFalse(P('A.py').match('a.PY')) 666 667 def test_is_absolute(self): 668 P = self.cls 669 self.assertFalse(P().is_absolute()) 670 self.assertFalse(P('a').is_absolute()) 671 self.assertFalse(P('a/b/').is_absolute()) 672 self.assertTrue(P('/').is_absolute()) 673 self.assertTrue(P('/a').is_absolute()) 674 self.assertTrue(P('/a/b/').is_absolute()) 675 self.assertTrue(P('//a').is_absolute()) 676 self.assertTrue(P('//a/b').is_absolute()) 677 678 def test_is_reserved(self): 679 P = self.cls 680 self.assertIs(False, P('').is_reserved()) 681 self.assertIs(False, P('/').is_reserved()) 682 self.assertIs(False, P('/foo/bar').is_reserved()) 683 self.assertIs(False, P('/dev/con/PRN/NUL').is_reserved()) 684 685 def test_join(self): 686 P = self.cls 687 p = P('//a') 688 pp = p.joinpath('b') 689 self.assertEqual(pp, P('//a/b')) 690 pp = P('/a').joinpath('//c') 691 self.assertEqual(pp, P('//c')) 692 pp = P('//a').joinpath('/c') 693 self.assertEqual(pp, P('/c')) 694 695 def test_div(self): 696 # Basically the same as joinpath() 697 P = self.cls 698 p = P('//a') 699 pp = p / 'b' 700 self.assertEqual(pp, P('//a/b')) 701 pp = P('/a') / '//c' 702 self.assertEqual(pp, P('//c')) 703 pp = P('//a') / '/c' 704 self.assertEqual(pp, P('/c')) 705 706 707 class PureWindowsPathTest(_BasePurePathTest, unittest.TestCase): 708 cls = pathlib.PureWindowsPath 709 710 equivalences = _BasePurePathTest.equivalences.copy() 711 equivalences.update({ 712 'c:a': [ ('c:', 'a'), ('c:', 'a/'), ('/', 'c:', 'a') ], 713 'c:/a': [ 714 ('c:/', 'a'), ('c:', '/', 'a'), ('c:', '/a'), 715 ('/z', 'c:/', 'a'), ('//x/y', 'c:/', 'a'), 716 ], 717 '//a/b/': [ ('//a/b',) ], 718 '//a/b/c': [ 719 ('//a/b', 'c'), ('//a/b/', 'c'), 720 ], 721 }) 722 723 def test_str(self): 724 p = self.cls('a/b/c') 725 self.assertEqual(str(p), 'a\\b\\c') 726 p = self.cls('c:/a/b/c') 727 self.assertEqual(str(p), 'c:\\a\\b\\c') 728 p = self.cls('//a/b') 729 self.assertEqual(str(p), '\\\\a\\b\\') 730 p = self.cls('//a/b/c') 731 self.assertEqual(str(p), '\\\\a\\b\\c') 732 p = self.cls('//a/b/c/d') 733 self.assertEqual(str(p), '\\\\a\\b\\c\\d') 734 735 def test_str_subclass(self): 736 self._check_str_subclass('c:') 737 self._check_str_subclass('c:a') 738 self._check_str_subclass('c:a\\b.txt') 739 self._check_str_subclass('c:\\') 740 self._check_str_subclass('c:\\a') 741 self._check_str_subclass('c:\\a\\b.txt') 742 self._check_str_subclass('\\\\some\\share') 743 self._check_str_subclass('\\\\some\\share\\a') 744 self._check_str_subclass('\\\\some\\share\\a\\b.txt') 745 746 def test_eq(self): 747 P = self.cls 748 self.assertEqual(P('c:a/b'), P('c:a/b')) 749 self.assertEqual(P('c:a/b'), P('c:', 'a', 'b')) 750 self.assertNotEqual(P('c:a/b'), P('d:a/b')) 751 self.assertNotEqual(P('c:a/b'), P('c:/a/b')) 752 self.assertNotEqual(P('/a/b'), P('c:/a/b')) 753 # Case-insensitivity 754 self.assertEqual(P('a/B'), P('A/b')) 755 self.assertEqual(P('C:a/B'), P('c:A/b')) 756 self.assertEqual(P('//Some/SHARE/a/B'), P('//somE/share/A/b')) 757 758 def test_as_uri(self): 759 P = self.cls 760 with self.assertRaises(ValueError): 761 P('/a/b').as_uri() 762 with self.assertRaises(ValueError): 763 P('c:a/b').as_uri() 764 self.assertEqual(P('c:/').as_uri(), 'file:///c:/') 765 self.assertEqual(P('c:/a/b.c').as_uri(), 'file:///c:/a/b.c') 766 self.assertEqual(P('c:/a/b%#c').as_uri(), 'file:///c:/a/b%25%23c') 767 self.assertEqual(P('c:/a/b\xe9').as_uri(), 'file:///c:/a/b%C3%A9') 768 self.assertEqual(P('//some/share/').as_uri(), 'file://some/share/') 769 self.assertEqual(P('//some/share/a/b.c').as_uri(), 770 'file://some/share/a/b.c') 771 self.assertEqual(P('//some/share/a/b%#c\xe9').as_uri(), 772 'file://some/share/a/b%25%23c%C3%A9') 773 774 def test_match_common(self): 775 P = self.cls 776 # Absolute patterns 777 self.assertTrue(P('c:/b.py').match('/*.py')) 778 self.assertTrue(P('c:/b.py').match('c:*.py')) 779 self.assertTrue(P('c:/b.py').match('c:/*.py')) 780 self.assertFalse(P('d:/b.py').match('c:/*.py')) # wrong drive 781 self.assertFalse(P('b.py').match('/*.py')) 782 self.assertFalse(P('b.py').match('c:*.py')) 783 self.assertFalse(P('b.py').match('c:/*.py')) 784 self.assertFalse(P('c:b.py').match('/*.py')) 785 self.assertFalse(P('c:b.py').match('c:/*.py')) 786 self.assertFalse(P('/b.py').match('c:*.py')) 787 self.assertFalse(P('/b.py').match('c:/*.py')) 788 # UNC patterns 789 self.assertTrue(P('//some/share/a.py').match('/*.py')) 790 self.assertTrue(P('//some/share/a.py').match('//some/share/*.py')) 791 self.assertFalse(P('//other/share/a.py').match('//some/share/*.py')) 792 self.assertFalse(P('//some/share/a/b.py').match('//some/share/*.py')) 793 # Case-insensitivity 794 self.assertTrue(P('B.py').match('b.PY')) 795 self.assertTrue(P('c:/a/B.Py').match('C:/A/*.pY')) 796 self.assertTrue(P('//Some/Share/B.Py').match('//somE/sharE/*.pY')) 797 798 def test_ordering_common(self): 799 # Case-insensitivity 800 def assertOrderedEqual(a, b): 801 self.assertLessEqual(a, b) 802 self.assertGreaterEqual(b, a) 803 P = self.cls 804 p = P('c:A/b') 805 q = P('C:a/B') 806 assertOrderedEqual(p, q) 807 self.assertFalse(p < q) 808 self.assertFalse(p > q) 809 p = P('//some/Share/A/b') 810 q = P('//Some/SHARE/a/B') 811 assertOrderedEqual(p, q) 812 self.assertFalse(p < q) 813 self.assertFalse(p > q) 814 815 def test_parts(self): 816 P = self.cls 817 p = P('c:a/b') 818 parts = p.parts 819 self.assertEqual(parts, ('c:', 'a', 'b')) 820 p = P('c:/a/b') 821 parts = p.parts 822 self.assertEqual(parts, ('c:\\', 'a', 'b')) 823 p = P('//a/b/c/d') 824 parts = p.parts 825 self.assertEqual(parts, ('\\\\a\\b\\', 'c', 'd')) 826 827 def test_parent(self): 828 # Anchored 829 P = self.cls 830 p = P('z:a/b/c') 831 self.assertEqual(p.parent, P('z:a/b')) 832 self.assertEqual(p.parent.parent, P('z:a')) 833 self.assertEqual(p.parent.parent.parent, P('z:')) 834 self.assertEqual(p.parent.parent.parent.parent, P('z:')) 835 p = P('z:/a/b/c') 836 self.assertEqual(p.parent, P('z:/a/b')) 837 self.assertEqual(p.parent.parent, P('z:/a')) 838 self.assertEqual(p.parent.parent.parent, P('z:/')) 839 self.assertEqual(p.parent.parent.parent.parent, P('z:/')) 840 p = P('//a/b/c/d') 841 self.assertEqual(p.parent, P('//a/b/c')) 842 self.assertEqual(p.parent.parent, P('//a/b')) 843 self.assertEqual(p.parent.parent.parent, P('//a/b')) 844 845 def test_parents(self): 846 # Anchored 847 P = self.cls 848 p = P('z:a/b/') 849 par = p.parents 850 self.assertEqual(len(par), 2) 851 self.assertEqual(par[0], P('z:a')) 852 self.assertEqual(par[1], P('z:')) 853 self.assertEqual(list(par), [P('z:a'), P('z:')]) 854 with self.assertRaises(IndexError): 855 par[2] 856 p = P('z:/a/b/') 857 par = p.parents 858 self.assertEqual(len(par), 2) 859 self.assertEqual(par[0], P('z:/a')) 860 self.assertEqual(par[1], P('z:/')) 861 self.assertEqual(list(par), [P('z:/a'), P('z:/')]) 862 with self.assertRaises(IndexError): 863 par[2] 864 p = P('//a/b/c/d') 865 par = p.parents 866 self.assertEqual(len(par), 2) 867 self.assertEqual(par[0], P('//a/b/c')) 868 self.assertEqual(par[1], P('//a/b')) 869 self.assertEqual(list(par), [P('//a/b/c'), P('//a/b')]) 870 with self.assertRaises(IndexError): 871 par[2] 872 873 def test_drive(self): 874 P = self.cls 875 self.assertEqual(P('c:').drive, 'c:') 876 self.assertEqual(P('c:a/b').drive, 'c:') 877 self.assertEqual(P('c:/').drive, 'c:') 878 self.assertEqual(P('c:/a/b/').drive, 'c:') 879 self.assertEqual(P('//a/b').drive, '\\\\a\\b') 880 self.assertEqual(P('//a/b/').drive, '\\\\a\\b') 881 self.assertEqual(P('//a/b/c/d').drive, '\\\\a\\b') 882 883 def test_root(self): 884 P = self.cls 885 self.assertEqual(P('c:').root, '') 886 self.assertEqual(P('c:a/b').root, '') 887 self.assertEqual(P('c:/').root, '\\') 888 self.assertEqual(P('c:/a/b/').root, '\\') 889 self.assertEqual(P('//a/b').root, '\\') 890 self.assertEqual(P('//a/b/').root, '\\') 891 self.assertEqual(P('//a/b/c/d').root, '\\') 892 893 def test_anchor(self): 894 P = self.cls 895 self.assertEqual(P('c:').anchor, 'c:') 896 self.assertEqual(P('c:a/b').anchor, 'c:') 897 self.assertEqual(P('c:/').anchor, 'c:\\') 898 self.assertEqual(P('c:/a/b/').anchor, 'c:\\') 899 self.assertEqual(P('//a/b').anchor, '\\\\a\\b\\') 900 self.assertEqual(P('//a/b/').anchor, '\\\\a\\b\\') 901 self.assertEqual(P('//a/b/c/d').anchor, '\\\\a\\b\\') 902 903 def test_name(self): 904 P = self.cls 905 self.assertEqual(P('c:').name, '') 906 self.assertEqual(P('c:/').name, '') 907 self.assertEqual(P('c:a/b').name, 'b') 908 self.assertEqual(P('c:/a/b').name, 'b') 909 self.assertEqual(P('c:a/b.py').name, 'b.py') 910 self.assertEqual(P('c:/a/b.py').name, 'b.py') 911 self.assertEqual(P('//My.py/Share.php').name, '') 912 self.assertEqual(P('//My.py/Share.php/a/b').name, 'b') 913 914 def test_suffix(self): 915 P = self.cls 916 self.assertEqual(P('c:').suffix, '') 917 self.assertEqual(P('c:/').suffix, '') 918 self.assertEqual(P('c:a/b').suffix, '') 919 self.assertEqual(P('c:/a/b').suffix, '') 920 self.assertEqual(P('c:a/b.py').suffix, '.py') 921 self.assertEqual(P('c:/a/b.py').suffix, '.py') 922 self.assertEqual(P('c:a/.hgrc').suffix, '') 923 self.assertEqual(P('c:/a/.hgrc').suffix, '') 924 self.assertEqual(P('c:a/.hg.rc').suffix, '.rc') 925 self.assertEqual(P('c:/a/.hg.rc').suffix, '.rc') 926 self.assertEqual(P('c:a/b.tar.gz').suffix, '.gz') 927 self.assertEqual(P('c:/a/b.tar.gz').suffix, '.gz') 928 self.assertEqual(P('c:a/Some name. Ending with a dot.').suffix, '') 929 self.assertEqual(P('c:/a/Some name. Ending with a dot.').suffix, '') 930 self.assertEqual(P('//My.py/Share.php').suffix, '') 931 self.assertEqual(P('//My.py/Share.php/a/b').suffix, '') 932 933 def test_suffixes(self): 934 P = self.cls 935 self.assertEqual(P('c:').suffixes, []) 936 self.assertEqual(P('c:/').suffixes, []) 937 self.assertEqual(P('c:a/b').suffixes, []) 938 self.assertEqual(P('c:/a/b').suffixes, []) 939 self.assertEqual(P('c:a/b.py').suffixes, ['.py']) 940 self.assertEqual(P('c:/a/b.py').suffixes, ['.py']) 941 self.assertEqual(P('c:a/.hgrc').suffixes, []) 942 self.assertEqual(P('c:/a/.hgrc').suffixes, []) 943 self.assertEqual(P('c:a/.hg.rc').suffixes, ['.rc']) 944 self.assertEqual(P('c:/a/.hg.rc').suffixes, ['.rc']) 945 self.assertEqual(P('c:a/b.tar.gz').suffixes, ['.tar', '.gz']) 946 self.assertEqual(P('c:/a/b.tar.gz').suffixes, ['.tar', '.gz']) 947 self.assertEqual(P('//My.py/Share.php').suffixes, []) 948 self.assertEqual(P('//My.py/Share.php/a/b').suffixes, []) 949 self.assertEqual(P('c:a/Some name. Ending with a dot.').suffixes, []) 950 self.assertEqual(P('c:/a/Some name. Ending with a dot.').suffixes, []) 951 952 def test_stem(self): 953 P = self.cls 954 self.assertEqual(P('c:').stem, '') 955 self.assertEqual(P('c:.').stem, '') 956 self.assertEqual(P('c:..').stem, '..') 957 self.assertEqual(P('c:/').stem, '') 958 self.assertEqual(P('c:a/b').stem, 'b') 959 self.assertEqual(P('c:a/b.py').stem, 'b') 960 self.assertEqual(P('c:a/.hgrc').stem, '.hgrc') 961 self.assertEqual(P('c:a/.hg.rc').stem, '.hg') 962 self.assertEqual(P('c:a/b.tar.gz').stem, 'b.tar') 963 self.assertEqual(P('c:a/Some name. Ending with a dot.').stem, 964 'Some name. Ending with a dot.') 965 966 def test_with_name(self): 967 P = self.cls 968 self.assertEqual(P('c:a/b').with_name('d.xml'), P('c:a/d.xml')) 969 self.assertEqual(P('c:/a/b').with_name('d.xml'), P('c:/a/d.xml')) 970 self.assertEqual(P('c:a/Dot ending.').with_name('d.xml'), P('c:a/d.xml')) 971 self.assertEqual(P('c:/a/Dot ending.').with_name('d.xml'), P('c:/a/d.xml')) 972 self.assertRaises(ValueError, P('c:').with_name, 'd.xml') 973 self.assertRaises(ValueError, P('c:/').with_name, 'd.xml') 974 self.assertRaises(ValueError, P('//My/Share').with_name, 'd.xml') 975 self.assertRaises(ValueError, P('c:a/b').with_name, 'd:') 976 self.assertRaises(ValueError, P('c:a/b').with_name, 'd:e') 977 self.assertRaises(ValueError, P('c:a/b').with_name, 'd:/e') 978 self.assertRaises(ValueError, P('c:a/b').with_name, '//My/Share') 979 980 def test_with_suffix(self): 981 P = self.cls 982 self.assertEqual(P('c:a/b').with_suffix('.gz'), P('c:a/b.gz')) 983 self.assertEqual(P('c:/a/b').with_suffix('.gz'), P('c:/a/b.gz')) 984 self.assertEqual(P('c:a/b.py').with_suffix('.gz'), P('c:a/b.gz')) 985 self.assertEqual(P('c:/a/b.py').with_suffix('.gz'), P('c:/a/b.gz')) 986 # Path doesn't have a "filename" component 987 self.assertRaises(ValueError, P('').with_suffix, '.gz') 988 self.assertRaises(ValueError, P('.').with_suffix, '.gz') 989 self.assertRaises(ValueError, P('/').with_suffix, '.gz') 990 self.assertRaises(ValueError, P('//My/Share').with_suffix, '.gz') 991 # Invalid suffix 992 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'gz') 993 self.assertRaises(ValueError, P('c:a/b').with_suffix, '/') 994 self.assertRaises(ValueError, P('c:a/b').with_suffix, '\\') 995 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c:') 996 self.assertRaises(ValueError, P('c:a/b').with_suffix, '/.gz') 997 self.assertRaises(ValueError, P('c:a/b').with_suffix, '\\.gz') 998 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c:.gz') 999 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c/d') 1000 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c\\d') 1001 self.assertRaises(ValueError, P('c:a/b').with_suffix, '.c/d') 1002 self.assertRaises(ValueError, P('c:a/b').with_suffix, '.c\\d') 1003 1004 def test_relative_to(self): 1005 P = self.cls 1006 p = P('C:Foo/Bar') 1007 self.assertEqual(p.relative_to(P('c:')), P('Foo/Bar')) 1008 self.assertEqual(p.relative_to('c:'), P('Foo/Bar')) 1009 self.assertEqual(p.relative_to(P('c:foO')), P('Bar')) 1010 self.assertEqual(p.relative_to('c:foO'), P('Bar')) 1011 self.assertEqual(p.relative_to('c:foO/'), P('Bar')) 1012 self.assertEqual(p.relative_to(P('c:foO/baR')), P()) 1013 self.assertEqual(p.relative_to('c:foO/baR'), P()) 1014 # Unrelated paths 1015 self.assertRaises(ValueError, p.relative_to, P()) 1016 self.assertRaises(ValueError, p.relative_to, '') 1017 self.assertRaises(ValueError, p.relative_to, P('d:')) 1018 self.assertRaises(ValueError, p.relative_to, P('/')) 1019 self.assertRaises(ValueError, p.relative_to, P('Foo')) 1020 self.assertRaises(ValueError, p.relative_to, P('/Foo')) 1021 self.assertRaises(ValueError, p.relative_to, P('C:/Foo')) 1022 self.assertRaises(ValueError, p.relative_to, P('C:Foo/Bar/Baz')) 1023 self.assertRaises(ValueError, p.relative_to, P('C:Foo/Baz')) 1024 p = P('C:/Foo/Bar') 1025 self.assertEqual(p.relative_to(P('c:')), P('/Foo/Bar')) 1026 self.assertEqual(p.relative_to('c:'), P('/Foo/Bar')) 1027 self.assertEqual(str(p.relative_to(P('c:'))), '\\Foo\\Bar') 1028 self.assertEqual(str(p.relative_to('c:')), '\\Foo\\Bar') 1029 self.assertEqual(p.relative_to(P('c:/')), P('Foo/Bar')) 1030 self.assertEqual(p.relative_to('c:/'), P('Foo/Bar')) 1031 self.assertEqual(p.relative_to(P('c:/foO')), P('Bar')) 1032 self.assertEqual(p.relative_to('c:/foO'), P('Bar')) 1033 self.assertEqual(p.relative_to('c:/foO/'), P('Bar')) 1034 self.assertEqual(p.relative_to(P('c:/foO/baR')), P()) 1035 self.assertEqual(p.relative_to('c:/foO/baR'), P()) 1036 # Unrelated paths 1037 self.assertRaises(ValueError, p.relative_to, P('C:/Baz')) 1038 self.assertRaises(ValueError, p.relative_to, P('C:/Foo/Bar/Baz')) 1039 self.assertRaises(ValueError, p.relative_to, P('C:/Foo/Baz')) 1040 self.assertRaises(ValueError, p.relative_to, P('C:Foo')) 1041 self.assertRaises(ValueError, p.relative_to, P('d:')) 1042 self.assertRaises(ValueError, p.relative_to, P('d:/')) 1043 self.assertRaises(ValueError, p.relative_to, P('/')) 1044 self.assertRaises(ValueError, p.relative_to, P('/Foo')) 1045 self.assertRaises(ValueError, p.relative_to, P('//C/Foo')) 1046 # UNC paths 1047 p = P('//Server/Share/Foo/Bar') 1048 self.assertEqual(p.relative_to(P('//sErver/sHare')), P('Foo/Bar')) 1049 self.assertEqual(p.relative_to('//sErver/sHare'), P('Foo/Bar')) 1050 self.assertEqual(p.relative_to('//sErver/sHare/'), P('Foo/Bar')) 1051 self.assertEqual(p.relative_to(P('//sErver/sHare/Foo')), P('Bar')) 1052 self.assertEqual(p.relative_to('//sErver/sHare/Foo'), P('Bar')) 1053 self.assertEqual(p.relative_to('//sErver/sHare/Foo/'), P('Bar')) 1054 self.assertEqual(p.relative_to(P('//sErver/sHare/Foo/Bar')), P()) 1055 self.assertEqual(p.relative_to('//sErver/sHare/Foo/Bar'), P()) 1056 # Unrelated paths 1057 self.assertRaises(ValueError, p.relative_to, P('/Server/Share/Foo')) 1058 self.assertRaises(ValueError, p.relative_to, P('c:/Server/Share/Foo')) 1059 self.assertRaises(ValueError, p.relative_to, P('//z/Share/Foo')) 1060 self.assertRaises(ValueError, p.relative_to, P('//Server/z/Foo')) 1061 1062 def test_is_absolute(self): 1063 P = self.cls 1064 # Under NT, only paths with both a drive and a root are absolute 1065 self.assertFalse(P().is_absolute()) 1066 self.assertFalse(P('a').is_absolute()) 1067 self.assertFalse(P('a/b/').is_absolute()) 1068 self.assertFalse(P('/').is_absolute()) 1069 self.assertFalse(P('/a').is_absolute()) 1070 self.assertFalse(P('/a/b/').is_absolute()) 1071 self.assertFalse(P('c:').is_absolute()) 1072 self.assertFalse(P('c:a').is_absolute()) 1073 self.assertFalse(P('c:a/b/').is_absolute()) 1074 self.assertTrue(P('c:/').is_absolute()) 1075 self.assertTrue(P('c:/a').is_absolute()) 1076 self.assertTrue(P('c:/a/b/').is_absolute()) 1077 # UNC paths are absolute by definition 1078 self.assertTrue(P('//a/b').is_absolute()) 1079 self.assertTrue(P('//a/b/').is_absolute()) 1080 self.assertTrue(P('//a/b/c').is_absolute()) 1081 self.assertTrue(P('//a/b/c/d').is_absolute()) 1082 1083 def test_join(self): 1084 P = self.cls 1085 p = P('C:/a/b') 1086 pp = p.joinpath('x/y') 1087 self.assertEqual(pp, P('C:/a/b/x/y')) 1088 pp = p.joinpath('/x/y') 1089 self.assertEqual(pp, P('C:/x/y')) 1090 # Joining with a different drive => the first path is ignored, even 1091 # if the second path is relative. 1092 pp = p.joinpath('D:x/y') 1093 self.assertEqual(pp, P('D:x/y')) 1094 pp = p.joinpath('D:/x/y') 1095 self.assertEqual(pp, P('D:/x/y')) 1096 pp = p.joinpath('//host/share/x/y') 1097 self.assertEqual(pp, P('//host/share/x/y')) 1098 # Joining with the same drive => the first path is appended to if 1099 # the second path is relative. 1100 pp = p.joinpath('c:x/y') 1101 self.assertEqual(pp, P('C:/a/b/x/y')) 1102 pp = p.joinpath('c:/x/y') 1103 self.assertEqual(pp, P('C:/x/y')) 1104 1105 def test_div(self): 1106 # Basically the same as joinpath() 1107 P = self.cls 1108 p = P('C:/a/b') 1109 self.assertEqual(p / 'x/y', P('C:/a/b/x/y')) 1110 self.assertEqual(p / 'x' / 'y', P('C:/a/b/x/y')) 1111 self.assertEqual(p / '/x/y', P('C:/x/y')) 1112 self.assertEqual(p / '/x' / 'y', P('C:/x/y')) 1113 # Joining with a different drive => the first path is ignored, even 1114 # if the second path is relative. 1115 self.assertEqual(p / 'D:x/y', P('D:x/y')) 1116 self.assertEqual(p / 'D:' / 'x/y', P('D:x/y')) 1117 self.assertEqual(p / 'D:/x/y', P('D:/x/y')) 1118 self.assertEqual(p / 'D:' / '/x/y', P('D:/x/y')) 1119 self.assertEqual(p / '//host/share/x/y', P('//host/share/x/y')) 1120 # Joining with the same drive => the first path is appended to if 1121 # the second path is relative. 1122 self.assertEqual(p / 'c:x/y', P('C:/a/b/x/y')) 1123 self.assertEqual(p / 'c:/x/y', P('C:/x/y')) 1124 1125 def test_is_reserved(self): 1126 P = self.cls 1127 self.assertIs(False, P('').is_reserved()) 1128 self.assertIs(False, P('/').is_reserved()) 1129 self.assertIs(False, P('/foo/bar').is_reserved()) 1130 self.assertIs(True, P('con').is_reserved()) 1131 self.assertIs(True, P('NUL').is_reserved()) 1132 self.assertIs(True, P('NUL.txt').is_reserved()) 1133 self.assertIs(True, P('com1').is_reserved()) 1134 self.assertIs(True, P('com9.bar').is_reserved()) 1135 self.assertIs(False, P('bar.com9').is_reserved()) 1136 self.assertIs(True, P('lpt1').is_reserved()) 1137 self.assertIs(True, P('lpt9.bar').is_reserved()) 1138 self.assertIs(False, P('bar.lpt9').is_reserved()) 1139 # Only the last component matters 1140 self.assertIs(False, P('c:/NUL/con/baz').is_reserved()) 1141 # UNC paths are never reserved 1142 self.assertIs(False, P('//my/share/nul/con/aux').is_reserved()) 1143 1144 class PurePathTest(_BasePurePathTest, unittest.TestCase): 1145 cls = pathlib.PurePath 1146 1147 def test_concrete_class(self): 1148 p = self.cls('a') 1149 self.assertIs(type(p), 1150 pathlib.PureWindowsPath if os.name == 'nt' else pathlib.PurePosixPath) 1151 1152 def test_different_flavours_unequal(self): 1153 p = pathlib.PurePosixPath('a') 1154 q = pathlib.PureWindowsPath('a') 1155 self.assertNotEqual(p, q) 1156 1157 def test_different_flavours_unordered(self): 1158 p = pathlib.PurePosixPath('a') 1159 q = pathlib.PureWindowsPath('a') 1160 with self.assertRaises(TypeError): 1161 p < q 1162 with self.assertRaises(TypeError): 1163 p <= q 1164 with self.assertRaises(TypeError): 1165 p > q 1166 with self.assertRaises(TypeError): 1167 p >= q 1168 1169 1170 # 1171 # Tests for the concrete classes 1172 # 1173 1174 # Make sure any symbolic links in the base test path are resolved 1175 BASE = os.path.realpath(TESTFN) 1176 join = lambda *x: os.path.join(BASE, *x) 1177 rel_join = lambda *x: os.path.join(TESTFN, *x) 1178 1179 def symlink_skip_reason(): 1180 if not pathlib.supports_symlinks: 1181 return "no system support for symlinks" 1182 try: 1183 os.symlink(__file__, BASE) 1184 except OSError as e: 1185 return str(e) 1186 else: 1187 support.unlink(BASE) 1188 return None 1189 1190 symlink_skip_reason = symlink_skip_reason() 1191 1192 only_nt = unittest.skipIf(os.name != 'nt', 1193 'test requires a Windows-compatible system') 1194 only_posix = unittest.skipIf(os.name == 'nt', 1195 'test requires a POSIX-compatible system') 1196 with_symlinks = unittest.skipIf(symlink_skip_reason, symlink_skip_reason) 1197 1198 1199 @only_posix 1200 class PosixPathAsPureTest(PurePosixPathTest): 1201 cls = pathlib.PosixPath 1202 1203 @only_nt 1204 class WindowsPathAsPureTest(PureWindowsPathTest): 1205 cls = pathlib.WindowsPath 1206 1207 def test_owner(self): 1208 P = self.cls 1209 with self.assertRaises(NotImplementedError): 1210 P('c:/').owner() 1211 1212 def test_group(self): 1213 P = self.cls 1214 with self.assertRaises(NotImplementedError): 1215 P('c:/').group() 1216 1217 1218 class _BasePathTest(object): 1219 """Tests for the FS-accessing functionalities of the Path classes.""" 1220 1221 # (BASE) 1222 # | 1223 # |-- brokenLink -> non-existing 1224 # |-- dirA 1225 # | `-- linkC -> ../dirB 1226 # |-- dirB 1227 # | |-- fileB 1228 # | `-- linkD -> ../dirB 1229 # |-- dirC 1230 # | |-- dirD 1231 # | | `-- fileD 1232 # | `-- fileC 1233 # |-- dirE # No permissions 1234 # |-- fileA 1235 # |-- linkA -> fileA 1236 # `-- linkB -> dirB 1237 # 1238 1239 def setUp(self): 1240 def cleanup(): 1241 os.chmod(join('dirE'), 0o777) 1242 support.rmtree(BASE) 1243 self.addCleanup(cleanup) 1244 os.mkdir(BASE) 1245 os.mkdir(join('dirA')) 1246 os.mkdir(join('dirB')) 1247 os.mkdir(join('dirC')) 1248 os.mkdir(join('dirC', 'dirD')) 1249 os.mkdir(join('dirE')) 1250 with open(join('fileA'), 'wb') as f: 1251 f.write(b"this is file A\n") 1252 with open(join('dirB', 'fileB'), 'wb') as f: 1253 f.write(b"this is file B\n") 1254 with open(join('dirC', 'fileC'), 'wb') as f: 1255 f.write(b"this is file C\n") 1256 with open(join('dirC', 'dirD', 'fileD'), 'wb') as f: 1257 f.write(b"this is file D\n") 1258 os.chmod(join('dirE'), 0) 1259 if not symlink_skip_reason: 1260 # Relative symlinks 1261 os.symlink('fileA', join('linkA')) 1262 os.symlink('non-existing', join('brokenLink')) 1263 self.dirlink('dirB', join('linkB')) 1264 self.dirlink(os.path.join('..', 'dirB'), join('dirA', 'linkC')) 1265 # This one goes upwards, creating a loop 1266 self.dirlink(os.path.join('..', 'dirB'), join('dirB', 'linkD')) 1267 1268 if os.name == 'nt': 1269 # Workaround for http://bugs.python.org/issue13772 1270 def dirlink(self, src, dest): 1271 os.symlink(src, dest, target_is_directory=True) 1272 else: 1273 def dirlink(self, src, dest): 1274 os.symlink(src, dest) 1275 1276 def assertSame(self, path_a, path_b): 1277 self.assertTrue(os.path.samefile(str(path_a), str(path_b)), 1278 "%r and %r don't point to the same file" % 1279 (path_a, path_b)) 1280 1281 def assertFileNotFound(self, func, *args, **kwargs): 1282 with self.assertRaises(FileNotFoundError) as cm: 1283 func(*args, **kwargs) 1284 self.assertEqual(cm.exception.errno, errno.ENOENT) 1285 1286 def _test_cwd(self, p): 1287 q = self.cls(os.getcwd()) 1288 self.assertEqual(p, q) 1289 self.assertEqual(str(p), str(q)) 1290 self.assertIs(type(p), type(q)) 1291 self.assertTrue(p.is_absolute()) 1292 1293 def test_cwd(self): 1294 p = self.cls.cwd() 1295 self._test_cwd(p) 1296 1297 def _test_home(self, p): 1298 q = self.cls(os.path.expanduser('~')) 1299 self.assertEqual(p, q) 1300 self.assertEqual(str(p), str(q)) 1301 self.assertIs(type(p), type(q)) 1302 self.assertTrue(p.is_absolute()) 1303 1304 def test_home(self): 1305 p = self.cls.home() 1306 self._test_home(p) 1307 1308 def test_samefile(self): 1309 fileA_path = os.path.join(BASE, 'fileA') 1310 fileB_path = os.path.join(BASE, 'dirB', 'fileB') 1311 p = self.cls(fileA_path) 1312 pp = self.cls(fileA_path) 1313 q = self.cls(fileB_path) 1314 self.assertTrue(p.samefile(fileA_path)) 1315 self.assertTrue(p.samefile(pp)) 1316 self.assertFalse(p.samefile(fileB_path)) 1317 self.assertFalse(p.samefile(q)) 1318 # Test the non-existent file case 1319 non_existent = os.path.join(BASE, 'foo') 1320 r = self.cls(non_existent) 1321 self.assertRaises(FileNotFoundError, p.samefile, r) 1322 self.assertRaises(FileNotFoundError, p.samefile, non_existent) 1323 self.assertRaises(FileNotFoundError, r.samefile, p) 1324 self.assertRaises(FileNotFoundError, r.samefile, non_existent) 1325 self.assertRaises(FileNotFoundError, r.samefile, r) 1326 self.assertRaises(FileNotFoundError, r.samefile, non_existent) 1327 1328 def test_empty_path(self): 1329 # The empty path points to '.' 1330 p = self.cls('') 1331 self.assertEqual(p.stat(), os.stat('.')) 1332 1333 def test_expanduser_common(self): 1334 P = self.cls 1335 p = P('~') 1336 self.assertEqual(p.expanduser(), P(os.path.expanduser('~'))) 1337 p = P('foo') 1338 self.assertEqual(p.expanduser(), p) 1339 p = P('/~') 1340 self.assertEqual(p.expanduser(), p) 1341 p = P('../~') 1342 self.assertEqual(p.expanduser(), p) 1343 p = P(P('').absolute().anchor) / '~' 1344 self.assertEqual(p.expanduser(), p) 1345 1346 def test_exists(self): 1347 P = self.cls 1348 p = P(BASE) 1349 self.assertIs(True, p.exists()) 1350 self.assertIs(True, (p / 'dirA').exists()) 1351 self.assertIs(True, (p / 'fileA').exists()) 1352 self.assertIs(False, (p / 'fileA' / 'bah').exists()) 1353 if not symlink_skip_reason: 1354 self.assertIs(True, (p / 'linkA').exists()) 1355 self.assertIs(True, (p / 'linkB').exists()) 1356 self.assertIs(True, (p / 'linkB' / 'fileB').exists()) 1357 self.assertIs(False, (p / 'linkA' / 'bah').exists()) 1358 self.assertIs(False, (p / 'foo').exists()) 1359 self.assertIs(False, P('/xyzzy').exists()) 1360 1361 def test_open_common(self): 1362 p = self.cls(BASE) 1363 with (p / 'fileA').open('r') as f: 1364 self.assertIsInstance(f, io.TextIOBase) 1365 self.assertEqual(f.read(), "this is file A\n") 1366 with (p / 'fileA').open('rb') as f: 1367 self.assertIsInstance(f, io.BufferedIOBase) 1368 self.assertEqual(f.read().strip(), b"this is file A") 1369 with (p / 'fileA').open('rb', buffering=0) as f: 1370 self.assertIsInstance(f, io.RawIOBase) 1371 self.assertEqual(f.read().strip(), b"this is file A") 1372 1373 def test_read_write_bytes(self): 1374 p = self.cls(BASE) 1375 (p / 'fileA').write_bytes(b'abcdefg') 1376 self.assertEqual((p / 'fileA').read_bytes(), b'abcdefg') 1377 # check that trying to write str does not truncate the file 1378 self.assertRaises(TypeError, (p / 'fileA').write_bytes, 'somestr') 1379 self.assertEqual((p / 'fileA').read_bytes(), b'abcdefg') 1380 1381 def test_read_write_text(self): 1382 p = self.cls(BASE) 1383 (p / 'fileA').write_text('bcdefg', encoding='latin-1') 1384 self.assertEqual((p / 'fileA').read_text( 1385 encoding='utf-8', errors='ignore'), 'bcdefg') 1386 # check that trying to write bytes does not truncate the file 1387 self.assertRaises(TypeError, (p / 'fileA').write_text, b'somebytes') 1388 self.assertEqual((p / 'fileA').read_text(encoding='latin-1'), 'bcdefg') 1389 1390 def test_iterdir(self): 1391 P = self.cls 1392 p = P(BASE) 1393 it = p.iterdir() 1394 paths = set(it) 1395 expected = ['dirA', 'dirB', 'dirC', 'dirE', 'fileA'] 1396 if not symlink_skip_reason: 1397 expected += ['linkA', 'linkB', 'brokenLink'] 1398 self.assertEqual(paths, { P(BASE, q) for q in expected }) 1399 1400 @with_symlinks 1401 def test_iterdir_symlink(self): 1402 # __iter__ on a symlink to a directory 1403 P = self.cls 1404 p = P(BASE, 'linkB') 1405 paths = set(p.iterdir()) 1406 expected = { P(BASE, 'linkB', q) for q in ['fileB', 'linkD'] } 1407 self.assertEqual(paths, expected) 1408 1409 def test_iterdir_nodir(self): 1410 # __iter__ on something that is not a directory 1411 p = self.cls(BASE, 'fileA') 1412 with self.assertRaises(OSError) as cm: 1413 next(p.iterdir()) 1414 # ENOENT or EINVAL under Windows, ENOTDIR otherwise 1415 # (see issue #12802) 1416 self.assertIn(cm.exception.errno, (errno.ENOTDIR, 1417 errno.ENOENT, errno.EINVAL)) 1418 1419 def test_glob_common(self): 1420 def _check(glob, expected): 1421 self.assertEqual(set(glob), { P(BASE, q) for q in expected }) 1422 P = self.cls 1423 p = P(BASE) 1424 it = p.glob("fileA") 1425 self.assertIsInstance(it, collections.Iterator) 1426 _check(it, ["fileA"]) 1427 _check(p.glob("fileB"), []) 1428 _check(p.glob("dir*/file*"), ["dirB/fileB", "dirC/fileC"]) 1429 if symlink_skip_reason: 1430 _check(p.glob("*A"), ['dirA', 'fileA']) 1431 else: 1432 _check(p.glob("*A"), ['dirA', 'fileA', 'linkA']) 1433 if symlink_skip_reason: 1434 _check(p.glob("*B/*"), ['dirB/fileB']) 1435 else: 1436 _check(p.glob("*B/*"), ['dirB/fileB', 'dirB/linkD', 1437 'linkB/fileB', 'linkB/linkD']) 1438 if symlink_skip_reason: 1439 _check(p.glob("*/fileB"), ['dirB/fileB']) 1440 else: 1441 _check(p.glob("*/fileB"), ['dirB/fileB', 'linkB/fileB']) 1442 1443 def test_rglob_common(self): 1444 def _check(glob, expected): 1445 self.assertEqual(set(glob), { P(BASE, q) for q in expected }) 1446 P = self.cls 1447 p = P(BASE) 1448 it = p.rglob("fileA") 1449 self.assertIsInstance(it, collections.Iterator) 1450 _check(it, ["fileA"]) 1451 _check(p.rglob("fileB"), ["dirB/fileB"]) 1452 _check(p.rglob("*/fileA"), []) 1453 if symlink_skip_reason: 1454 _check(p.rglob("*/fileB"), ["dirB/fileB"]) 1455 else: 1456 _check(p.rglob("*/fileB"), ["dirB/fileB", "dirB/linkD/fileB", 1457 "linkB/fileB", "dirA/linkC/fileB"]) 1458 _check(p.rglob("file*"), ["fileA", "dirB/fileB", 1459 "dirC/fileC", "dirC/dirD/fileD"]) 1460 p = P(BASE, "dirC") 1461 _check(p.rglob("file*"), ["dirC/fileC", "dirC/dirD/fileD"]) 1462 _check(p.rglob("*/*"), ["dirC/dirD/fileD"]) 1463 1464 @with_symlinks 1465 def test_rglob_symlink_loop(self): 1466 # Don't get fooled by symlink loops (Issue #26012) 1467 P = self.cls 1468 p = P(BASE) 1469 given = set(p.rglob('*')) 1470 expect = {'brokenLink', 1471 'dirA', 'dirA/linkC', 1472 'dirB', 'dirB/fileB', 'dirB/linkD', 1473 'dirC', 'dirC/dirD', 'dirC/dirD/fileD', 'dirC/fileC', 1474 'dirE', 1475 'fileA', 1476 'linkA', 1477 'linkB', 1478 } 1479 self.assertEqual(given, {p / x for x in expect}) 1480 1481 def test_glob_dotdot(self): 1482 # ".." is not special in globs 1483 P = self.cls 1484 p = P(BASE) 1485 self.assertEqual(set(p.glob("..")), { P(BASE, "..") }) 1486 self.assertEqual(set(p.glob("dirA/../file*")), { P(BASE, "dirA/../fileA") }) 1487 self.assertEqual(set(p.glob("../xyzzy")), set()) 1488 1489 1490 def _check_resolve(self, p, expected, strict=True): 1491 q = p.resolve(strict) 1492 self.assertEqual(q, expected) 1493 1494 # this can be used to check both relative and absolute resolutions 1495 _check_resolve_relative = _check_resolve_absolute = _check_resolve 1496 1497 @with_symlinks 1498 def test_resolve_common(self): 1499 P = self.cls 1500 p = P(BASE, 'foo') 1501 with self.assertRaises(OSError) as cm: 1502 p.resolve(strict=True) 1503 self.assertEqual(cm.exception.errno, errno.ENOENT) 1504 # Non-strict 1505 self.assertEqual(str(p.resolve(strict=False)), 1506 os.path.join(BASE, 'foo')) 1507 p = P(BASE, 'foo', 'in', 'spam') 1508 self.assertEqual(str(p.resolve(strict=False)), 1509 os.path.join(BASE, 'foo')) 1510 p = P(BASE, '..', 'foo', 'in', 'spam') 1511 self.assertEqual(str(p.resolve(strict=False)), 1512 os.path.abspath(os.path.join('foo'))) 1513 # These are all relative symlinks 1514 p = P(BASE, 'dirB', 'fileB') 1515 self._check_resolve_relative(p, p) 1516 p = P(BASE, 'linkA') 1517 self._check_resolve_relative(p, P(BASE, 'fileA')) 1518 p = P(BASE, 'dirA', 'linkC', 'fileB') 1519 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB')) 1520 p = P(BASE, 'dirB', 'linkD', 'fileB') 1521 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB')) 1522 # Non-strict 1523 p = P(BASE, 'dirA', 'linkC', 'fileB', 'foo', 'in', 'spam') 1524 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB', 'foo'), False) 1525 p = P(BASE, 'dirA', 'linkC', '..', 'foo', 'in', 'spam') 1526 if os.name == 'nt': 1527 # In Windows, if linkY points to dirB, 'dirA\linkY\..' 1528 # resolves to 'dirA' without resolving linkY first. 1529 self._check_resolve_relative(p, P(BASE, 'dirA', 'foo'), False) 1530 else: 1531 # In Posix, if linkY points to dirB, 'dirA/linkY/..' 1532 # resolves to 'dirB/..' first before resolving to parent of dirB. 1533 self._check_resolve_relative(p, P(BASE, 'foo'), False) 1534 # Now create absolute symlinks 1535 d = tempfile.mkdtemp(suffix='-dirD') 1536 self.addCleanup(support.rmtree, d) 1537 os.symlink(os.path.join(d), join('dirA', 'linkX')) 1538 os.symlink(join('dirB'), os.path.join(d, 'linkY')) 1539 p = P(BASE, 'dirA', 'linkX', 'linkY', 'fileB') 1540 self._check_resolve_absolute(p, P(BASE, 'dirB', 'fileB')) 1541 # Non-strict 1542 p = P(BASE, 'dirA', 'linkX', 'linkY', 'foo', 'in', 'spam') 1543 self._check_resolve_relative(p, P(BASE, 'dirB', 'foo'), False) 1544 p = P(BASE, 'dirA', 'linkX', 'linkY', '..', 'foo', 'in', 'spam') 1545 if os.name == 'nt': 1546 # In Windows, if linkY points to dirB, 'dirA\linkY\..' 1547 # resolves to 'dirA' without resolving linkY first. 1548 self._check_resolve_relative(p, P(d, 'foo'), False) 1549 else: 1550 # In Posix, if linkY points to dirB, 'dirA/linkY/..' 1551 # resolves to 'dirB/..' first before resolving to parent of dirB. 1552 self._check_resolve_relative(p, P(BASE, 'foo'), False) 1553 1554 @with_symlinks 1555 def test_resolve_dot(self): 1556 # See https://bitbucket.org/pitrou/pathlib/issue/9/pathresolve-fails-on-complex-symlinks 1557 p = self.cls(BASE) 1558 self.dirlink('.', join('0')) 1559 self.dirlink(os.path.join('0', '0'), join('1')) 1560 self.dirlink(os.path.join('1', '1'), join('2')) 1561 q = p / '2' 1562 self.assertEqual(q.resolve(strict=True), p) 1563 r = q / '3' / '4' 1564 self.assertRaises(FileNotFoundError, r.resolve, strict=True) 1565 # Non-strict 1566 self.assertEqual(r.resolve(strict=False), p / '3') 1567 1568 def test_with(self): 1569 p = self.cls(BASE) 1570 it = p.iterdir() 1571 it2 = p.iterdir() 1572 next(it2) 1573 with p: 1574 pass 1575 # I/O operation on closed path 1576 self.assertRaises(ValueError, next, it) 1577 self.assertRaises(ValueError, next, it2) 1578 self.assertRaises(ValueError, p.open) 1579 self.assertRaises(ValueError, p.resolve) 1580 self.assertRaises(ValueError, p.absolute) 1581 self.assertRaises(ValueError, p.__enter__) 1582 1583 def test_chmod(self): 1584 p = self.cls(BASE) / 'fileA' 1585 mode = p.stat().st_mode 1586 # Clear writable bit 1587 new_mode = mode & ~0o222 1588 p.chmod(new_mode) 1589 self.assertEqual(p.stat().st_mode, new_mode) 1590 # Set writable bit 1591 new_mode = mode | 0o222 1592 p.chmod(new_mode) 1593 self.assertEqual(p.stat().st_mode, new_mode) 1594 1595 # XXX also need a test for lchmod 1596 1597 def test_stat(self): 1598 p = self.cls(BASE) / 'fileA' 1599 st = p.stat() 1600 self.assertEqual(p.stat(), st) 1601 # Change file mode by flipping write bit 1602 p.chmod(st.st_mode ^ 0o222) 1603 self.addCleanup(p.chmod, st.st_mode) 1604 self.assertNotEqual(p.stat(), st) 1605 1606 @with_symlinks 1607 def test_lstat(self): 1608 p = self.cls(BASE)/ 'linkA' 1609 st = p.stat() 1610 self.assertNotEqual(st, p.lstat()) 1611 1612 def test_lstat_nosymlink(self): 1613 p = self.cls(BASE) / 'fileA' 1614 st = p.stat() 1615 self.assertEqual(st, p.lstat()) 1616 1617 @unittest.skipUnless(pwd, "the pwd module is needed for this test") 1618 def test_owner(self): 1619 p = self.cls(BASE) / 'fileA' 1620 uid = p.stat().st_uid 1621 try: 1622 name = pwd.getpwuid(uid).pw_name 1623 except KeyError: 1624 self.skipTest( 1625 "user %d doesn't have an entry in the system database" % uid) 1626 self.assertEqual(name, p.owner()) 1627 1628 @unittest.skipUnless(grp, "the grp module is needed for this test") 1629 def test_group(self): 1630 p = self.cls(BASE) / 'fileA' 1631 gid = p.stat().st_gid 1632 try: 1633 name = grp.getgrgid(gid).gr_name 1634 except KeyError: 1635 self.skipTest( 1636 "group %d doesn't have an entry in the system database" % gid) 1637 self.assertEqual(name, p.group()) 1638 1639 def test_unlink(self): 1640 p = self.cls(BASE) / 'fileA' 1641 p.unlink() 1642 self.assertFileNotFound(p.stat) 1643 self.assertFileNotFound(p.unlink) 1644 1645 def test_rmdir(self): 1646 p = self.cls(BASE) / 'dirA' 1647 for q in p.iterdir(): 1648 q.unlink() 1649 p.rmdir() 1650 self.assertFileNotFound(p.stat) 1651 self.assertFileNotFound(p.unlink) 1652 1653 def test_rename(self): 1654 P = self.cls(BASE) 1655 p = P / 'fileA' 1656 size = p.stat().st_size 1657 # Renaming to another path 1658 q = P / 'dirA' / 'fileAA' 1659 p.rename(q) 1660 self.assertEqual(q.stat().st_size, size) 1661 self.assertFileNotFound(p.stat) 1662 # Renaming to a str of a relative path 1663 r = rel_join('fileAAA') 1664 q.rename(r) 1665 self.assertEqual(os.stat(r).st_size, size) 1666 self.assertFileNotFound(q.stat) 1667 1668 def test_replace(self): 1669 P = self.cls(BASE) 1670 p = P / 'fileA' 1671 size = p.stat().st_size 1672 # Replacing a non-existing path 1673 q = P / 'dirA' / 'fileAA' 1674 p.replace(q) 1675 self.assertEqual(q.stat().st_size, size) 1676 self.assertFileNotFound(p.stat) 1677 # Replacing another (existing) path 1678 r = rel_join('dirB', 'fileB') 1679 q.replace(r) 1680 self.assertEqual(os.stat(r).st_size, size) 1681 self.assertFileNotFound(q.stat) 1682 1683 def test_touch_common(self): 1684 P = self.cls(BASE) 1685 p = P / 'newfileA' 1686 self.assertFalse(p.exists()) 1687 p.touch() 1688 self.assertTrue(p.exists()) 1689 st = p.stat() 1690 old_mtime = st.st_mtime 1691 old_mtime_ns = st.st_mtime_ns 1692 # Rewind the mtime sufficiently far in the past to work around 1693 # filesystem-specific timestamp granularity. 1694 os.utime(str(p), (old_mtime - 10, old_mtime - 10)) 1695 # The file mtime should be refreshed by calling touch() again 1696 p.touch() 1697 st = p.stat() 1698 self.assertGreaterEqual(st.st_mtime_ns, old_mtime_ns) 1699 self.assertGreaterEqual(st.st_mtime, old_mtime) 1700 # Now with exist_ok=False 1701 p = P / 'newfileB' 1702 self.assertFalse(p.exists()) 1703 p.touch(mode=0o700, exist_ok=False) 1704 self.assertTrue(p.exists()) 1705 self.assertRaises(OSError, p.touch, exist_ok=False) 1706 1707 def test_touch_nochange(self): 1708 P = self.cls(BASE) 1709 p = P / 'fileA' 1710 p.touch() 1711 with p.open('rb') as f: 1712 self.assertEqual(f.read().strip(), b"this is file A") 1713 1714 def test_mkdir(self): 1715 P = self.cls(BASE) 1716 p = P / 'newdirA' 1717 self.assertFalse(p.exists()) 1718 p.mkdir() 1719 self.assertTrue(p.exists()) 1720 self.assertTrue(p.is_dir()) 1721 with self.assertRaises(OSError) as cm: 1722 p.mkdir() 1723 self.assertEqual(cm.exception.errno, errno.EEXIST) 1724 1725 def test_mkdir_parents(self): 1726 # Creating a chain of directories 1727 p = self.cls(BASE, 'newdirB', 'newdirC') 1728 self.assertFalse(p.exists()) 1729 with self.assertRaises(OSError) as cm: 1730 p.mkdir() 1731 self.assertEqual(cm.exception.errno, errno.ENOENT) 1732 p.mkdir(parents=True) 1733 self.assertTrue(p.exists()) 1734 self.assertTrue(p.is_dir()) 1735 with self.assertRaises(OSError) as cm: 1736 p.mkdir(parents=True) 1737 self.assertEqual(cm.exception.errno, errno.EEXIST) 1738 # test `mode` arg 1739 mode = stat.S_IMODE(p.stat().st_mode) # default mode 1740 p = self.cls(BASE, 'newdirD', 'newdirE') 1741 p.mkdir(0o555, parents=True) 1742 self.assertTrue(p.exists()) 1743 self.assertTrue(p.is_dir()) 1744 if os.name != 'nt': 1745 # the directory's permissions follow the mode argument 1746 self.assertEqual(stat.S_IMODE(p.stat().st_mode), 0o7555 & mode) 1747 # the parent's permissions follow the default process settings 1748 self.assertEqual(stat.S_IMODE(p.parent.stat().st_mode), mode) 1749 1750 def test_mkdir_exist_ok(self): 1751 p = self.cls(BASE, 'dirB') 1752 st_ctime_first = p.stat().st_ctime 1753 self.assertTrue(p.exists()) 1754 self.assertTrue(p.is_dir()) 1755 with self.assertRaises(FileExistsError) as cm: 1756 p.mkdir() 1757 self.assertEqual(cm.exception.errno, errno.EEXIST) 1758 p.mkdir(exist_ok=True) 1759 self.assertTrue(p.exists()) 1760 self.assertEqual(p.stat().st_ctime, st_ctime_first) 1761 1762 def test_mkdir_exist_ok_with_parent(self): 1763 p = self.cls(BASE, 'dirC') 1764 self.assertTrue(p.exists()) 1765 with self.assertRaises(FileExistsError) as cm: 1766 p.mkdir() 1767 self.assertEqual(cm.exception.errno, errno.EEXIST) 1768 p = p / 'newdirC' 1769 p.mkdir(parents=True) 1770 st_ctime_first = p.stat().st_ctime 1771 self.assertTrue(p.exists()) 1772 with self.assertRaises(FileExistsError) as cm: 1773 p.mkdir(parents=True) 1774 self.assertEqual(cm.exception.errno, errno.EEXIST) 1775 p.mkdir(parents=True, exist_ok=True) 1776 self.assertTrue(p.exists()) 1777 self.assertEqual(p.stat().st_ctime, st_ctime_first) 1778 1779 @only_nt # XXX: not sure how to test this on POSIX 1780 def test_mkdir_with_unknown_drive(self): 1781 for d in 'ZYXWVUTSRQPONMLKJIHGFEDCBA': 1782 p = self.cls(d + ':\\') 1783 if not p.is_dir(): 1784 break 1785 else: 1786 self.skipTest("cannot find a drive that doesn't exist") 1787 with self.assertRaises(OSError): 1788 (p / 'child' / 'path').mkdir(parents=True) 1789 1790 def test_mkdir_with_child_file(self): 1791 p = self.cls(BASE, 'dirB', 'fileB') 1792 self.assertTrue(p.exists()) 1793 # An exception is raised when the last path component is an existing 1794 # regular file, regardless of whether exist_ok is true or not. 1795 with self.assertRaises(FileExistsError) as cm: 1796 p.mkdir(parents=True) 1797 self.assertEqual(cm.exception.errno, errno.EEXIST) 1798 with self.assertRaises(FileExistsError) as cm: 1799 p.mkdir(parents=True, exist_ok=True) 1800 self.assertEqual(cm.exception.errno, errno.EEXIST) 1801 1802 def test_mkdir_no_parents_file(self): 1803 p = self.cls(BASE, 'fileA') 1804 self.assertTrue(p.exists()) 1805 # An exception is raised when the last path component is an existing 1806 # regular file, regardless of whether exist_ok is true or not. 1807 with self.assertRaises(FileExistsError) as cm: 1808 p.mkdir() 1809 self.assertEqual(cm.exception.errno, errno.EEXIST) 1810 with self.assertRaises(FileExistsError) as cm: 1811 p.mkdir(exist_ok=True) 1812 self.assertEqual(cm.exception.errno, errno.EEXIST) 1813 1814 @with_symlinks 1815 def test_symlink_to(self): 1816 P = self.cls(BASE) 1817 target = P / 'fileA' 1818 # Symlinking a path target 1819 link = P / 'dirA' / 'linkAA' 1820 link.symlink_to(target) 1821 self.assertEqual(link.stat(), target.stat()) 1822 self.assertNotEqual(link.lstat(), target.stat()) 1823 # Symlinking a str target 1824 link = P / 'dirA' / 'linkAAA' 1825 link.symlink_to(str(target)) 1826 self.assertEqual(link.stat(), target.stat()) 1827 self.assertNotEqual(link.lstat(), target.stat()) 1828 self.assertFalse(link.is_dir()) 1829 # Symlinking to a directory 1830 target = P / 'dirB' 1831 link = P / 'dirA' / 'linkAAAA' 1832 link.symlink_to(target, target_is_directory=True) 1833 self.assertEqual(link.stat(), target.stat()) 1834 self.assertNotEqual(link.lstat(), target.stat()) 1835 self.assertTrue(link.is_dir()) 1836 self.assertTrue(list(link.iterdir())) 1837 1838 def test_is_dir(self): 1839 P = self.cls(BASE) 1840 self.assertTrue((P / 'dirA').is_dir()) 1841 self.assertFalse((P / 'fileA').is_dir()) 1842 self.assertFalse((P / 'non-existing').is_dir()) 1843 self.assertFalse((P / 'fileA' / 'bah').is_dir()) 1844 if not symlink_skip_reason: 1845 self.assertFalse((P / 'linkA').is_dir()) 1846 self.assertTrue((P / 'linkB').is_dir()) 1847 self.assertFalse((P/ 'brokenLink').is_dir()) 1848 1849 def test_is_file(self): 1850 P = self.cls(BASE) 1851 self.assertTrue((P / 'fileA').is_file()) 1852 self.assertFalse((P / 'dirA').is_file()) 1853 self.assertFalse((P / 'non-existing').is_file()) 1854 self.assertFalse((P / 'fileA' / 'bah').is_file()) 1855 if not symlink_skip_reason: 1856 self.assertTrue((P / 'linkA').is_file()) 1857 self.assertFalse((P / 'linkB').is_file()) 1858 self.assertFalse((P/ 'brokenLink').is_file()) 1859 1860 def test_is_symlink(self): 1861 P = self.cls(BASE) 1862 self.assertFalse((P / 'fileA').is_symlink()) 1863 self.assertFalse((P / 'dirA').is_symlink()) 1864 self.assertFalse((P / 'non-existing').is_symlink()) 1865 self.assertFalse((P / 'fileA' / 'bah').is_symlink()) 1866 if not symlink_skip_reason: 1867 self.assertTrue((P / 'linkA').is_symlink()) 1868 self.assertTrue((P / 'linkB').is_symlink()) 1869 self.assertTrue((P/ 'brokenLink').is_symlink()) 1870 1871 def test_is_fifo_false(self): 1872 P = self.cls(BASE) 1873 self.assertFalse((P / 'fileA').is_fifo()) 1874 self.assertFalse((P / 'dirA').is_fifo()) 1875 self.assertFalse((P / 'non-existing').is_fifo()) 1876 self.assertFalse((P / 'fileA' / 'bah').is_fifo()) 1877 1878 @unittest.skipUnless(hasattr(os, "mkfifo"), "os.mkfifo() required") 1879 @unittest.skipIf(android_not_root, "mkfifo not allowed, non root user") 1880 def test_is_fifo_true(self): 1881 P = self.cls(BASE, 'myfifo') 1882 os.mkfifo(str(P)) 1883 self.assertTrue(P.is_fifo()) 1884 self.assertFalse(P.is_socket()) 1885 self.assertFalse(P.is_file()) 1886 1887 def test_is_socket_false(self): 1888 P = self.cls(BASE) 1889 self.assertFalse((P / 'fileA').is_socket()) 1890 self.assertFalse((P / 'dirA').is_socket()) 1891 self.assertFalse((P / 'non-existing').is_socket()) 1892 self.assertFalse((P / 'fileA' / 'bah').is_socket()) 1893 1894 @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") 1895 def test_is_socket_true(self): 1896 P = self.cls(BASE, 'mysock') 1897 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 1898 self.addCleanup(sock.close) 1899 try: 1900 sock.bind(str(P)) 1901 except OSError as e: 1902 if (isinstance(e, PermissionError) or 1903 "AF_UNIX path too long" in str(e)): 1904 self.skipTest("cannot bind Unix socket: " + str(e)) 1905 self.assertTrue(P.is_socket()) 1906 self.assertFalse(P.is_fifo()) 1907 self.assertFalse(P.is_file()) 1908 1909 def test_is_block_device_false(self): 1910 P = self.cls(BASE) 1911 self.assertFalse((P / 'fileA').is_block_device()) 1912 self.assertFalse((P / 'dirA').is_block_device()) 1913 self.assertFalse((P / 'non-existing').is_block_device()) 1914 self.assertFalse((P / 'fileA' / 'bah').is_block_device()) 1915 1916 def test_is_char_device_false(self): 1917 P = self.cls(BASE) 1918 self.assertFalse((P / 'fileA').is_char_device()) 1919 self.assertFalse((P / 'dirA').is_char_device()) 1920 self.assertFalse((P / 'non-existing').is_char_device()) 1921 self.assertFalse((P / 'fileA' / 'bah').is_char_device()) 1922 1923 def test_is_char_device_true(self): 1924 # Under Unix, /dev/null should generally be a char device 1925 P = self.cls('/dev/null') 1926 if not P.exists(): 1927 self.skipTest("/dev/null required") 1928 self.assertTrue(P.is_char_device()) 1929 self.assertFalse(P.is_block_device()) 1930 self.assertFalse(P.is_file()) 1931 1932 def test_pickling_common(self): 1933 p = self.cls(BASE, 'fileA') 1934 for proto in range(0, pickle.HIGHEST_PROTOCOL + 1): 1935 dumped = pickle.dumps(p, proto) 1936 pp = pickle.loads(dumped) 1937 self.assertEqual(pp.stat(), p.stat()) 1938 1939 def test_parts_interning(self): 1940 P = self.cls 1941 p = P('/usr/bin/foo') 1942 q = P('/usr/local/bin') 1943 # 'usr' 1944 self.assertIs(p.parts[1], q.parts[1]) 1945 # 'bin' 1946 self.assertIs(p.parts[2], q.parts[3]) 1947 1948 def _check_complex_symlinks(self, link0_target): 1949 # Test solving a non-looping chain of symlinks (issue #19887) 1950 P = self.cls(BASE) 1951 self.dirlink(os.path.join('link0', 'link0'), join('link1')) 1952 self.dirlink(os.path.join('link1', 'link1'), join('link2')) 1953 self.dirlink(os.path.join('link2', 'link2'), join('link3')) 1954 self.dirlink(link0_target, join('link0')) 1955 1956 # Resolve absolute paths 1957 p = (P / 'link0').resolve() 1958 self.assertEqual(p, P) 1959 self.assertEqual(str(p), BASE) 1960 p = (P / 'link1').resolve() 1961 self.assertEqual(p, P) 1962 self.assertEqual(str(p), BASE) 1963 p = (P / 'link2').resolve() 1964 self.assertEqual(p, P) 1965 self.assertEqual(str(p), BASE) 1966 p = (P / 'link3').resolve() 1967 self.assertEqual(p, P) 1968 self.assertEqual(str(p), BASE) 1969 1970 # Resolve relative paths 1971 old_path = os.getcwd() 1972 os.chdir(BASE) 1973 try: 1974 p = self.cls('link0').resolve() 1975 self.assertEqual(p, P) 1976 self.assertEqual(str(p), BASE) 1977 p = self.cls('link1').resolve() 1978 self.assertEqual(p, P) 1979 self.assertEqual(str(p), BASE) 1980 p = self.cls('link2').resolve() 1981 self.assertEqual(p, P) 1982 self.assertEqual(str(p), BASE) 1983 p = self.cls('link3').resolve() 1984 self.assertEqual(p, P) 1985 self.assertEqual(str(p), BASE) 1986 finally: 1987 os.chdir(old_path) 1988 1989 @with_symlinks 1990 def test_complex_symlinks_absolute(self): 1991 self._check_complex_symlinks(BASE) 1992 1993 @with_symlinks 1994 def test_complex_symlinks_relative(self): 1995 self._check_complex_symlinks('.') 1996 1997 @with_symlinks 1998 def test_complex_symlinks_relative_dot_dot(self): 1999 self._check_complex_symlinks(os.path.join('dirA', '..')) 2000 2001 2002 class PathTest(_BasePathTest, unittest.TestCase): 2003 cls = pathlib.Path 2004 2005 def test_concrete_class(self): 2006 p = self.cls('a') 2007 self.assertIs(type(p), 2008 pathlib.WindowsPath if os.name == 'nt' else pathlib.PosixPath) 2009 2010 def test_unsupported_flavour(self): 2011 if os.name == 'nt': 2012 self.assertRaises(NotImplementedError, pathlib.PosixPath) 2013 else: 2014 self.assertRaises(NotImplementedError, pathlib.WindowsPath) 2015 2016 def test_glob_empty_pattern(self): 2017 p = self.cls() 2018 with self.assertRaisesRegex(ValueError, 'Unacceptable pattern'): 2019 list(p.glob('')) 2020 2021 2022 @only_posix 2023 class PosixPathTest(_BasePathTest, unittest.TestCase): 2024 cls = pathlib.PosixPath 2025 2026 def _check_symlink_loop(self, *args, strict=True): 2027 path = self.cls(*args) 2028 with self.assertRaises(RuntimeError): 2029 print(path.resolve(strict)) 2030 2031 def test_open_mode(self): 2032 old_mask = os.umask(0) 2033 self.addCleanup(os.umask, old_mask) 2034 p = self.cls(BASE) 2035 with (p / 'new_file').open('wb'): 2036 pass 2037 st = os.stat(join('new_file')) 2038 self.assertEqual(stat.S_IMODE(st.st_mode), 0o666) 2039 os.umask(0o022) 2040 with (p / 'other_new_file').open('wb'): 2041 pass 2042 st = os.stat(join('other_new_file')) 2043 self.assertEqual(stat.S_IMODE(st.st_mode), 0o644) 2044 2045 def test_touch_mode(self): 2046 old_mask = os.umask(0) 2047 self.addCleanup(os.umask, old_mask) 2048 p = self.cls(BASE) 2049 (p / 'new_file').touch() 2050 st = os.stat(join('new_file')) 2051 self.assertEqual(stat.S_IMODE(st.st_mode), 0o666) 2052 os.umask(0o022) 2053 (p / 'other_new_file').touch() 2054 st = os.stat(join('other_new_file')) 2055 self.assertEqual(stat.S_IMODE(st.st_mode), 0o644) 2056 (p / 'masked_new_file').touch(mode=0o750) 2057 st = os.stat(join('masked_new_file')) 2058 self.assertEqual(stat.S_IMODE(st.st_mode), 0o750) 2059 2060 @with_symlinks 2061 def test_resolve_loop(self): 2062 # Loops with relative symlinks 2063 os.symlink('linkX/inside', join('linkX')) 2064 self._check_symlink_loop(BASE, 'linkX') 2065 os.symlink('linkY', join('linkY')) 2066 self._check_symlink_loop(BASE, 'linkY') 2067 os.symlink('linkZ/../linkZ', join('linkZ')) 2068 self._check_symlink_loop(BASE, 'linkZ') 2069 # Non-strict 2070 self._check_symlink_loop(BASE, 'linkZ', 'foo', strict=False) 2071 # Loops with absolute symlinks 2072 os.symlink(join('linkU/inside'), join('linkU')) 2073 self._check_symlink_loop(BASE, 'linkU') 2074 os.symlink(join('linkV'), join('linkV')) 2075 self._check_symlink_loop(BASE, 'linkV') 2076 os.symlink(join('linkW/../linkW'), join('linkW')) 2077 self._check_symlink_loop(BASE, 'linkW') 2078 # Non-strict 2079 self._check_symlink_loop(BASE, 'linkW', 'foo', strict=False) 2080 2081 def test_glob(self): 2082 P = self.cls 2083 p = P(BASE) 2084 given = set(p.glob("FILEa")) 2085 expect = set() if not support.fs_is_case_insensitive(BASE) else given 2086 self.assertEqual(given, expect) 2087 self.assertEqual(set(p.glob("FILEa*")), set()) 2088 2089 def test_rglob(self): 2090 P = self.cls 2091 p = P(BASE, "dirC") 2092 given = set(p.rglob("FILEd")) 2093 expect = set() if not support.fs_is_case_insensitive(BASE) else given 2094 self.assertEqual(given, expect) 2095 self.assertEqual(set(p.rglob("FILEd*")), set()) 2096 2097 @unittest.skipUnless(hasattr(pwd, 'getpwall'), 2098 'pwd module does not expose getpwall()') 2099 def test_expanduser(self): 2100 P = self.cls 2101 support.import_module('pwd') 2102 import pwd 2103 pwdent = pwd.getpwuid(os.getuid()) 2104 username = pwdent.pw_name 2105 userhome = pwdent.pw_dir.rstrip('/') or '/' 2106 # find arbitrary different user (if exists) 2107 for pwdent in pwd.getpwall(): 2108 othername = pwdent.pw_name 2109 otherhome = pwdent.pw_dir.rstrip('/') 2110 if othername != username and otherhome: 2111 break 2112 2113 p1 = P('~/Documents') 2114 p2 = P('~' + username + '/Documents') 2115 p3 = P('~' + othername + '/Documents') 2116 p4 = P('../~' + username + '/Documents') 2117 p5 = P('/~' + username + '/Documents') 2118 p6 = P('') 2119 p7 = P('~fakeuser/Documents') 2120 2121 with support.EnvironmentVarGuard() as env: 2122 env.pop('HOME', None) 2123 2124 self.assertEqual(p1.expanduser(), P(userhome) / 'Documents') 2125 self.assertEqual(p2.expanduser(), P(userhome) / 'Documents') 2126 self.assertEqual(p3.expanduser(), P(otherhome) / 'Documents') 2127 self.assertEqual(p4.expanduser(), p4) 2128 self.assertEqual(p5.expanduser(), p5) 2129 self.assertEqual(p6.expanduser(), p6) 2130 self.assertRaises(RuntimeError, p7.expanduser) 2131 2132 env['HOME'] = '/tmp' 2133 self.assertEqual(p1.expanduser(), P('/tmp/Documents')) 2134 self.assertEqual(p2.expanduser(), P(userhome) / 'Documents') 2135 self.assertEqual(p3.expanduser(), P(otherhome) / 'Documents') 2136 self.assertEqual(p4.expanduser(), p4) 2137 self.assertEqual(p5.expanduser(), p5) 2138 self.assertEqual(p6.expanduser(), p6) 2139 self.assertRaises(RuntimeError, p7.expanduser) 2140 2141 2142 @only_nt 2143 class WindowsPathTest(_BasePathTest, unittest.TestCase): 2144 cls = pathlib.WindowsPath 2145 2146 def test_glob(self): 2147 P = self.cls 2148 p = P(BASE) 2149 self.assertEqual(set(p.glob("FILEa")), { P(BASE, "fileA") }) 2150 2151 def test_rglob(self): 2152 P = self.cls 2153 p = P(BASE, "dirC") 2154 self.assertEqual(set(p.rglob("FILEd")), { P(BASE, "dirC/dirD/fileD") }) 2155 2156 def test_expanduser(self): 2157 P = self.cls 2158 with support.EnvironmentVarGuard() as env: 2159 env.pop('HOME', None) 2160 env.pop('USERPROFILE', None) 2161 env.pop('HOMEPATH', None) 2162 env.pop('HOMEDRIVE', None) 2163 env['USERNAME'] = 'alice' 2164 2165 # test that the path returns unchanged 2166 p1 = P('~/My Documents') 2167 p2 = P('~alice/My Documents') 2168 p3 = P('~bob/My Documents') 2169 p4 = P('/~/My Documents') 2170 p5 = P('d:~/My Documents') 2171 p6 = P('') 2172 self.assertRaises(RuntimeError, p1.expanduser) 2173 self.assertRaises(RuntimeError, p2.expanduser) 2174 self.assertRaises(RuntimeError, p3.expanduser) 2175 self.assertEqual(p4.expanduser(), p4) 2176 self.assertEqual(p5.expanduser(), p5) 2177 self.assertEqual(p6.expanduser(), p6) 2178 2179 def check(): 2180 env.pop('USERNAME', None) 2181 self.assertEqual(p1.expanduser(), 2182 P('C:/Users/alice/My Documents')) 2183 self.assertRaises(KeyError, p2.expanduser) 2184 env['USERNAME'] = 'alice' 2185 self.assertEqual(p2.expanduser(), 2186 P('C:/Users/alice/My Documents')) 2187 self.assertEqual(p3.expanduser(), 2188 P('C:/Users/bob/My Documents')) 2189 self.assertEqual(p4.expanduser(), p4) 2190 self.assertEqual(p5.expanduser(), p5) 2191 self.assertEqual(p6.expanduser(), p6) 2192 2193 # test the first lookup key in the env vars 2194 env['HOME'] = 'C:\\Users\\alice' 2195 check() 2196 2197 # test that HOMEPATH is available instead 2198 env.pop('HOME', None) 2199 env['HOMEPATH'] = 'C:\\Users\\alice' 2200 check() 2201 2202 env['HOMEDRIVE'] = 'C:\\' 2203 env['HOMEPATH'] = 'Users\\alice' 2204 check() 2205 2206 env.pop('HOMEDRIVE', None) 2207 env.pop('HOMEPATH', None) 2208 env['USERPROFILE'] = 'C:\\Users\\alice' 2209 check() 2210 2211 2212 if __name__ == "__main__": 2213 unittest.main() 2214