1import collections.abc 2import io 3import os 4import sys 5import errno 6import pathlib 7import pickle 8import socket 9import stat 10import tempfile 11import unittest 12from unittest import mock 13 14from test import support 15from test.support import TESTFN, FakePath 16 17try: 18 import grp, pwd 19except ImportError: 20 grp = pwd = None 21 22 23class _BaseFlavourTest(object): 24 25 def _check_parse_parts(self, arg, expected): 26 f = self.flavour.parse_parts 27 sep = self.flavour.sep 28 altsep = self.flavour.altsep 29 actual = f([x.replace('/', sep) for x in arg]) 30 self.assertEqual(actual, expected) 31 if altsep: 32 actual = f([x.replace('/', altsep) for x in arg]) 33 self.assertEqual(actual, expected) 34 35 def test_parse_parts_common(self): 36 check = self._check_parse_parts 37 sep = self.flavour.sep 38 # Unanchored parts 39 check([], ('', '', [])) 40 check(['a'], ('', '', ['a'])) 41 check(['a/'], ('', '', ['a'])) 42 check(['a', 'b'], ('', '', ['a', 'b'])) 43 # Expansion 44 check(['a/b'], ('', '', ['a', 'b'])) 45 check(['a/b/'], ('', '', ['a', 'b'])) 46 check(['a', 'b/c', 'd'], ('', '', ['a', 'b', 'c', 'd'])) 47 # Collapsing and stripping excess slashes 48 check(['a', 'b//c', 'd'], ('', '', ['a', 'b', 'c', 'd'])) 49 check(['a', 'b/c/', 'd'], ('', '', ['a', 'b', 'c', 'd'])) 50 # Eliminating standalone dots 51 check(['.'], ('', '', [])) 52 check(['.', '.', 'b'], ('', '', ['b'])) 53 check(['a', '.', 'b'], ('', '', ['a', 'b'])) 54 check(['a', '.', '.'], ('', '', ['a'])) 55 # The first part is anchored 56 check(['/a/b'], ('', sep, [sep, 'a', 'b'])) 57 check(['/a', 'b'], ('', sep, [sep, 'a', 'b'])) 58 check(['/a/', 'b'], ('', sep, [sep, 'a', 'b'])) 59 # Ignoring parts before an anchored part 60 check(['a', '/b', 'c'], ('', sep, [sep, 'b', 'c'])) 61 check(['a', '/b', '/c'], ('', sep, [sep, 'c'])) 62 63 64class PosixFlavourTest(_BaseFlavourTest, unittest.TestCase): 65 flavour = pathlib._posix_flavour 66 67 def test_parse_parts(self): 68 check = self._check_parse_parts 69 # Collapsing of excess leading slashes, except for the double-slash 70 # special case. 71 check(['//a', 'b'], ('', '//', ['//', 'a', 'b'])) 72 check(['///a', 'b'], ('', '/', ['/', 'a', 'b'])) 73 check(['////a', 'b'], ('', '/', ['/', 'a', 'b'])) 74 # Paths which look like NT paths aren't treated specially 75 check(['c:a'], ('', '', ['c:a'])) 76 check(['c:\\a'], ('', '', ['c:\\a'])) 77 check(['\\a'], ('', '', ['\\a'])) 78 79 def test_splitroot(self): 80 f = self.flavour.splitroot 81 self.assertEqual(f(''), ('', '', '')) 82 self.assertEqual(f('a'), ('', '', 'a')) 83 self.assertEqual(f('a/b'), ('', '', 'a/b')) 84 self.assertEqual(f('a/b/'), ('', '', 'a/b/')) 85 self.assertEqual(f('/a'), ('', '/', 'a')) 86 self.assertEqual(f('/a/b'), ('', '/', 'a/b')) 87 self.assertEqual(f('/a/b/'), ('', '/', 'a/b/')) 88 # The root is collapsed when there are redundant slashes 89 # except when there are exactly two leading slashes, which 90 # is a special case in POSIX. 91 self.assertEqual(f('//a'), ('', '//', 'a')) 92 self.assertEqual(f('///a'), ('', '/', 'a')) 93 self.assertEqual(f('///a/b'), ('', '/', 'a/b')) 94 # Paths which look like NT paths aren't treated specially 95 self.assertEqual(f('c:/a/b'), ('', '', 'c:/a/b')) 96 self.assertEqual(f('\\/a/b'), ('', '', '\\/a/b')) 97 self.assertEqual(f('\\a\\b'), ('', '', '\\a\\b')) 98 99 100class NTFlavourTest(_BaseFlavourTest, unittest.TestCase): 101 flavour = pathlib._windows_flavour 102 103 def test_parse_parts(self): 104 check = self._check_parse_parts 105 # First part is anchored 106 check(['c:'], ('c:', '', ['c:'])) 107 check(['c:/'], ('c:', '\\', ['c:\\'])) 108 check(['/'], ('', '\\', ['\\'])) 109 check(['c:a'], ('c:', '', ['c:', 'a'])) 110 check(['c:/a'], ('c:', '\\', ['c:\\', 'a'])) 111 check(['/a'], ('', '\\', ['\\', 'a'])) 112 # UNC paths 113 check(['//a/b'], ('\\\\a\\b', '\\', ['\\\\a\\b\\'])) 114 check(['//a/b/'], ('\\\\a\\b', '\\', ['\\\\a\\b\\'])) 115 check(['//a/b/c'], ('\\\\a\\b', '\\', ['\\\\a\\b\\', 'c'])) 116 # Second part is anchored, so that the first part is ignored 117 check(['a', 'Z:b', 'c'], ('Z:', '', ['Z:', 'b', 'c'])) 118 check(['a', 'Z:/b', 'c'], ('Z:', '\\', ['Z:\\', 'b', 'c'])) 119 # UNC paths 120 check(['a', '//b/c', 'd'], ('\\\\b\\c', '\\', ['\\\\b\\c\\', 'd'])) 121 # Collapsing and stripping excess slashes 122 check(['a', 'Z://b//c/', 'd/'], ('Z:', '\\', ['Z:\\', 'b', 'c', 'd'])) 123 # UNC paths 124 check(['a', '//b/c//', 'd'], ('\\\\b\\c', '\\', ['\\\\b\\c\\', 'd'])) 125 # Extended paths 126 check(['//?/c:/'], ('\\\\?\\c:', '\\', ['\\\\?\\c:\\'])) 127 check(['//?/c:/a'], ('\\\\?\\c:', '\\', ['\\\\?\\c:\\', 'a'])) 128 check(['//?/c:/a', '/b'], ('\\\\?\\c:', '\\', ['\\\\?\\c:\\', 'b'])) 129 # Extended UNC paths (format is "\\?\UNC\server\share") 130 check(['//?/UNC/b/c'], ('\\\\?\\UNC\\b\\c', '\\', ['\\\\?\\UNC\\b\\c\\'])) 131 check(['//?/UNC/b/c/d'], ('\\\\?\\UNC\\b\\c', '\\', ['\\\\?\\UNC\\b\\c\\', 'd'])) 132 # Second part has a root but not drive 133 check(['a', '/b', 'c'], ('', '\\', ['\\', 'b', 'c'])) 134 check(['Z:/a', '/b', 'c'], ('Z:', '\\', ['Z:\\', 'b', 'c'])) 135 check(['//?/Z:/a', '/b', 'c'], ('\\\\?\\Z:', '\\', ['\\\\?\\Z:\\', 'b', 'c'])) 136 137 def test_splitroot(self): 138 f = self.flavour.splitroot 139 self.assertEqual(f(''), ('', '', '')) 140 self.assertEqual(f('a'), ('', '', 'a')) 141 self.assertEqual(f('a\\b'), ('', '', 'a\\b')) 142 self.assertEqual(f('\\a'), ('', '\\', 'a')) 143 self.assertEqual(f('\\a\\b'), ('', '\\', 'a\\b')) 144 self.assertEqual(f('c:a\\b'), ('c:', '', 'a\\b')) 145 self.assertEqual(f('c:\\a\\b'), ('c:', '\\', 'a\\b')) 146 # Redundant slashes in the root are collapsed 147 self.assertEqual(f('\\\\a'), ('', '\\', 'a')) 148 self.assertEqual(f('\\\\\\a/b'), ('', '\\', 'a/b')) 149 self.assertEqual(f('c:\\\\a'), ('c:', '\\', 'a')) 150 self.assertEqual(f('c:\\\\\\a/b'), ('c:', '\\', 'a/b')) 151 # Valid UNC paths 152 self.assertEqual(f('\\\\a\\b'), ('\\\\a\\b', '\\', '')) 153 self.assertEqual(f('\\\\a\\b\\'), ('\\\\a\\b', '\\', '')) 154 self.assertEqual(f('\\\\a\\b\\c\\d'), ('\\\\a\\b', '\\', 'c\\d')) 155 # These are non-UNC paths (according to ntpath.py and test_ntpath) 156 # However, command.com says such paths are invalid, so it's 157 # difficult to know what the right semantics are 158 self.assertEqual(f('\\\\\\a\\b'), ('', '\\', 'a\\b')) 159 self.assertEqual(f('\\\\a'), ('', '\\', 'a')) 160 161 162# 163# Tests for the pure classes 164# 165 166class _BasePurePathTest(object): 167 168 # keys are canonical paths, values are list of tuples of arguments 169 # supposed to produce equal paths 170 equivalences = { 171 'a/b': [ 172 ('a', 'b'), ('a/', 'b'), ('a', 'b/'), ('a/', 'b/'), 173 ('a/b/',), ('a//b',), ('a//b//',), 174 # empty components get removed 175 ('', 'a', 'b'), ('a', '', 'b'), ('a', 'b', ''), 176 ], 177 '/b/c/d': [ 178 ('a', '/b/c', 'd'), ('a', '///b//c', 'd/'), 179 ('/a', '/b/c', 'd'), 180 # empty components get removed 181 ('/', 'b', '', 'c/d'), ('/', '', 'b/c/d'), ('', '/b/c/d'), 182 ], 183 } 184 185 def setUp(self): 186 p = self.cls('a') 187 self.flavour = p._flavour 188 self.sep = self.flavour.sep 189 self.altsep = self.flavour.altsep 190 191 def test_constructor_common(self): 192 P = self.cls 193 p = P('a') 194 self.assertIsInstance(p, P) 195 P('a', 'b', 'c') 196 P('/a', 'b', 'c') 197 P('a/b/c') 198 P('/a/b/c') 199 P(FakePath("a/b/c")) 200 self.assertEqual(P(P('a')), P('a')) 201 self.assertEqual(P(P('a'), 'b'), P('a/b')) 202 self.assertEqual(P(P('a'), P('b')), P('a/b')) 203 self.assertEqual(P(P('a'), P('b'), P('c')), P(FakePath("a/b/c"))) 204 205 def _check_str_subclass(self, *args): 206 # Issue #21127: it should be possible to construct a PurePath object 207 # from a str subclass instance, and it then gets converted to 208 # a pure str object. 209 class StrSubclass(str): 210 pass 211 P = self.cls 212 p = P(*(StrSubclass(x) for x in args)) 213 self.assertEqual(p, P(*args)) 214 for part in p.parts: 215 self.assertIs(type(part), str) 216 217 def test_str_subclass_common(self): 218 self._check_str_subclass('') 219 self._check_str_subclass('.') 220 self._check_str_subclass('a') 221 self._check_str_subclass('a/b.txt') 222 self._check_str_subclass('/a/b.txt') 223 224 def test_join_common(self): 225 P = self.cls 226 p = P('a/b') 227 pp = p.joinpath('c') 228 self.assertEqual(pp, P('a/b/c')) 229 self.assertIs(type(pp), type(p)) 230 pp = p.joinpath('c', 'd') 231 self.assertEqual(pp, P('a/b/c/d')) 232 pp = p.joinpath(P('c')) 233 self.assertEqual(pp, P('a/b/c')) 234 pp = p.joinpath('/c') 235 self.assertEqual(pp, P('/c')) 236 237 def test_div_common(self): 238 # Basically the same as joinpath() 239 P = self.cls 240 p = P('a/b') 241 pp = p / 'c' 242 self.assertEqual(pp, P('a/b/c')) 243 self.assertIs(type(pp), type(p)) 244 pp = p / 'c/d' 245 self.assertEqual(pp, P('a/b/c/d')) 246 pp = p / 'c' / 'd' 247 self.assertEqual(pp, P('a/b/c/d')) 248 pp = 'c' / p / 'd' 249 self.assertEqual(pp, P('c/a/b/d')) 250 pp = p / P('c') 251 self.assertEqual(pp, P('a/b/c')) 252 pp = p/ '/c' 253 self.assertEqual(pp, P('/c')) 254 255 def _check_str(self, expected, args): 256 p = self.cls(*args) 257 self.assertEqual(str(p), expected.replace('/', self.sep)) 258 259 def test_str_common(self): 260 # Canonicalized paths roundtrip 261 for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'): 262 self._check_str(pathstr, (pathstr,)) 263 # Special case for the empty path 264 self._check_str('.', ('',)) 265 # Other tests for str() are in test_equivalences() 266 267 def test_as_posix_common(self): 268 P = self.cls 269 for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'): 270 self.assertEqual(P(pathstr).as_posix(), pathstr) 271 # Other tests for as_posix() are in test_equivalences() 272 273 def test_as_bytes_common(self): 274 sep = os.fsencode(self.sep) 275 P = self.cls 276 self.assertEqual(bytes(P('a/b')), b'a' + sep + b'b') 277 278 def test_as_uri_common(self): 279 P = self.cls 280 with self.assertRaises(ValueError): 281 P('a').as_uri() 282 with self.assertRaises(ValueError): 283 P().as_uri() 284 285 def test_repr_common(self): 286 for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'): 287 p = self.cls(pathstr) 288 clsname = p.__class__.__name__ 289 r = repr(p) 290 # The repr() is in the form ClassName("forward-slashes path") 291 self.assertTrue(r.startswith(clsname + '('), r) 292 self.assertTrue(r.endswith(')'), r) 293 inner = r[len(clsname) + 1 : -1] 294 self.assertEqual(eval(inner), p.as_posix()) 295 # The repr() roundtrips 296 q = eval(r, pathlib.__dict__) 297 self.assertIs(q.__class__, p.__class__) 298 self.assertEqual(q, p) 299 self.assertEqual(repr(q), r) 300 301 def test_eq_common(self): 302 P = self.cls 303 self.assertEqual(P('a/b'), P('a/b')) 304 self.assertEqual(P('a/b'), P('a', 'b')) 305 self.assertNotEqual(P('a/b'), P('a')) 306 self.assertNotEqual(P('a/b'), P('/a/b')) 307 self.assertNotEqual(P('a/b'), P()) 308 self.assertNotEqual(P('/a/b'), P('/')) 309 self.assertNotEqual(P(), P('/')) 310 self.assertNotEqual(P(), "") 311 self.assertNotEqual(P(), {}) 312 self.assertNotEqual(P(), int) 313 314 def test_match_common(self): 315 P = self.cls 316 self.assertRaises(ValueError, P('a').match, '') 317 self.assertRaises(ValueError, P('a').match, '.') 318 # Simple relative pattern 319 self.assertTrue(P('b.py').match('b.py')) 320 self.assertTrue(P('a/b.py').match('b.py')) 321 self.assertTrue(P('/a/b.py').match('b.py')) 322 self.assertFalse(P('a.py').match('b.py')) 323 self.assertFalse(P('b/py').match('b.py')) 324 self.assertFalse(P('/a.py').match('b.py')) 325 self.assertFalse(P('b.py/c').match('b.py')) 326 # Wilcard relative pattern 327 self.assertTrue(P('b.py').match('*.py')) 328 self.assertTrue(P('a/b.py').match('*.py')) 329 self.assertTrue(P('/a/b.py').match('*.py')) 330 self.assertFalse(P('b.pyc').match('*.py')) 331 self.assertFalse(P('b./py').match('*.py')) 332 self.assertFalse(P('b.py/c').match('*.py')) 333 # Multi-part relative pattern 334 self.assertTrue(P('ab/c.py').match('a*/*.py')) 335 self.assertTrue(P('/d/ab/c.py').match('a*/*.py')) 336 self.assertFalse(P('a.py').match('a*/*.py')) 337 self.assertFalse(P('/dab/c.py').match('a*/*.py')) 338 self.assertFalse(P('ab/c.py/d').match('a*/*.py')) 339 # Absolute pattern 340 self.assertTrue(P('/b.py').match('/*.py')) 341 self.assertFalse(P('b.py').match('/*.py')) 342 self.assertFalse(P('a/b.py').match('/*.py')) 343 self.assertFalse(P('/a/b.py').match('/*.py')) 344 # Multi-part absolute pattern 345 self.assertTrue(P('/a/b.py').match('/a/*.py')) 346 self.assertFalse(P('/ab.py').match('/a/*.py')) 347 self.assertFalse(P('/a/b/c.py').match('/a/*.py')) 348 349 def test_ordering_common(self): 350 # Ordering is tuple-alike 351 def assertLess(a, b): 352 self.assertLess(a, b) 353 self.assertGreater(b, a) 354 P = self.cls 355 a = P('a') 356 b = P('a/b') 357 c = P('abc') 358 d = P('b') 359 assertLess(a, b) 360 assertLess(a, c) 361 assertLess(a, d) 362 assertLess(b, c) 363 assertLess(c, d) 364 P = self.cls 365 a = P('/a') 366 b = P('/a/b') 367 c = P('/abc') 368 d = P('/b') 369 assertLess(a, b) 370 assertLess(a, c) 371 assertLess(a, d) 372 assertLess(b, c) 373 assertLess(c, d) 374 with self.assertRaises(TypeError): 375 P() < {} 376 377 def test_parts_common(self): 378 # `parts` returns a tuple 379 sep = self.sep 380 P = self.cls 381 p = P('a/b') 382 parts = p.parts 383 self.assertEqual(parts, ('a', 'b')) 384 # The object gets reused 385 self.assertIs(parts, p.parts) 386 # When the path is absolute, the anchor is a separate part 387 p = P('/a/b') 388 parts = p.parts 389 self.assertEqual(parts, (sep, 'a', 'b')) 390 391 def test_fspath_common(self): 392 P = self.cls 393 p = P('a/b') 394 self._check_str(p.__fspath__(), ('a/b',)) 395 self._check_str(os.fspath(p), ('a/b',)) 396 397 def test_equivalences(self): 398 for k, tuples in self.equivalences.items(): 399 canon = k.replace('/', self.sep) 400 posix = k.replace(self.sep, '/') 401 if canon != posix: 402 tuples = tuples + [ 403 tuple(part.replace('/', self.sep) for part in t) 404 for t in tuples 405 ] 406 tuples.append((posix, )) 407 pcanon = self.cls(canon) 408 for t in tuples: 409 p = self.cls(*t) 410 self.assertEqual(p, pcanon, "failed with args {}".format(t)) 411 self.assertEqual(hash(p), hash(pcanon)) 412 self.assertEqual(str(p), canon) 413 self.assertEqual(p.as_posix(), posix) 414 415 def test_parent_common(self): 416 # Relative 417 P = self.cls 418 p = P('a/b/c') 419 self.assertEqual(p.parent, P('a/b')) 420 self.assertEqual(p.parent.parent, P('a')) 421 self.assertEqual(p.parent.parent.parent, P()) 422 self.assertEqual(p.parent.parent.parent.parent, P()) 423 # Anchored 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 430 def test_parents_common(self): 431 # Relative 432 P = self.cls 433 p = P('a/b/c') 434 par = p.parents 435 self.assertEqual(len(par), 3) 436 self.assertEqual(par[0], P('a/b')) 437 self.assertEqual(par[1], P('a')) 438 self.assertEqual(par[2], P('.')) 439 self.assertEqual(list(par), [P('a/b'), P('a'), P('.')]) 440 with self.assertRaises(IndexError): 441 par[-1] 442 with self.assertRaises(IndexError): 443 par[3] 444 with self.assertRaises(TypeError): 445 par[0] = p 446 # Anchored 447 p = P('/a/b/c') 448 par = p.parents 449 self.assertEqual(len(par), 3) 450 self.assertEqual(par[0], P('/a/b')) 451 self.assertEqual(par[1], P('/a')) 452 self.assertEqual(par[2], P('/')) 453 self.assertEqual(list(par), [P('/a/b'), P('/a'), P('/')]) 454 with self.assertRaises(IndexError): 455 par[3] 456 457 def test_drive_common(self): 458 P = self.cls 459 self.assertEqual(P('a/b').drive, '') 460 self.assertEqual(P('/a/b').drive, '') 461 self.assertEqual(P('').drive, '') 462 463 def test_root_common(self): 464 P = self.cls 465 sep = self.sep 466 self.assertEqual(P('').root, '') 467 self.assertEqual(P('a/b').root, '') 468 self.assertEqual(P('/').root, sep) 469 self.assertEqual(P('/a/b').root, sep) 470 471 def test_anchor_common(self): 472 P = self.cls 473 sep = self.sep 474 self.assertEqual(P('').anchor, '') 475 self.assertEqual(P('a/b').anchor, '') 476 self.assertEqual(P('/').anchor, sep) 477 self.assertEqual(P('/a/b').anchor, sep) 478 479 def test_name_common(self): 480 P = self.cls 481 self.assertEqual(P('').name, '') 482 self.assertEqual(P('.').name, '') 483 self.assertEqual(P('/').name, '') 484 self.assertEqual(P('a/b').name, 'b') 485 self.assertEqual(P('/a/b').name, 'b') 486 self.assertEqual(P('/a/b/.').name, 'b') 487 self.assertEqual(P('a/b.py').name, 'b.py') 488 self.assertEqual(P('/a/b.py').name, 'b.py') 489 490 def test_suffix_common(self): 491 P = self.cls 492 self.assertEqual(P('').suffix, '') 493 self.assertEqual(P('.').suffix, '') 494 self.assertEqual(P('..').suffix, '') 495 self.assertEqual(P('/').suffix, '') 496 self.assertEqual(P('a/b').suffix, '') 497 self.assertEqual(P('/a/b').suffix, '') 498 self.assertEqual(P('/a/b/.').suffix, '') 499 self.assertEqual(P('a/b.py').suffix, '.py') 500 self.assertEqual(P('/a/b.py').suffix, '.py') 501 self.assertEqual(P('a/.hgrc').suffix, '') 502 self.assertEqual(P('/a/.hgrc').suffix, '') 503 self.assertEqual(P('a/.hg.rc').suffix, '.rc') 504 self.assertEqual(P('/a/.hg.rc').suffix, '.rc') 505 self.assertEqual(P('a/b.tar.gz').suffix, '.gz') 506 self.assertEqual(P('/a/b.tar.gz').suffix, '.gz') 507 self.assertEqual(P('a/Some name. Ending with a dot.').suffix, '') 508 self.assertEqual(P('/a/Some name. Ending with a dot.').suffix, '') 509 510 def test_suffixes_common(self): 511 P = self.cls 512 self.assertEqual(P('').suffixes, []) 513 self.assertEqual(P('.').suffixes, []) 514 self.assertEqual(P('/').suffixes, []) 515 self.assertEqual(P('a/b').suffixes, []) 516 self.assertEqual(P('/a/b').suffixes, []) 517 self.assertEqual(P('/a/b/.').suffixes, []) 518 self.assertEqual(P('a/b.py').suffixes, ['.py']) 519 self.assertEqual(P('/a/b.py').suffixes, ['.py']) 520 self.assertEqual(P('a/.hgrc').suffixes, []) 521 self.assertEqual(P('/a/.hgrc').suffixes, []) 522 self.assertEqual(P('a/.hg.rc').suffixes, ['.rc']) 523 self.assertEqual(P('/a/.hg.rc').suffixes, ['.rc']) 524 self.assertEqual(P('a/b.tar.gz').suffixes, ['.tar', '.gz']) 525 self.assertEqual(P('/a/b.tar.gz').suffixes, ['.tar', '.gz']) 526 self.assertEqual(P('a/Some name. Ending with a dot.').suffixes, []) 527 self.assertEqual(P('/a/Some name. Ending with a dot.').suffixes, []) 528 529 def test_stem_common(self): 530 P = self.cls 531 self.assertEqual(P('').stem, '') 532 self.assertEqual(P('.').stem, '') 533 self.assertEqual(P('..').stem, '..') 534 self.assertEqual(P('/').stem, '') 535 self.assertEqual(P('a/b').stem, 'b') 536 self.assertEqual(P('a/b.py').stem, 'b') 537 self.assertEqual(P('a/.hgrc').stem, '.hgrc') 538 self.assertEqual(P('a/.hg.rc').stem, '.hg') 539 self.assertEqual(P('a/b.tar.gz').stem, 'b.tar') 540 self.assertEqual(P('a/Some name. Ending with a dot.').stem, 541 'Some name. Ending with a dot.') 542 543 def test_with_name_common(self): 544 P = self.cls 545 self.assertEqual(P('a/b').with_name('d.xml'), P('a/d.xml')) 546 self.assertEqual(P('/a/b').with_name('d.xml'), P('/a/d.xml')) 547 self.assertEqual(P('a/b.py').with_name('d.xml'), P('a/d.xml')) 548 self.assertEqual(P('/a/b.py').with_name('d.xml'), P('/a/d.xml')) 549 self.assertEqual(P('a/Dot ending.').with_name('d.xml'), P('a/d.xml')) 550 self.assertEqual(P('/a/Dot ending.').with_name('d.xml'), P('/a/d.xml')) 551 self.assertRaises(ValueError, P('').with_name, 'd.xml') 552 self.assertRaises(ValueError, P('.').with_name, 'd.xml') 553 self.assertRaises(ValueError, P('/').with_name, 'd.xml') 554 self.assertRaises(ValueError, P('a/b').with_name, '') 555 self.assertRaises(ValueError, P('a/b').with_name, '/c') 556 self.assertRaises(ValueError, P('a/b').with_name, 'c/') 557 self.assertRaises(ValueError, P('a/b').with_name, 'c/d') 558 559 def test_with_suffix_common(self): 560 P = self.cls 561 self.assertEqual(P('a/b').with_suffix('.gz'), P('a/b.gz')) 562 self.assertEqual(P('/a/b').with_suffix('.gz'), P('/a/b.gz')) 563 self.assertEqual(P('a/b.py').with_suffix('.gz'), P('a/b.gz')) 564 self.assertEqual(P('/a/b.py').with_suffix('.gz'), P('/a/b.gz')) 565 # Stripping suffix 566 self.assertEqual(P('a/b.py').with_suffix(''), P('a/b')) 567 self.assertEqual(P('/a/b').with_suffix(''), P('/a/b')) 568 # Path doesn't have a "filename" component 569 self.assertRaises(ValueError, P('').with_suffix, '.gz') 570 self.assertRaises(ValueError, P('.').with_suffix, '.gz') 571 self.assertRaises(ValueError, P('/').with_suffix, '.gz') 572 # Invalid suffix 573 self.assertRaises(ValueError, P('a/b').with_suffix, 'gz') 574 self.assertRaises(ValueError, P('a/b').with_suffix, '/') 575 self.assertRaises(ValueError, P('a/b').with_suffix, '.') 576 self.assertRaises(ValueError, P('a/b').with_suffix, '/.gz') 577 self.assertRaises(ValueError, P('a/b').with_suffix, 'c/d') 578 self.assertRaises(ValueError, P('a/b').with_suffix, '.c/.d') 579 self.assertRaises(ValueError, P('a/b').with_suffix, './.d') 580 self.assertRaises(ValueError, P('a/b').with_suffix, '.d/.') 581 self.assertRaises(ValueError, P('a/b').with_suffix, 582 (self.flavour.sep, '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 631class 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 707class 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 1144class 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 1175BASE = os.path.realpath(TESTFN) 1176join = lambda *x: os.path.join(BASE, *x) 1177rel_join = lambda *x: os.path.join(TESTFN, *x) 1178 1179only_nt = unittest.skipIf(os.name != 'nt', 1180 'test requires a Windows-compatible system') 1181only_posix = unittest.skipIf(os.name == 'nt', 1182 'test requires a POSIX-compatible system') 1183 1184@only_posix 1185class PosixPathAsPureTest(PurePosixPathTest): 1186 cls = pathlib.PosixPath 1187 1188@only_nt 1189class WindowsPathAsPureTest(PureWindowsPathTest): 1190 cls = pathlib.WindowsPath 1191 1192 def test_owner(self): 1193 P = self.cls 1194 with self.assertRaises(NotImplementedError): 1195 P('c:/').owner() 1196 1197 def test_group(self): 1198 P = self.cls 1199 with self.assertRaises(NotImplementedError): 1200 P('c:/').group() 1201 1202 1203class _BasePathTest(object): 1204 """Tests for the FS-accessing functionalities of the Path classes.""" 1205 1206 # (BASE) 1207 # | 1208 # |-- brokenLink -> non-existing 1209 # |-- dirA 1210 # | `-- linkC -> ../dirB 1211 # |-- dirB 1212 # | |-- fileB 1213 # | `-- linkD -> ../dirB 1214 # |-- dirC 1215 # | |-- dirD 1216 # | | `-- fileD 1217 # | `-- fileC 1218 # |-- dirE # No permissions 1219 # |-- fileA 1220 # |-- linkA -> fileA 1221 # `-- linkB -> dirB 1222 # 1223 1224 def setUp(self): 1225 def cleanup(): 1226 os.chmod(join('dirE'), 0o777) 1227 support.rmtree(BASE) 1228 self.addCleanup(cleanup) 1229 os.mkdir(BASE) 1230 os.mkdir(join('dirA')) 1231 os.mkdir(join('dirB')) 1232 os.mkdir(join('dirC')) 1233 os.mkdir(join('dirC', 'dirD')) 1234 os.mkdir(join('dirE')) 1235 with open(join('fileA'), 'wb') as f: 1236 f.write(b"this is file A\n") 1237 with open(join('dirB', 'fileB'), 'wb') as f: 1238 f.write(b"this is file B\n") 1239 with open(join('dirC', 'fileC'), 'wb') as f: 1240 f.write(b"this is file C\n") 1241 with open(join('dirC', 'dirD', 'fileD'), 'wb') as f: 1242 f.write(b"this is file D\n") 1243 os.chmod(join('dirE'), 0) 1244 if support.can_symlink(): 1245 # Relative symlinks 1246 os.symlink('fileA', join('linkA')) 1247 os.symlink('non-existing', join('brokenLink')) 1248 self.dirlink('dirB', join('linkB')) 1249 self.dirlink(os.path.join('..', 'dirB'), join('dirA', 'linkC')) 1250 # This one goes upwards, creating a loop 1251 self.dirlink(os.path.join('..', 'dirB'), join('dirB', 'linkD')) 1252 1253 if os.name == 'nt': 1254 # Workaround for http://bugs.python.org/issue13772 1255 def dirlink(self, src, dest): 1256 os.symlink(src, dest, target_is_directory=True) 1257 else: 1258 def dirlink(self, src, dest): 1259 os.symlink(src, dest) 1260 1261 def assertSame(self, path_a, path_b): 1262 self.assertTrue(os.path.samefile(str(path_a), str(path_b)), 1263 "%r and %r don't point to the same file" % 1264 (path_a, path_b)) 1265 1266 def assertFileNotFound(self, func, *args, **kwargs): 1267 with self.assertRaises(FileNotFoundError) as cm: 1268 func(*args, **kwargs) 1269 self.assertEqual(cm.exception.errno, errno.ENOENT) 1270 1271 def _test_cwd(self, p): 1272 q = self.cls(os.getcwd()) 1273 self.assertEqual(p, q) 1274 self.assertEqual(str(p), str(q)) 1275 self.assertIs(type(p), type(q)) 1276 self.assertTrue(p.is_absolute()) 1277 1278 def test_cwd(self): 1279 p = self.cls.cwd() 1280 self._test_cwd(p) 1281 1282 def _test_home(self, p): 1283 q = self.cls(os.path.expanduser('~')) 1284 self.assertEqual(p, q) 1285 self.assertEqual(str(p), str(q)) 1286 self.assertIs(type(p), type(q)) 1287 self.assertTrue(p.is_absolute()) 1288 1289 def test_home(self): 1290 p = self.cls.home() 1291 self._test_home(p) 1292 1293 def test_samefile(self): 1294 fileA_path = os.path.join(BASE, 'fileA') 1295 fileB_path = os.path.join(BASE, 'dirB', 'fileB') 1296 p = self.cls(fileA_path) 1297 pp = self.cls(fileA_path) 1298 q = self.cls(fileB_path) 1299 self.assertTrue(p.samefile(fileA_path)) 1300 self.assertTrue(p.samefile(pp)) 1301 self.assertFalse(p.samefile(fileB_path)) 1302 self.assertFalse(p.samefile(q)) 1303 # Test the non-existent file case 1304 non_existent = os.path.join(BASE, 'foo') 1305 r = self.cls(non_existent) 1306 self.assertRaises(FileNotFoundError, p.samefile, r) 1307 self.assertRaises(FileNotFoundError, p.samefile, non_existent) 1308 self.assertRaises(FileNotFoundError, r.samefile, p) 1309 self.assertRaises(FileNotFoundError, r.samefile, non_existent) 1310 self.assertRaises(FileNotFoundError, r.samefile, r) 1311 self.assertRaises(FileNotFoundError, r.samefile, non_existent) 1312 1313 def test_empty_path(self): 1314 # The empty path points to '.' 1315 p = self.cls('') 1316 self.assertEqual(p.stat(), os.stat('.')) 1317 1318 def test_expanduser_common(self): 1319 P = self.cls 1320 p = P('~') 1321 self.assertEqual(p.expanduser(), P(os.path.expanduser('~'))) 1322 p = P('foo') 1323 self.assertEqual(p.expanduser(), p) 1324 p = P('/~') 1325 self.assertEqual(p.expanduser(), p) 1326 p = P('../~') 1327 self.assertEqual(p.expanduser(), p) 1328 p = P(P('').absolute().anchor) / '~' 1329 self.assertEqual(p.expanduser(), p) 1330 1331 def test_exists(self): 1332 P = self.cls 1333 p = P(BASE) 1334 self.assertIs(True, p.exists()) 1335 self.assertIs(True, (p / 'dirA').exists()) 1336 self.assertIs(True, (p / 'fileA').exists()) 1337 self.assertIs(False, (p / 'fileA' / 'bah').exists()) 1338 if support.can_symlink(): 1339 self.assertIs(True, (p / 'linkA').exists()) 1340 self.assertIs(True, (p / 'linkB').exists()) 1341 self.assertIs(True, (p / 'linkB' / 'fileB').exists()) 1342 self.assertIs(False, (p / 'linkA' / 'bah').exists()) 1343 self.assertIs(False, (p / 'foo').exists()) 1344 self.assertIs(False, P('/xyzzy').exists()) 1345 1346 def test_open_common(self): 1347 p = self.cls(BASE) 1348 with (p / 'fileA').open('r') as f: 1349 self.assertIsInstance(f, io.TextIOBase) 1350 self.assertEqual(f.read(), "this is file A\n") 1351 with (p / 'fileA').open('rb') as f: 1352 self.assertIsInstance(f, io.BufferedIOBase) 1353 self.assertEqual(f.read().strip(), b"this is file A") 1354 with (p / 'fileA').open('rb', buffering=0) as f: 1355 self.assertIsInstance(f, io.RawIOBase) 1356 self.assertEqual(f.read().strip(), b"this is file A") 1357 1358 def test_read_write_bytes(self): 1359 p = self.cls(BASE) 1360 (p / 'fileA').write_bytes(b'abcdefg') 1361 self.assertEqual((p / 'fileA').read_bytes(), b'abcdefg') 1362 # check that trying to write str does not truncate the file 1363 self.assertRaises(TypeError, (p / 'fileA').write_bytes, 'somestr') 1364 self.assertEqual((p / 'fileA').read_bytes(), b'abcdefg') 1365 1366 def test_read_write_text(self): 1367 p = self.cls(BASE) 1368 (p / 'fileA').write_text('äbcdefg', encoding='latin-1') 1369 self.assertEqual((p / 'fileA').read_text( 1370 encoding='utf-8', errors='ignore'), 'bcdefg') 1371 # check that trying to write bytes does not truncate the file 1372 self.assertRaises(TypeError, (p / 'fileA').write_text, b'somebytes') 1373 self.assertEqual((p / 'fileA').read_text(encoding='latin-1'), 'äbcdefg') 1374 1375 def test_iterdir(self): 1376 P = self.cls 1377 p = P(BASE) 1378 it = p.iterdir() 1379 paths = set(it) 1380 expected = ['dirA', 'dirB', 'dirC', 'dirE', 'fileA'] 1381 if support.can_symlink(): 1382 expected += ['linkA', 'linkB', 'brokenLink'] 1383 self.assertEqual(paths, { P(BASE, q) for q in expected }) 1384 1385 @support.skip_unless_symlink 1386 def test_iterdir_symlink(self): 1387 # __iter__ on a symlink to a directory 1388 P = self.cls 1389 p = P(BASE, 'linkB') 1390 paths = set(p.iterdir()) 1391 expected = { P(BASE, 'linkB', q) for q in ['fileB', 'linkD'] } 1392 self.assertEqual(paths, expected) 1393 1394 def test_iterdir_nodir(self): 1395 # __iter__ on something that is not a directory 1396 p = self.cls(BASE, 'fileA') 1397 with self.assertRaises(OSError) as cm: 1398 next(p.iterdir()) 1399 # ENOENT or EINVAL under Windows, ENOTDIR otherwise 1400 # (see issue #12802) 1401 self.assertIn(cm.exception.errno, (errno.ENOTDIR, 1402 errno.ENOENT, errno.EINVAL)) 1403 1404 def test_glob_common(self): 1405 def _check(glob, expected): 1406 self.assertEqual(set(glob), { P(BASE, q) for q in expected }) 1407 P = self.cls 1408 p = P(BASE) 1409 it = p.glob("fileA") 1410 self.assertIsInstance(it, collections.abc.Iterator) 1411 _check(it, ["fileA"]) 1412 _check(p.glob("fileB"), []) 1413 _check(p.glob("dir*/file*"), ["dirB/fileB", "dirC/fileC"]) 1414 if not support.can_symlink(): 1415 _check(p.glob("*A"), ['dirA', 'fileA']) 1416 else: 1417 _check(p.glob("*A"), ['dirA', 'fileA', 'linkA']) 1418 if not support.can_symlink(): 1419 _check(p.glob("*B/*"), ['dirB/fileB']) 1420 else: 1421 _check(p.glob("*B/*"), ['dirB/fileB', 'dirB/linkD', 1422 'linkB/fileB', 'linkB/linkD']) 1423 if not support.can_symlink(): 1424 _check(p.glob("*/fileB"), ['dirB/fileB']) 1425 else: 1426 _check(p.glob("*/fileB"), ['dirB/fileB', 'linkB/fileB']) 1427 1428 def test_rglob_common(self): 1429 def _check(glob, expected): 1430 self.assertEqual(set(glob), { P(BASE, q) for q in expected }) 1431 P = self.cls 1432 p = P(BASE) 1433 it = p.rglob("fileA") 1434 self.assertIsInstance(it, collections.abc.Iterator) 1435 _check(it, ["fileA"]) 1436 _check(p.rglob("fileB"), ["dirB/fileB"]) 1437 _check(p.rglob("*/fileA"), []) 1438 if not support.can_symlink(): 1439 _check(p.rglob("*/fileB"), ["dirB/fileB"]) 1440 else: 1441 _check(p.rglob("*/fileB"), ["dirB/fileB", "dirB/linkD/fileB", 1442 "linkB/fileB", "dirA/linkC/fileB"]) 1443 _check(p.rglob("file*"), ["fileA", "dirB/fileB", 1444 "dirC/fileC", "dirC/dirD/fileD"]) 1445 p = P(BASE, "dirC") 1446 _check(p.rglob("file*"), ["dirC/fileC", "dirC/dirD/fileD"]) 1447 _check(p.rglob("*/*"), ["dirC/dirD/fileD"]) 1448 1449 @support.skip_unless_symlink 1450 def test_rglob_symlink_loop(self): 1451 # Don't get fooled by symlink loops (Issue #26012) 1452 P = self.cls 1453 p = P(BASE) 1454 given = set(p.rglob('*')) 1455 expect = {'brokenLink', 1456 'dirA', 'dirA/linkC', 1457 'dirB', 'dirB/fileB', 'dirB/linkD', 1458 'dirC', 'dirC/dirD', 'dirC/dirD/fileD', 'dirC/fileC', 1459 'dirE', 1460 'fileA', 1461 'linkA', 1462 'linkB', 1463 } 1464 self.assertEqual(given, {p / x for x in expect}) 1465 1466 def test_glob_dotdot(self): 1467 # ".." is not special in globs 1468 P = self.cls 1469 p = P(BASE) 1470 self.assertEqual(set(p.glob("..")), { P(BASE, "..") }) 1471 self.assertEqual(set(p.glob("dirA/../file*")), { P(BASE, "dirA/../fileA") }) 1472 self.assertEqual(set(p.glob("../xyzzy")), set()) 1473 1474 1475 def _check_resolve(self, p, expected, strict=True): 1476 q = p.resolve(strict) 1477 self.assertEqual(q, expected) 1478 1479 # this can be used to check both relative and absolute resolutions 1480 _check_resolve_relative = _check_resolve_absolute = _check_resolve 1481 1482 @support.skip_unless_symlink 1483 def test_resolve_common(self): 1484 P = self.cls 1485 p = P(BASE, 'foo') 1486 with self.assertRaises(OSError) as cm: 1487 p.resolve(strict=True) 1488 self.assertEqual(cm.exception.errno, errno.ENOENT) 1489 # Non-strict 1490 self.assertEqual(str(p.resolve(strict=False)), 1491 os.path.join(BASE, 'foo')) 1492 p = P(BASE, 'foo', 'in', 'spam') 1493 self.assertEqual(str(p.resolve(strict=False)), 1494 os.path.join(BASE, 'foo', 'in', 'spam')) 1495 p = P(BASE, '..', 'foo', 'in', 'spam') 1496 self.assertEqual(str(p.resolve(strict=False)), 1497 os.path.abspath(os.path.join('foo', 'in', 'spam'))) 1498 # These are all relative symlinks 1499 p = P(BASE, 'dirB', 'fileB') 1500 self._check_resolve_relative(p, p) 1501 p = P(BASE, 'linkA') 1502 self._check_resolve_relative(p, P(BASE, 'fileA')) 1503 p = P(BASE, 'dirA', 'linkC', 'fileB') 1504 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB')) 1505 p = P(BASE, 'dirB', 'linkD', 'fileB') 1506 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB')) 1507 # Non-strict 1508 p = P(BASE, 'dirA', 'linkC', 'fileB', 'foo', 'in', 'spam') 1509 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB', 'foo', 'in', 1510 'spam'), False) 1511 p = P(BASE, 'dirA', 'linkC', '..', 'foo', 'in', 'spam') 1512 if os.name == 'nt': 1513 # In Windows, if linkY points to dirB, 'dirA\linkY\..' 1514 # resolves to 'dirA' without resolving linkY first. 1515 self._check_resolve_relative(p, P(BASE, 'dirA', 'foo', 'in', 1516 'spam'), False) 1517 else: 1518 # In Posix, if linkY points to dirB, 'dirA/linkY/..' 1519 # resolves to 'dirB/..' first before resolving to parent of dirB. 1520 self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False) 1521 # Now create absolute symlinks 1522 d = support._longpath(tempfile.mkdtemp(suffix='-dirD', dir=os.getcwd())) 1523 self.addCleanup(support.rmtree, d) 1524 os.symlink(os.path.join(d), join('dirA', 'linkX')) 1525 os.symlink(join('dirB'), os.path.join(d, 'linkY')) 1526 p = P(BASE, 'dirA', 'linkX', 'linkY', 'fileB') 1527 self._check_resolve_absolute(p, P(BASE, 'dirB', 'fileB')) 1528 # Non-strict 1529 p = P(BASE, 'dirA', 'linkX', 'linkY', 'foo', 'in', 'spam') 1530 self._check_resolve_relative(p, P(BASE, 'dirB', 'foo', 'in', 'spam'), 1531 False) 1532 p = P(BASE, 'dirA', 'linkX', 'linkY', '..', 'foo', 'in', 'spam') 1533 if os.name == 'nt': 1534 # In Windows, if linkY points to dirB, 'dirA\linkY\..' 1535 # resolves to 'dirA' without resolving linkY first. 1536 self._check_resolve_relative(p, P(d, 'foo', 'in', 'spam'), False) 1537 else: 1538 # In Posix, if linkY points to dirB, 'dirA/linkY/..' 1539 # resolves to 'dirB/..' first before resolving to parent of dirB. 1540 self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False) 1541 1542 @support.skip_unless_symlink 1543 def test_resolve_dot(self): 1544 # See https://bitbucket.org/pitrou/pathlib/issue/9/pathresolve-fails-on-complex-symlinks 1545 p = self.cls(BASE) 1546 self.dirlink('.', join('0')) 1547 self.dirlink(os.path.join('0', '0'), join('1')) 1548 self.dirlink(os.path.join('1', '1'), join('2')) 1549 q = p / '2' 1550 self.assertEqual(q.resolve(strict=True), p) 1551 r = q / '3' / '4' 1552 self.assertRaises(FileNotFoundError, r.resolve, strict=True) 1553 # Non-strict 1554 self.assertEqual(r.resolve(strict=False), p / '3' / '4') 1555 1556 def test_with(self): 1557 p = self.cls(BASE) 1558 it = p.iterdir() 1559 it2 = p.iterdir() 1560 next(it2) 1561 with p: 1562 pass 1563 # I/O operation on closed path 1564 self.assertRaises(ValueError, next, it) 1565 self.assertRaises(ValueError, next, it2) 1566 self.assertRaises(ValueError, p.open) 1567 self.assertRaises(ValueError, p.resolve) 1568 self.assertRaises(ValueError, p.absolute) 1569 self.assertRaises(ValueError, p.__enter__) 1570 1571 def test_chmod(self): 1572 p = self.cls(BASE) / 'fileA' 1573 mode = p.stat().st_mode 1574 # Clear writable bit 1575 new_mode = mode & ~0o222 1576 p.chmod(new_mode) 1577 self.assertEqual(p.stat().st_mode, new_mode) 1578 # Set writable bit 1579 new_mode = mode | 0o222 1580 p.chmod(new_mode) 1581 self.assertEqual(p.stat().st_mode, new_mode) 1582 1583 # XXX also need a test for lchmod 1584 1585 def test_stat(self): 1586 p = self.cls(BASE) / 'fileA' 1587 st = p.stat() 1588 self.assertEqual(p.stat(), st) 1589 # Change file mode by flipping write bit 1590 p.chmod(st.st_mode ^ 0o222) 1591 self.addCleanup(p.chmod, st.st_mode) 1592 self.assertNotEqual(p.stat(), st) 1593 1594 @support.skip_unless_symlink 1595 def test_lstat(self): 1596 p = self.cls(BASE)/ 'linkA' 1597 st = p.stat() 1598 self.assertNotEqual(st, p.lstat()) 1599 1600 def test_lstat_nosymlink(self): 1601 p = self.cls(BASE) / 'fileA' 1602 st = p.stat() 1603 self.assertEqual(st, p.lstat()) 1604 1605 @unittest.skipUnless(pwd, "the pwd module is needed for this test") 1606 def test_owner(self): 1607 p = self.cls(BASE) / 'fileA' 1608 uid = p.stat().st_uid 1609 try: 1610 name = pwd.getpwuid(uid).pw_name 1611 except KeyError: 1612 self.skipTest( 1613 "user %d doesn't have an entry in the system database" % uid) 1614 self.assertEqual(name, p.owner()) 1615 1616 @unittest.skipUnless(grp, "the grp module is needed for this test") 1617 def test_group(self): 1618 p = self.cls(BASE) / 'fileA' 1619 gid = p.stat().st_gid 1620 try: 1621 name = grp.getgrgid(gid).gr_name 1622 except KeyError: 1623 self.skipTest( 1624 "group %d doesn't have an entry in the system database" % gid) 1625 self.assertEqual(name, p.group()) 1626 1627 def test_unlink(self): 1628 p = self.cls(BASE) / 'fileA' 1629 p.unlink() 1630 self.assertFileNotFound(p.stat) 1631 self.assertFileNotFound(p.unlink) 1632 1633 def test_rmdir(self): 1634 p = self.cls(BASE) / 'dirA' 1635 for q in p.iterdir(): 1636 q.unlink() 1637 p.rmdir() 1638 self.assertFileNotFound(p.stat) 1639 self.assertFileNotFound(p.unlink) 1640 1641 def test_rename(self): 1642 P = self.cls(BASE) 1643 p = P / 'fileA' 1644 size = p.stat().st_size 1645 # Renaming to another path 1646 q = P / 'dirA' / 'fileAA' 1647 p.rename(q) 1648 self.assertEqual(q.stat().st_size, size) 1649 self.assertFileNotFound(p.stat) 1650 # Renaming to a str of a relative path 1651 r = rel_join('fileAAA') 1652 q.rename(r) 1653 self.assertEqual(os.stat(r).st_size, size) 1654 self.assertFileNotFound(q.stat) 1655 1656 def test_replace(self): 1657 P = self.cls(BASE) 1658 p = P / 'fileA' 1659 size = p.stat().st_size 1660 # Replacing a non-existing path 1661 q = P / 'dirA' / 'fileAA' 1662 p.replace(q) 1663 self.assertEqual(q.stat().st_size, size) 1664 self.assertFileNotFound(p.stat) 1665 # Replacing another (existing) path 1666 r = rel_join('dirB', 'fileB') 1667 q.replace(r) 1668 self.assertEqual(os.stat(r).st_size, size) 1669 self.assertFileNotFound(q.stat) 1670 1671 def test_touch_common(self): 1672 P = self.cls(BASE) 1673 p = P / 'newfileA' 1674 self.assertFalse(p.exists()) 1675 p.touch() 1676 self.assertTrue(p.exists()) 1677 st = p.stat() 1678 old_mtime = st.st_mtime 1679 old_mtime_ns = st.st_mtime_ns 1680 # Rewind the mtime sufficiently far in the past to work around 1681 # filesystem-specific timestamp granularity. 1682 os.utime(str(p), (old_mtime - 10, old_mtime - 10)) 1683 # The file mtime should be refreshed by calling touch() again 1684 p.touch() 1685 st = p.stat() 1686 self.assertGreaterEqual(st.st_mtime_ns, old_mtime_ns) 1687 self.assertGreaterEqual(st.st_mtime, old_mtime) 1688 # Now with exist_ok=False 1689 p = P / 'newfileB' 1690 self.assertFalse(p.exists()) 1691 p.touch(mode=0o700, exist_ok=False) 1692 self.assertTrue(p.exists()) 1693 self.assertRaises(OSError, p.touch, exist_ok=False) 1694 1695 def test_touch_nochange(self): 1696 P = self.cls(BASE) 1697 p = P / 'fileA' 1698 p.touch() 1699 with p.open('rb') as f: 1700 self.assertEqual(f.read().strip(), b"this is file A") 1701 1702 def test_mkdir(self): 1703 P = self.cls(BASE) 1704 p = P / 'newdirA' 1705 self.assertFalse(p.exists()) 1706 p.mkdir() 1707 self.assertTrue(p.exists()) 1708 self.assertTrue(p.is_dir()) 1709 with self.assertRaises(OSError) as cm: 1710 p.mkdir() 1711 self.assertEqual(cm.exception.errno, errno.EEXIST) 1712 1713 def test_mkdir_parents(self): 1714 # Creating a chain of directories 1715 p = self.cls(BASE, 'newdirB', 'newdirC') 1716 self.assertFalse(p.exists()) 1717 with self.assertRaises(OSError) as cm: 1718 p.mkdir() 1719 self.assertEqual(cm.exception.errno, errno.ENOENT) 1720 p.mkdir(parents=True) 1721 self.assertTrue(p.exists()) 1722 self.assertTrue(p.is_dir()) 1723 with self.assertRaises(OSError) as cm: 1724 p.mkdir(parents=True) 1725 self.assertEqual(cm.exception.errno, errno.EEXIST) 1726 # test `mode` arg 1727 mode = stat.S_IMODE(p.stat().st_mode) # default mode 1728 p = self.cls(BASE, 'newdirD', 'newdirE') 1729 p.mkdir(0o555, parents=True) 1730 self.assertTrue(p.exists()) 1731 self.assertTrue(p.is_dir()) 1732 if os.name != 'nt': 1733 # the directory's permissions follow the mode argument 1734 self.assertEqual(stat.S_IMODE(p.stat().st_mode), 0o7555 & mode) 1735 # the parent's permissions follow the default process settings 1736 self.assertEqual(stat.S_IMODE(p.parent.stat().st_mode), mode) 1737 1738 def test_mkdir_exist_ok(self): 1739 p = self.cls(BASE, 'dirB') 1740 st_ctime_first = p.stat().st_ctime 1741 self.assertTrue(p.exists()) 1742 self.assertTrue(p.is_dir()) 1743 with self.assertRaises(FileExistsError) as cm: 1744 p.mkdir() 1745 self.assertEqual(cm.exception.errno, errno.EEXIST) 1746 p.mkdir(exist_ok=True) 1747 self.assertTrue(p.exists()) 1748 self.assertEqual(p.stat().st_ctime, st_ctime_first) 1749 1750 def test_mkdir_exist_ok_with_parent(self): 1751 p = self.cls(BASE, 'dirC') 1752 self.assertTrue(p.exists()) 1753 with self.assertRaises(FileExistsError) as cm: 1754 p.mkdir() 1755 self.assertEqual(cm.exception.errno, errno.EEXIST) 1756 p = p / 'newdirC' 1757 p.mkdir(parents=True) 1758 st_ctime_first = p.stat().st_ctime 1759 self.assertTrue(p.exists()) 1760 with self.assertRaises(FileExistsError) as cm: 1761 p.mkdir(parents=True) 1762 self.assertEqual(cm.exception.errno, errno.EEXIST) 1763 p.mkdir(parents=True, exist_ok=True) 1764 self.assertTrue(p.exists()) 1765 self.assertEqual(p.stat().st_ctime, st_ctime_first) 1766 1767 def test_mkdir_exist_ok_root(self): 1768 # Issue #25803: A drive root could raise PermissionError on Windows 1769 self.cls('/').resolve().mkdir(exist_ok=True) 1770 self.cls('/').resolve().mkdir(parents=True, exist_ok=True) 1771 1772 @only_nt # XXX: not sure how to test this on POSIX 1773 def test_mkdir_with_unknown_drive(self): 1774 for d in 'ZYXWVUTSRQPONMLKJIHGFEDCBA': 1775 p = self.cls(d + ':\\') 1776 if not p.is_dir(): 1777 break 1778 else: 1779 self.skipTest("cannot find a drive that doesn't exist") 1780 with self.assertRaises(OSError): 1781 (p / 'child' / 'path').mkdir(parents=True) 1782 1783 def test_mkdir_with_child_file(self): 1784 p = self.cls(BASE, 'dirB', 'fileB') 1785 self.assertTrue(p.exists()) 1786 # An exception is raised when the last path component is an existing 1787 # regular file, regardless of whether exist_ok is true or not. 1788 with self.assertRaises(FileExistsError) as cm: 1789 p.mkdir(parents=True) 1790 self.assertEqual(cm.exception.errno, errno.EEXIST) 1791 with self.assertRaises(FileExistsError) as cm: 1792 p.mkdir(parents=True, exist_ok=True) 1793 self.assertEqual(cm.exception.errno, errno.EEXIST) 1794 1795 def test_mkdir_no_parents_file(self): 1796 p = self.cls(BASE, 'fileA') 1797 self.assertTrue(p.exists()) 1798 # An exception is raised when the last path component is an existing 1799 # regular file, regardless of whether exist_ok is true or not. 1800 with self.assertRaises(FileExistsError) as cm: 1801 p.mkdir() 1802 self.assertEqual(cm.exception.errno, errno.EEXIST) 1803 with self.assertRaises(FileExistsError) as cm: 1804 p.mkdir(exist_ok=True) 1805 self.assertEqual(cm.exception.errno, errno.EEXIST) 1806 1807 def test_mkdir_concurrent_parent_creation(self): 1808 for pattern_num in range(32): 1809 p = self.cls(BASE, 'dirCPC%d' % pattern_num) 1810 self.assertFalse(p.exists()) 1811 1812 def my_mkdir(path, mode=0o777): 1813 path = str(path) 1814 # Emulate another process that would create the directory 1815 # just before we try to create it ourselves. We do it 1816 # in all possible pattern combinations, assuming that this 1817 # function is called at most 5 times (dirCPC/dir1/dir2, 1818 # dirCPC/dir1, dirCPC, dirCPC/dir1, dirCPC/dir1/dir2). 1819 if pattern.pop(): 1820 os.mkdir(path, mode) # from another process 1821 concurrently_created.add(path) 1822 os.mkdir(path, mode) # our real call 1823 1824 pattern = [bool(pattern_num & (1 << n)) for n in range(5)] 1825 concurrently_created = set() 1826 p12 = p / 'dir1' / 'dir2' 1827 try: 1828 with mock.patch("pathlib._normal_accessor.mkdir", my_mkdir): 1829 p12.mkdir(parents=True, exist_ok=False) 1830 except FileExistsError: 1831 self.assertIn(str(p12), concurrently_created) 1832 else: 1833 self.assertNotIn(str(p12), concurrently_created) 1834 self.assertTrue(p.exists()) 1835 1836 @support.skip_unless_symlink 1837 def test_symlink_to(self): 1838 P = self.cls(BASE) 1839 target = P / 'fileA' 1840 # Symlinking a path target 1841 link = P / 'dirA' / 'linkAA' 1842 link.symlink_to(target) 1843 self.assertEqual(link.stat(), target.stat()) 1844 self.assertNotEqual(link.lstat(), target.stat()) 1845 # Symlinking a str target 1846 link = P / 'dirA' / 'linkAAA' 1847 link.symlink_to(str(target)) 1848 self.assertEqual(link.stat(), target.stat()) 1849 self.assertNotEqual(link.lstat(), target.stat()) 1850 self.assertFalse(link.is_dir()) 1851 # Symlinking to a directory 1852 target = P / 'dirB' 1853 link = P / 'dirA' / 'linkAAAA' 1854 link.symlink_to(target, target_is_directory=True) 1855 self.assertEqual(link.stat(), target.stat()) 1856 self.assertNotEqual(link.lstat(), target.stat()) 1857 self.assertTrue(link.is_dir()) 1858 self.assertTrue(list(link.iterdir())) 1859 1860 def test_is_dir(self): 1861 P = self.cls(BASE) 1862 self.assertTrue((P / 'dirA').is_dir()) 1863 self.assertFalse((P / 'fileA').is_dir()) 1864 self.assertFalse((P / 'non-existing').is_dir()) 1865 self.assertFalse((P / 'fileA' / 'bah').is_dir()) 1866 if support.can_symlink(): 1867 self.assertFalse((P / 'linkA').is_dir()) 1868 self.assertTrue((P / 'linkB').is_dir()) 1869 self.assertFalse((P/ 'brokenLink').is_dir()) 1870 1871 def test_is_file(self): 1872 P = self.cls(BASE) 1873 self.assertTrue((P / 'fileA').is_file()) 1874 self.assertFalse((P / 'dirA').is_file()) 1875 self.assertFalse((P / 'non-existing').is_file()) 1876 self.assertFalse((P / 'fileA' / 'bah').is_file()) 1877 if support.can_symlink(): 1878 self.assertTrue((P / 'linkA').is_file()) 1879 self.assertFalse((P / 'linkB').is_file()) 1880 self.assertFalse((P/ 'brokenLink').is_file()) 1881 1882 @only_posix 1883 def test_is_mount(self): 1884 P = self.cls(BASE) 1885 R = self.cls('/') # TODO: Work out windows 1886 self.assertFalse((P / 'fileA').is_mount()) 1887 self.assertFalse((P / 'dirA').is_mount()) 1888 self.assertFalse((P / 'non-existing').is_mount()) 1889 self.assertFalse((P / 'fileA' / 'bah').is_mount()) 1890 self.assertTrue(R.is_mount()) 1891 if support.can_symlink(): 1892 self.assertFalse((P / 'linkA').is_mount()) 1893 1894 def test_is_symlink(self): 1895 P = self.cls(BASE) 1896 self.assertFalse((P / 'fileA').is_symlink()) 1897 self.assertFalse((P / 'dirA').is_symlink()) 1898 self.assertFalse((P / 'non-existing').is_symlink()) 1899 self.assertFalse((P / 'fileA' / 'bah').is_symlink()) 1900 if support.can_symlink(): 1901 self.assertTrue((P / 'linkA').is_symlink()) 1902 self.assertTrue((P / 'linkB').is_symlink()) 1903 self.assertTrue((P/ 'brokenLink').is_symlink()) 1904 1905 def test_is_fifo_false(self): 1906 P = self.cls(BASE) 1907 self.assertFalse((P / 'fileA').is_fifo()) 1908 self.assertFalse((P / 'dirA').is_fifo()) 1909 self.assertFalse((P / 'non-existing').is_fifo()) 1910 self.assertFalse((P / 'fileA' / 'bah').is_fifo()) 1911 1912 @unittest.skipUnless(hasattr(os, "mkfifo"), "os.mkfifo() required") 1913 def test_is_fifo_true(self): 1914 P = self.cls(BASE, 'myfifo') 1915 try: 1916 os.mkfifo(str(P)) 1917 except PermissionError as e: 1918 self.skipTest('os.mkfifo(): %s' % e) 1919 self.assertTrue(P.is_fifo()) 1920 self.assertFalse(P.is_socket()) 1921 self.assertFalse(P.is_file()) 1922 1923 def test_is_socket_false(self): 1924 P = self.cls(BASE) 1925 self.assertFalse((P / 'fileA').is_socket()) 1926 self.assertFalse((P / 'dirA').is_socket()) 1927 self.assertFalse((P / 'non-existing').is_socket()) 1928 self.assertFalse((P / 'fileA' / 'bah').is_socket()) 1929 1930 @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") 1931 def test_is_socket_true(self): 1932 P = self.cls(BASE, 'mysock') 1933 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 1934 self.addCleanup(sock.close) 1935 try: 1936 sock.bind(str(P)) 1937 except OSError as e: 1938 if (isinstance(e, PermissionError) or 1939 "AF_UNIX path too long" in str(e)): 1940 self.skipTest("cannot bind Unix socket: " + str(e)) 1941 self.assertTrue(P.is_socket()) 1942 self.assertFalse(P.is_fifo()) 1943 self.assertFalse(P.is_file()) 1944 1945 def test_is_block_device_false(self): 1946 P = self.cls(BASE) 1947 self.assertFalse((P / 'fileA').is_block_device()) 1948 self.assertFalse((P / 'dirA').is_block_device()) 1949 self.assertFalse((P / 'non-existing').is_block_device()) 1950 self.assertFalse((P / 'fileA' / 'bah').is_block_device()) 1951 1952 def test_is_char_device_false(self): 1953 P = self.cls(BASE) 1954 self.assertFalse((P / 'fileA').is_char_device()) 1955 self.assertFalse((P / 'dirA').is_char_device()) 1956 self.assertFalse((P / 'non-existing').is_char_device()) 1957 self.assertFalse((P / 'fileA' / 'bah').is_char_device()) 1958 1959 def test_is_char_device_true(self): 1960 # Under Unix, /dev/null should generally be a char device 1961 P = self.cls('/dev/null') 1962 if not P.exists(): 1963 self.skipTest("/dev/null required") 1964 self.assertTrue(P.is_char_device()) 1965 self.assertFalse(P.is_block_device()) 1966 self.assertFalse(P.is_file()) 1967 1968 def test_pickling_common(self): 1969 p = self.cls(BASE, 'fileA') 1970 for proto in range(0, pickle.HIGHEST_PROTOCOL + 1): 1971 dumped = pickle.dumps(p, proto) 1972 pp = pickle.loads(dumped) 1973 self.assertEqual(pp.stat(), p.stat()) 1974 1975 def test_parts_interning(self): 1976 P = self.cls 1977 p = P('/usr/bin/foo') 1978 q = P('/usr/local/bin') 1979 # 'usr' 1980 self.assertIs(p.parts[1], q.parts[1]) 1981 # 'bin' 1982 self.assertIs(p.parts[2], q.parts[3]) 1983 1984 def _check_complex_symlinks(self, link0_target): 1985 # Test solving a non-looping chain of symlinks (issue #19887) 1986 P = self.cls(BASE) 1987 self.dirlink(os.path.join('link0', 'link0'), join('link1')) 1988 self.dirlink(os.path.join('link1', 'link1'), join('link2')) 1989 self.dirlink(os.path.join('link2', 'link2'), join('link3')) 1990 self.dirlink(link0_target, join('link0')) 1991 1992 # Resolve absolute paths 1993 p = (P / 'link0').resolve() 1994 self.assertEqual(p, P) 1995 self.assertEqual(str(p), BASE) 1996 p = (P / 'link1').resolve() 1997 self.assertEqual(p, P) 1998 self.assertEqual(str(p), BASE) 1999 p = (P / 'link2').resolve() 2000 self.assertEqual(p, P) 2001 self.assertEqual(str(p), BASE) 2002 p = (P / 'link3').resolve() 2003 self.assertEqual(p, P) 2004 self.assertEqual(str(p), BASE) 2005 2006 # Resolve relative paths 2007 old_path = os.getcwd() 2008 os.chdir(BASE) 2009 try: 2010 p = self.cls('link0').resolve() 2011 self.assertEqual(p, P) 2012 self.assertEqual(str(p), BASE) 2013 p = self.cls('link1').resolve() 2014 self.assertEqual(p, P) 2015 self.assertEqual(str(p), BASE) 2016 p = self.cls('link2').resolve() 2017 self.assertEqual(p, P) 2018 self.assertEqual(str(p), BASE) 2019 p = self.cls('link3').resolve() 2020 self.assertEqual(p, P) 2021 self.assertEqual(str(p), BASE) 2022 finally: 2023 os.chdir(old_path) 2024 2025 @support.skip_unless_symlink 2026 def test_complex_symlinks_absolute(self): 2027 self._check_complex_symlinks(BASE) 2028 2029 @support.skip_unless_symlink 2030 def test_complex_symlinks_relative(self): 2031 self._check_complex_symlinks('.') 2032 2033 @support.skip_unless_symlink 2034 def test_complex_symlinks_relative_dot_dot(self): 2035 self._check_complex_symlinks(os.path.join('dirA', '..')) 2036 2037 2038class PathTest(_BasePathTest, unittest.TestCase): 2039 cls = pathlib.Path 2040 2041 def test_concrete_class(self): 2042 p = self.cls('a') 2043 self.assertIs(type(p), 2044 pathlib.WindowsPath if os.name == 'nt' else pathlib.PosixPath) 2045 2046 def test_unsupported_flavour(self): 2047 if os.name == 'nt': 2048 self.assertRaises(NotImplementedError, pathlib.PosixPath) 2049 else: 2050 self.assertRaises(NotImplementedError, pathlib.WindowsPath) 2051 2052 def test_glob_empty_pattern(self): 2053 p = self.cls() 2054 with self.assertRaisesRegex(ValueError, 'Unacceptable pattern'): 2055 list(p.glob('')) 2056 2057 2058@only_posix 2059class PosixPathTest(_BasePathTest, unittest.TestCase): 2060 cls = pathlib.PosixPath 2061 2062 def _check_symlink_loop(self, *args, strict=True): 2063 path = self.cls(*args) 2064 with self.assertRaises(RuntimeError): 2065 print(path.resolve(strict)) 2066 2067 def test_open_mode(self): 2068 old_mask = os.umask(0) 2069 self.addCleanup(os.umask, old_mask) 2070 p = self.cls(BASE) 2071 with (p / 'new_file').open('wb'): 2072 pass 2073 st = os.stat(join('new_file')) 2074 self.assertEqual(stat.S_IMODE(st.st_mode), 0o666) 2075 os.umask(0o022) 2076 with (p / 'other_new_file').open('wb'): 2077 pass 2078 st = os.stat(join('other_new_file')) 2079 self.assertEqual(stat.S_IMODE(st.st_mode), 0o644) 2080 2081 def test_touch_mode(self): 2082 old_mask = os.umask(0) 2083 self.addCleanup(os.umask, old_mask) 2084 p = self.cls(BASE) 2085 (p / 'new_file').touch() 2086 st = os.stat(join('new_file')) 2087 self.assertEqual(stat.S_IMODE(st.st_mode), 0o666) 2088 os.umask(0o022) 2089 (p / 'other_new_file').touch() 2090 st = os.stat(join('other_new_file')) 2091 self.assertEqual(stat.S_IMODE(st.st_mode), 0o644) 2092 (p / 'masked_new_file').touch(mode=0o750) 2093 st = os.stat(join('masked_new_file')) 2094 self.assertEqual(stat.S_IMODE(st.st_mode), 0o750) 2095 2096 @support.skip_unless_symlink 2097 def test_resolve_loop(self): 2098 # Loops with relative symlinks 2099 os.symlink('linkX/inside', join('linkX')) 2100 self._check_symlink_loop(BASE, 'linkX') 2101 os.symlink('linkY', join('linkY')) 2102 self._check_symlink_loop(BASE, 'linkY') 2103 os.symlink('linkZ/../linkZ', join('linkZ')) 2104 self._check_symlink_loop(BASE, 'linkZ') 2105 # Non-strict 2106 self._check_symlink_loop(BASE, 'linkZ', 'foo', strict=False) 2107 # Loops with absolute symlinks 2108 os.symlink(join('linkU/inside'), join('linkU')) 2109 self._check_symlink_loop(BASE, 'linkU') 2110 os.symlink(join('linkV'), join('linkV')) 2111 self._check_symlink_loop(BASE, 'linkV') 2112 os.symlink(join('linkW/../linkW'), join('linkW')) 2113 self._check_symlink_loop(BASE, 'linkW') 2114 # Non-strict 2115 self._check_symlink_loop(BASE, 'linkW', 'foo', strict=False) 2116 2117 def test_glob(self): 2118 P = self.cls 2119 p = P(BASE) 2120 given = set(p.glob("FILEa")) 2121 expect = set() if not support.fs_is_case_insensitive(BASE) else given 2122 self.assertEqual(given, expect) 2123 self.assertEqual(set(p.glob("FILEa*")), set()) 2124 2125 def test_rglob(self): 2126 P = self.cls 2127 p = P(BASE, "dirC") 2128 given = set(p.rglob("FILEd")) 2129 expect = set() if not support.fs_is_case_insensitive(BASE) else given 2130 self.assertEqual(given, expect) 2131 self.assertEqual(set(p.rglob("FILEd*")), set()) 2132 2133 @unittest.skipUnless(hasattr(pwd, 'getpwall'), 2134 'pwd module does not expose getpwall()') 2135 def test_expanduser(self): 2136 P = self.cls 2137 support.import_module('pwd') 2138 import pwd 2139 pwdent = pwd.getpwuid(os.getuid()) 2140 username = pwdent.pw_name 2141 userhome = pwdent.pw_dir.rstrip('/') or '/' 2142 # find arbitrary different user (if exists) 2143 for pwdent in pwd.getpwall(): 2144 othername = pwdent.pw_name 2145 otherhome = pwdent.pw_dir.rstrip('/') 2146 if othername != username and otherhome: 2147 break 2148 else: 2149 othername = username 2150 otherhome = userhome 2151 2152 p1 = P('~/Documents') 2153 p2 = P('~' + username + '/Documents') 2154 p3 = P('~' + othername + '/Documents') 2155 p4 = P('../~' + username + '/Documents') 2156 p5 = P('/~' + username + '/Documents') 2157 p6 = P('') 2158 p7 = P('~fakeuser/Documents') 2159 2160 with support.EnvironmentVarGuard() as env: 2161 env.pop('HOME', None) 2162 2163 self.assertEqual(p1.expanduser(), P(userhome) / 'Documents') 2164 self.assertEqual(p2.expanduser(), P(userhome) / 'Documents') 2165 self.assertEqual(p3.expanduser(), P(otherhome) / 'Documents') 2166 self.assertEqual(p4.expanduser(), p4) 2167 self.assertEqual(p5.expanduser(), p5) 2168 self.assertEqual(p6.expanduser(), p6) 2169 self.assertRaises(RuntimeError, p7.expanduser) 2170 2171 env['HOME'] = '/tmp' 2172 self.assertEqual(p1.expanduser(), P('/tmp/Documents')) 2173 self.assertEqual(p2.expanduser(), P(userhome) / 'Documents') 2174 self.assertEqual(p3.expanduser(), P(otherhome) / 'Documents') 2175 self.assertEqual(p4.expanduser(), p4) 2176 self.assertEqual(p5.expanduser(), p5) 2177 self.assertEqual(p6.expanduser(), p6) 2178 self.assertRaises(RuntimeError, p7.expanduser) 2179 2180 @unittest.skipIf(sys.platform != "darwin", 2181 "Bad file descriptor in /dev/fd affects only macOS") 2182 def test_handling_bad_descriptor(self): 2183 try: 2184 file_descriptors = list(pathlib.Path('/dev/fd').rglob("*"))[3:] 2185 if not file_descriptors: 2186 self.skipTest("no file descriptors - issue was not reproduced") 2187 # Checking all file descriptors because there is no guarantee 2188 # which one will fail. 2189 for f in file_descriptors: 2190 f.exists() 2191 f.is_dir() 2192 f.is_file() 2193 f.is_symlink() 2194 f.is_block_device() 2195 f.is_char_device() 2196 f.is_fifo() 2197 f.is_socket() 2198 except OSError as e: 2199 if e.errno == errno.EBADF: 2200 self.fail("Bad file descriptor not handled.") 2201 raise 2202 2203 2204@only_nt 2205class WindowsPathTest(_BasePathTest, unittest.TestCase): 2206 cls = pathlib.WindowsPath 2207 2208 def test_glob(self): 2209 P = self.cls 2210 p = P(BASE) 2211 self.assertEqual(set(p.glob("FILEa")), { P(BASE, "fileA") }) 2212 2213 def test_rglob(self): 2214 P = self.cls 2215 p = P(BASE, "dirC") 2216 self.assertEqual(set(p.rglob("FILEd")), { P(BASE, "dirC/dirD/fileD") }) 2217 2218 def test_expanduser(self): 2219 P = self.cls 2220 with support.EnvironmentVarGuard() as env: 2221 env.pop('HOME', None) 2222 env.pop('USERPROFILE', None) 2223 env.pop('HOMEPATH', None) 2224 env.pop('HOMEDRIVE', None) 2225 env['USERNAME'] = 'alice' 2226 2227 # test that the path returns unchanged 2228 p1 = P('~/My Documents') 2229 p2 = P('~alice/My Documents') 2230 p3 = P('~bob/My Documents') 2231 p4 = P('/~/My Documents') 2232 p5 = P('d:~/My Documents') 2233 p6 = P('') 2234 self.assertRaises(RuntimeError, p1.expanduser) 2235 self.assertRaises(RuntimeError, p2.expanduser) 2236 self.assertRaises(RuntimeError, p3.expanduser) 2237 self.assertEqual(p4.expanduser(), p4) 2238 self.assertEqual(p5.expanduser(), p5) 2239 self.assertEqual(p6.expanduser(), p6) 2240 2241 def check(): 2242 env.pop('USERNAME', None) 2243 self.assertEqual(p1.expanduser(), 2244 P('C:/Users/alice/My Documents')) 2245 self.assertRaises(KeyError, p2.expanduser) 2246 env['USERNAME'] = 'alice' 2247 self.assertEqual(p2.expanduser(), 2248 P('C:/Users/alice/My Documents')) 2249 self.assertEqual(p3.expanduser(), 2250 P('C:/Users/bob/My Documents')) 2251 self.assertEqual(p4.expanduser(), p4) 2252 self.assertEqual(p5.expanduser(), p5) 2253 self.assertEqual(p6.expanduser(), p6) 2254 2255 # test the first lookup key in the env vars 2256 env['HOME'] = 'C:\\Users\\alice' 2257 check() 2258 2259 # test that HOMEPATH is available instead 2260 env.pop('HOME', None) 2261 env['HOMEPATH'] = 'C:\\Users\\alice' 2262 check() 2263 2264 env['HOMEDRIVE'] = 'C:\\' 2265 env['HOMEPATH'] = 'Users\\alice' 2266 check() 2267 2268 env.pop('HOMEDRIVE', None) 2269 env.pop('HOMEPATH', None) 2270 env['USERPROFILE'] = 'C:\\Users\\alice' 2271 check() 2272 2273 2274if __name__ == "__main__": 2275 unittest.main() 2276