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 # Multi-part glob-style pattern. 349 self.assertFalse(P('/a/b/c.py').match('/**/*.py')) 350 self.assertTrue(P('/a/b/c.py').match('/a/**/*.py')) 351 352 def test_ordering_common(self): 353 # Ordering is tuple-alike. 354 def assertLess(a, b): 355 self.assertLess(a, b) 356 self.assertGreater(b, a) 357 P = self.cls 358 a = P('a') 359 b = P('a/b') 360 c = P('abc') 361 d = P('b') 362 assertLess(a, b) 363 assertLess(a, c) 364 assertLess(a, d) 365 assertLess(b, c) 366 assertLess(c, d) 367 P = self.cls 368 a = P('/a') 369 b = P('/a/b') 370 c = P('/abc') 371 d = P('/b') 372 assertLess(a, b) 373 assertLess(a, c) 374 assertLess(a, d) 375 assertLess(b, c) 376 assertLess(c, d) 377 with self.assertRaises(TypeError): 378 P() < {} 379 380 def test_parts_common(self): 381 # `parts` returns a tuple. 382 sep = self.sep 383 P = self.cls 384 p = P('a/b') 385 parts = p.parts 386 self.assertEqual(parts, ('a', 'b')) 387 # The object gets reused. 388 self.assertIs(parts, p.parts) 389 # When the path is absolute, the anchor is a separate part. 390 p = P('/a/b') 391 parts = p.parts 392 self.assertEqual(parts, (sep, 'a', 'b')) 393 394 def test_fspath_common(self): 395 P = self.cls 396 p = P('a/b') 397 self._check_str(p.__fspath__(), ('a/b',)) 398 self._check_str(os.fspath(p), ('a/b',)) 399 400 def test_equivalences(self): 401 for k, tuples in self.equivalences.items(): 402 canon = k.replace('/', self.sep) 403 posix = k.replace(self.sep, '/') 404 if canon != posix: 405 tuples = tuples + [ 406 tuple(part.replace('/', self.sep) for part in t) 407 for t in tuples 408 ] 409 tuples.append((posix, )) 410 pcanon = self.cls(canon) 411 for t in tuples: 412 p = self.cls(*t) 413 self.assertEqual(p, pcanon, "failed with args {}".format(t)) 414 self.assertEqual(hash(p), hash(pcanon)) 415 self.assertEqual(str(p), canon) 416 self.assertEqual(p.as_posix(), posix) 417 418 def test_parent_common(self): 419 # Relative 420 P = self.cls 421 p = P('a/b/c') 422 self.assertEqual(p.parent, P('a/b')) 423 self.assertEqual(p.parent.parent, P('a')) 424 self.assertEqual(p.parent.parent.parent, P()) 425 self.assertEqual(p.parent.parent.parent.parent, P()) 426 # Anchored 427 p = P('/a/b/c') 428 self.assertEqual(p.parent, P('/a/b')) 429 self.assertEqual(p.parent.parent, P('/a')) 430 self.assertEqual(p.parent.parent.parent, P('/')) 431 self.assertEqual(p.parent.parent.parent.parent, P('/')) 432 433 def test_parents_common(self): 434 # Relative 435 P = self.cls 436 p = P('a/b/c') 437 par = p.parents 438 self.assertEqual(len(par), 3) 439 self.assertEqual(par[0], P('a/b')) 440 self.assertEqual(par[1], P('a')) 441 self.assertEqual(par[2], P('.')) 442 self.assertEqual(list(par), [P('a/b'), P('a'), P('.')]) 443 with self.assertRaises(IndexError): 444 par[-1] 445 with self.assertRaises(IndexError): 446 par[3] 447 with self.assertRaises(TypeError): 448 par[0] = p 449 # Anchored 450 p = P('/a/b/c') 451 par = p.parents 452 self.assertEqual(len(par), 3) 453 self.assertEqual(par[0], P('/a/b')) 454 self.assertEqual(par[1], P('/a')) 455 self.assertEqual(par[2], P('/')) 456 self.assertEqual(list(par), [P('/a/b'), P('/a'), P('/')]) 457 with self.assertRaises(IndexError): 458 par[3] 459 460 def test_drive_common(self): 461 P = self.cls 462 self.assertEqual(P('a/b').drive, '') 463 self.assertEqual(P('/a/b').drive, '') 464 self.assertEqual(P('').drive, '') 465 466 def test_root_common(self): 467 P = self.cls 468 sep = self.sep 469 self.assertEqual(P('').root, '') 470 self.assertEqual(P('a/b').root, '') 471 self.assertEqual(P('/').root, sep) 472 self.assertEqual(P('/a/b').root, sep) 473 474 def test_anchor_common(self): 475 P = self.cls 476 sep = self.sep 477 self.assertEqual(P('').anchor, '') 478 self.assertEqual(P('a/b').anchor, '') 479 self.assertEqual(P('/').anchor, sep) 480 self.assertEqual(P('/a/b').anchor, sep) 481 482 def test_name_common(self): 483 P = self.cls 484 self.assertEqual(P('').name, '') 485 self.assertEqual(P('.').name, '') 486 self.assertEqual(P('/').name, '') 487 self.assertEqual(P('a/b').name, 'b') 488 self.assertEqual(P('/a/b').name, 'b') 489 self.assertEqual(P('/a/b/.').name, 'b') 490 self.assertEqual(P('a/b.py').name, 'b.py') 491 self.assertEqual(P('/a/b.py').name, 'b.py') 492 493 def test_suffix_common(self): 494 P = self.cls 495 self.assertEqual(P('').suffix, '') 496 self.assertEqual(P('.').suffix, '') 497 self.assertEqual(P('..').suffix, '') 498 self.assertEqual(P('/').suffix, '') 499 self.assertEqual(P('a/b').suffix, '') 500 self.assertEqual(P('/a/b').suffix, '') 501 self.assertEqual(P('/a/b/.').suffix, '') 502 self.assertEqual(P('a/b.py').suffix, '.py') 503 self.assertEqual(P('/a/b.py').suffix, '.py') 504 self.assertEqual(P('a/.hgrc').suffix, '') 505 self.assertEqual(P('/a/.hgrc').suffix, '') 506 self.assertEqual(P('a/.hg.rc').suffix, '.rc') 507 self.assertEqual(P('/a/.hg.rc').suffix, '.rc') 508 self.assertEqual(P('a/b.tar.gz').suffix, '.gz') 509 self.assertEqual(P('/a/b.tar.gz').suffix, '.gz') 510 self.assertEqual(P('a/Some name. Ending with a dot.').suffix, '') 511 self.assertEqual(P('/a/Some name. Ending with a dot.').suffix, '') 512 513 def test_suffixes_common(self): 514 P = self.cls 515 self.assertEqual(P('').suffixes, []) 516 self.assertEqual(P('.').suffixes, []) 517 self.assertEqual(P('/').suffixes, []) 518 self.assertEqual(P('a/b').suffixes, []) 519 self.assertEqual(P('/a/b').suffixes, []) 520 self.assertEqual(P('/a/b/.').suffixes, []) 521 self.assertEqual(P('a/b.py').suffixes, ['.py']) 522 self.assertEqual(P('/a/b.py').suffixes, ['.py']) 523 self.assertEqual(P('a/.hgrc').suffixes, []) 524 self.assertEqual(P('/a/.hgrc').suffixes, []) 525 self.assertEqual(P('a/.hg.rc').suffixes, ['.rc']) 526 self.assertEqual(P('/a/.hg.rc').suffixes, ['.rc']) 527 self.assertEqual(P('a/b.tar.gz').suffixes, ['.tar', '.gz']) 528 self.assertEqual(P('/a/b.tar.gz').suffixes, ['.tar', '.gz']) 529 self.assertEqual(P('a/Some name. Ending with a dot.').suffixes, []) 530 self.assertEqual(P('/a/Some name. Ending with a dot.').suffixes, []) 531 532 def test_stem_common(self): 533 P = self.cls 534 self.assertEqual(P('').stem, '') 535 self.assertEqual(P('.').stem, '') 536 self.assertEqual(P('..').stem, '..') 537 self.assertEqual(P('/').stem, '') 538 self.assertEqual(P('a/b').stem, 'b') 539 self.assertEqual(P('a/b.py').stem, 'b') 540 self.assertEqual(P('a/.hgrc').stem, '.hgrc') 541 self.assertEqual(P('a/.hg.rc').stem, '.hg') 542 self.assertEqual(P('a/b.tar.gz').stem, 'b.tar') 543 self.assertEqual(P('a/Some name. Ending with a dot.').stem, 544 'Some name. Ending with a dot.') 545 546 def test_with_name_common(self): 547 P = self.cls 548 self.assertEqual(P('a/b').with_name('d.xml'), P('a/d.xml')) 549 self.assertEqual(P('/a/b').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/b.py').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.assertEqual(P('/a/Dot ending.').with_name('d.xml'), P('/a/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('/').with_name, 'd.xml') 557 self.assertRaises(ValueError, P('a/b').with_name, '') 558 self.assertRaises(ValueError, P('a/b').with_name, '/c') 559 self.assertRaises(ValueError, P('a/b').with_name, 'c/') 560 self.assertRaises(ValueError, P('a/b').with_name, 'c/d') 561 562 def test_with_suffix_common(self): 563 P = self.cls 564 self.assertEqual(P('a/b').with_suffix('.gz'), P('a/b.gz')) 565 self.assertEqual(P('/a/b').with_suffix('.gz'), P('/a/b.gz')) 566 self.assertEqual(P('a/b.py').with_suffix('.gz'), P('a/b.gz')) 567 self.assertEqual(P('/a/b.py').with_suffix('.gz'), P('/a/b.gz')) 568 # Stripping suffix. 569 self.assertEqual(P('a/b.py').with_suffix(''), P('a/b')) 570 self.assertEqual(P('/a/b').with_suffix(''), P('/a/b')) 571 # Path doesn't have a "filename" component. 572 self.assertRaises(ValueError, P('').with_suffix, '.gz') 573 self.assertRaises(ValueError, P('.').with_suffix, '.gz') 574 self.assertRaises(ValueError, P('/').with_suffix, '.gz') 575 # Invalid suffix. 576 self.assertRaises(ValueError, P('a/b').with_suffix, 'gz') 577 self.assertRaises(ValueError, P('a/b').with_suffix, '/') 578 self.assertRaises(ValueError, P('a/b').with_suffix, '.') 579 self.assertRaises(ValueError, P('a/b').with_suffix, '/.gz') 580 self.assertRaises(ValueError, P('a/b').with_suffix, 'c/d') 581 self.assertRaises(ValueError, P('a/b').with_suffix, '.c/.d') 582 self.assertRaises(ValueError, P('a/b').with_suffix, './.d') 583 self.assertRaises(ValueError, P('a/b').with_suffix, '.d/.') 584 self.assertRaises(ValueError, P('a/b').with_suffix, 585 (self.flavour.sep, 'd')) 586 587 def test_relative_to_common(self): 588 P = self.cls 589 p = P('a/b') 590 self.assertRaises(TypeError, p.relative_to) 591 self.assertRaises(TypeError, p.relative_to, b'a') 592 self.assertEqual(p.relative_to(P()), P('a/b')) 593 self.assertEqual(p.relative_to(''), P('a/b')) 594 self.assertEqual(p.relative_to(P('a')), P('b')) 595 self.assertEqual(p.relative_to('a'), P('b')) 596 self.assertEqual(p.relative_to('a/'), P('b')) 597 self.assertEqual(p.relative_to(P('a/b')), P()) 598 self.assertEqual(p.relative_to('a/b'), P()) 599 # With several args. 600 self.assertEqual(p.relative_to('a', 'b'), P()) 601 # Unrelated paths. 602 self.assertRaises(ValueError, p.relative_to, P('c')) 603 self.assertRaises(ValueError, p.relative_to, P('a/b/c')) 604 self.assertRaises(ValueError, p.relative_to, P('a/c')) 605 self.assertRaises(ValueError, p.relative_to, P('/a')) 606 p = P('/a/b') 607 self.assertEqual(p.relative_to(P('/')), P('a/b')) 608 self.assertEqual(p.relative_to('/'), P('a/b')) 609 self.assertEqual(p.relative_to(P('/a')), P('b')) 610 self.assertEqual(p.relative_to('/a'), P('b')) 611 self.assertEqual(p.relative_to('/a/'), P('b')) 612 self.assertEqual(p.relative_to(P('/a/b')), P()) 613 self.assertEqual(p.relative_to('/a/b'), P()) 614 # Unrelated paths. 615 self.assertRaises(ValueError, p.relative_to, P('/c')) 616 self.assertRaises(ValueError, p.relative_to, P('/a/b/c')) 617 self.assertRaises(ValueError, p.relative_to, P('/a/c')) 618 self.assertRaises(ValueError, p.relative_to, P()) 619 self.assertRaises(ValueError, p.relative_to, '') 620 self.assertRaises(ValueError, p.relative_to, P('a')) 621 622 def test_pickling_common(self): 623 P = self.cls 624 p = P('/a/b') 625 for proto in range(0, pickle.HIGHEST_PROTOCOL + 1): 626 dumped = pickle.dumps(p, proto) 627 pp = pickle.loads(dumped) 628 self.assertIs(pp.__class__, p.__class__) 629 self.assertEqual(pp, p) 630 self.assertEqual(hash(pp), hash(p)) 631 self.assertEqual(str(pp), str(p)) 632 633 634class PurePosixPathTest(_BasePurePathTest, unittest.TestCase): 635 cls = pathlib.PurePosixPath 636 637 def test_root(self): 638 P = self.cls 639 self.assertEqual(P('/a/b').root, '/') 640 self.assertEqual(P('///a/b').root, '/') 641 # POSIX special case for two leading slashes. 642 self.assertEqual(P('//a/b').root, '//') 643 644 def test_eq(self): 645 P = self.cls 646 self.assertNotEqual(P('a/b'), P('A/b')) 647 self.assertEqual(P('/a'), P('///a')) 648 self.assertNotEqual(P('/a'), P('//a')) 649 650 def test_as_uri(self): 651 P = self.cls 652 self.assertEqual(P('/').as_uri(), 'file:///') 653 self.assertEqual(P('/a/b.c').as_uri(), 'file:///a/b.c') 654 self.assertEqual(P('/a/b%#c').as_uri(), 'file:///a/b%25%23c') 655 656 def test_as_uri_non_ascii(self): 657 from urllib.parse import quote_from_bytes 658 P = self.cls 659 try: 660 os.fsencode('\xe9') 661 except UnicodeEncodeError: 662 self.skipTest("\\xe9 cannot be encoded to the filesystem encoding") 663 self.assertEqual(P('/a/b\xe9').as_uri(), 664 'file:///a/b' + quote_from_bytes(os.fsencode('\xe9'))) 665 666 def test_match(self): 667 P = self.cls 668 self.assertFalse(P('A.py').match('a.PY')) 669 670 def test_is_absolute(self): 671 P = self.cls 672 self.assertFalse(P().is_absolute()) 673 self.assertFalse(P('a').is_absolute()) 674 self.assertFalse(P('a/b/').is_absolute()) 675 self.assertTrue(P('/').is_absolute()) 676 self.assertTrue(P('/a').is_absolute()) 677 self.assertTrue(P('/a/b/').is_absolute()) 678 self.assertTrue(P('//a').is_absolute()) 679 self.assertTrue(P('//a/b').is_absolute()) 680 681 def test_is_reserved(self): 682 P = self.cls 683 self.assertIs(False, P('').is_reserved()) 684 self.assertIs(False, P('/').is_reserved()) 685 self.assertIs(False, P('/foo/bar').is_reserved()) 686 self.assertIs(False, P('/dev/con/PRN/NUL').is_reserved()) 687 688 def test_join(self): 689 P = self.cls 690 p = P('//a') 691 pp = p.joinpath('b') 692 self.assertEqual(pp, P('//a/b')) 693 pp = P('/a').joinpath('//c') 694 self.assertEqual(pp, P('//c')) 695 pp = P('//a').joinpath('/c') 696 self.assertEqual(pp, P('/c')) 697 698 def test_div(self): 699 # Basically the same as joinpath(). 700 P = self.cls 701 p = P('//a') 702 pp = p / 'b' 703 self.assertEqual(pp, P('//a/b')) 704 pp = P('/a') / '//c' 705 self.assertEqual(pp, P('//c')) 706 pp = P('//a') / '/c' 707 self.assertEqual(pp, P('/c')) 708 709 710class PureWindowsPathTest(_BasePurePathTest, unittest.TestCase): 711 cls = pathlib.PureWindowsPath 712 713 equivalences = _BasePurePathTest.equivalences.copy() 714 equivalences.update({ 715 'c:a': [ ('c:', 'a'), ('c:', 'a/'), ('/', 'c:', 'a') ], 716 'c:/a': [ 717 ('c:/', 'a'), ('c:', '/', 'a'), ('c:', '/a'), 718 ('/z', 'c:/', 'a'), ('//x/y', 'c:/', 'a'), 719 ], 720 '//a/b/': [ ('//a/b',) ], 721 '//a/b/c': [ 722 ('//a/b', 'c'), ('//a/b/', 'c'), 723 ], 724 }) 725 726 def test_str(self): 727 p = self.cls('a/b/c') 728 self.assertEqual(str(p), 'a\\b\\c') 729 p = self.cls('c:/a/b/c') 730 self.assertEqual(str(p), 'c:\\a\\b\\c') 731 p = self.cls('//a/b') 732 self.assertEqual(str(p), '\\\\a\\b\\') 733 p = self.cls('//a/b/c') 734 self.assertEqual(str(p), '\\\\a\\b\\c') 735 p = self.cls('//a/b/c/d') 736 self.assertEqual(str(p), '\\\\a\\b\\c\\d') 737 738 def test_str_subclass(self): 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('c:\\') 743 self._check_str_subclass('c:\\a') 744 self._check_str_subclass('c:\\a\\b.txt') 745 self._check_str_subclass('\\\\some\\share') 746 self._check_str_subclass('\\\\some\\share\\a') 747 self._check_str_subclass('\\\\some\\share\\a\\b.txt') 748 749 def test_eq(self): 750 P = self.cls 751 self.assertEqual(P('c:a/b'), P('c:a/b')) 752 self.assertEqual(P('c:a/b'), P('c:', 'a', 'b')) 753 self.assertNotEqual(P('c:a/b'), P('d:a/b')) 754 self.assertNotEqual(P('c:a/b'), P('c:/a/b')) 755 self.assertNotEqual(P('/a/b'), P('c:/a/b')) 756 # Case-insensitivity. 757 self.assertEqual(P('a/B'), P('A/b')) 758 self.assertEqual(P('C:a/B'), P('c:A/b')) 759 self.assertEqual(P('//Some/SHARE/a/B'), P('//somE/share/A/b')) 760 761 def test_as_uri(self): 762 P = self.cls 763 with self.assertRaises(ValueError): 764 P('/a/b').as_uri() 765 with self.assertRaises(ValueError): 766 P('c:a/b').as_uri() 767 self.assertEqual(P('c:/').as_uri(), 'file:///c:/') 768 self.assertEqual(P('c:/a/b.c').as_uri(), 'file:///c:/a/b.c') 769 self.assertEqual(P('c:/a/b%#c').as_uri(), 'file:///c:/a/b%25%23c') 770 self.assertEqual(P('c:/a/b\xe9').as_uri(), 'file:///c:/a/b%C3%A9') 771 self.assertEqual(P('//some/share/').as_uri(), 'file://some/share/') 772 self.assertEqual(P('//some/share/a/b.c').as_uri(), 773 'file://some/share/a/b.c') 774 self.assertEqual(P('//some/share/a/b%#c\xe9').as_uri(), 775 'file://some/share/a/b%25%23c%C3%A9') 776 777 def test_match_common(self): 778 P = self.cls 779 # Absolute patterns. 780 self.assertTrue(P('c:/b.py').match('/*.py')) 781 self.assertTrue(P('c:/b.py').match('c:*.py')) 782 self.assertTrue(P('c:/b.py').match('c:/*.py')) 783 self.assertFalse(P('d:/b.py').match('c:/*.py')) # wrong drive 784 self.assertFalse(P('b.py').match('/*.py')) 785 self.assertFalse(P('b.py').match('c:*.py')) 786 self.assertFalse(P('b.py').match('c:/*.py')) 787 self.assertFalse(P('c:b.py').match('/*.py')) 788 self.assertFalse(P('c:b.py').match('c:/*.py')) 789 self.assertFalse(P('/b.py').match('c:*.py')) 790 self.assertFalse(P('/b.py').match('c:/*.py')) 791 # UNC patterns. 792 self.assertTrue(P('//some/share/a.py').match('/*.py')) 793 self.assertTrue(P('//some/share/a.py').match('//some/share/*.py')) 794 self.assertFalse(P('//other/share/a.py').match('//some/share/*.py')) 795 self.assertFalse(P('//some/share/a/b.py').match('//some/share/*.py')) 796 # Case-insensitivity. 797 self.assertTrue(P('B.py').match('b.PY')) 798 self.assertTrue(P('c:/a/B.Py').match('C:/A/*.pY')) 799 self.assertTrue(P('//Some/Share/B.Py').match('//somE/sharE/*.pY')) 800 801 def test_ordering_common(self): 802 # Case-insensitivity. 803 def assertOrderedEqual(a, b): 804 self.assertLessEqual(a, b) 805 self.assertGreaterEqual(b, a) 806 P = self.cls 807 p = P('c:A/b') 808 q = P('C:a/B') 809 assertOrderedEqual(p, q) 810 self.assertFalse(p < q) 811 self.assertFalse(p > q) 812 p = P('//some/Share/A/b') 813 q = P('//Some/SHARE/a/B') 814 assertOrderedEqual(p, q) 815 self.assertFalse(p < q) 816 self.assertFalse(p > q) 817 818 def test_parts(self): 819 P = self.cls 820 p = P('c:a/b') 821 parts = p.parts 822 self.assertEqual(parts, ('c:', 'a', 'b')) 823 p = P('c:/a/b') 824 parts = p.parts 825 self.assertEqual(parts, ('c:\\', 'a', 'b')) 826 p = P('//a/b/c/d') 827 parts = p.parts 828 self.assertEqual(parts, ('\\\\a\\b\\', 'c', 'd')) 829 830 def test_parent(self): 831 # Anchored 832 P = self.cls 833 p = P('z:a/b/c') 834 self.assertEqual(p.parent, P('z:a/b')) 835 self.assertEqual(p.parent.parent, P('z:a')) 836 self.assertEqual(p.parent.parent.parent, P('z:')) 837 self.assertEqual(p.parent.parent.parent.parent, P('z:')) 838 p = P('z:/a/b/c') 839 self.assertEqual(p.parent, P('z:/a/b')) 840 self.assertEqual(p.parent.parent, P('z:/a')) 841 self.assertEqual(p.parent.parent.parent, P('z:/')) 842 self.assertEqual(p.parent.parent.parent.parent, P('z:/')) 843 p = P('//a/b/c/d') 844 self.assertEqual(p.parent, P('//a/b/c')) 845 self.assertEqual(p.parent.parent, P('//a/b')) 846 self.assertEqual(p.parent.parent.parent, P('//a/b')) 847 848 def test_parents(self): 849 # Anchored 850 P = self.cls 851 p = P('z:a/b/') 852 par = p.parents 853 self.assertEqual(len(par), 2) 854 self.assertEqual(par[0], P('z:a')) 855 self.assertEqual(par[1], P('z:')) 856 self.assertEqual(list(par), [P('z:a'), P('z:')]) 857 with self.assertRaises(IndexError): 858 par[2] 859 p = P('z:/a/b/') 860 par = p.parents 861 self.assertEqual(len(par), 2) 862 self.assertEqual(par[0], P('z:/a')) 863 self.assertEqual(par[1], P('z:/')) 864 self.assertEqual(list(par), [P('z:/a'), P('z:/')]) 865 with self.assertRaises(IndexError): 866 par[2] 867 p = P('//a/b/c/d') 868 par = p.parents 869 self.assertEqual(len(par), 2) 870 self.assertEqual(par[0], P('//a/b/c')) 871 self.assertEqual(par[1], P('//a/b')) 872 self.assertEqual(list(par), [P('//a/b/c'), P('//a/b')]) 873 with self.assertRaises(IndexError): 874 par[2] 875 876 def test_drive(self): 877 P = self.cls 878 self.assertEqual(P('c:').drive, 'c:') 879 self.assertEqual(P('c:a/b').drive, 'c:') 880 self.assertEqual(P('c:/').drive, 'c:') 881 self.assertEqual(P('c:/a/b/').drive, 'c:') 882 self.assertEqual(P('//a/b').drive, '\\\\a\\b') 883 self.assertEqual(P('//a/b/').drive, '\\\\a\\b') 884 self.assertEqual(P('//a/b/c/d').drive, '\\\\a\\b') 885 886 def test_root(self): 887 P = self.cls 888 self.assertEqual(P('c:').root, '') 889 self.assertEqual(P('c:a/b').root, '') 890 self.assertEqual(P('c:/').root, '\\') 891 self.assertEqual(P('c:/a/b/').root, '\\') 892 self.assertEqual(P('//a/b').root, '\\') 893 self.assertEqual(P('//a/b/').root, '\\') 894 self.assertEqual(P('//a/b/c/d').root, '\\') 895 896 def test_anchor(self): 897 P = self.cls 898 self.assertEqual(P('c:').anchor, 'c:') 899 self.assertEqual(P('c:a/b').anchor, 'c:') 900 self.assertEqual(P('c:/').anchor, 'c:\\') 901 self.assertEqual(P('c:/a/b/').anchor, 'c:\\') 902 self.assertEqual(P('//a/b').anchor, '\\\\a\\b\\') 903 self.assertEqual(P('//a/b/').anchor, '\\\\a\\b\\') 904 self.assertEqual(P('//a/b/c/d').anchor, '\\\\a\\b\\') 905 906 def test_name(self): 907 P = self.cls 908 self.assertEqual(P('c:').name, '') 909 self.assertEqual(P('c:/').name, '') 910 self.assertEqual(P('c:a/b').name, 'b') 911 self.assertEqual(P('c:/a/b').name, 'b') 912 self.assertEqual(P('c:a/b.py').name, 'b.py') 913 self.assertEqual(P('c:/a/b.py').name, 'b.py') 914 self.assertEqual(P('//My.py/Share.php').name, '') 915 self.assertEqual(P('//My.py/Share.php/a/b').name, 'b') 916 917 def test_suffix(self): 918 P = self.cls 919 self.assertEqual(P('c:').suffix, '') 920 self.assertEqual(P('c:/').suffix, '') 921 self.assertEqual(P('c:a/b').suffix, '') 922 self.assertEqual(P('c:/a/b').suffix, '') 923 self.assertEqual(P('c:a/b.py').suffix, '.py') 924 self.assertEqual(P('c:/a/b.py').suffix, '.py') 925 self.assertEqual(P('c:a/.hgrc').suffix, '') 926 self.assertEqual(P('c:/a/.hgrc').suffix, '') 927 self.assertEqual(P('c:a/.hg.rc').suffix, '.rc') 928 self.assertEqual(P('c:/a/.hg.rc').suffix, '.rc') 929 self.assertEqual(P('c:a/b.tar.gz').suffix, '.gz') 930 self.assertEqual(P('c:/a/b.tar.gz').suffix, '.gz') 931 self.assertEqual(P('c:a/Some name. Ending with a dot.').suffix, '') 932 self.assertEqual(P('c:/a/Some name. Ending with a dot.').suffix, '') 933 self.assertEqual(P('//My.py/Share.php').suffix, '') 934 self.assertEqual(P('//My.py/Share.php/a/b').suffix, '') 935 936 def test_suffixes(self): 937 P = self.cls 938 self.assertEqual(P('c:').suffixes, []) 939 self.assertEqual(P('c:/').suffixes, []) 940 self.assertEqual(P('c:a/b').suffixes, []) 941 self.assertEqual(P('c:/a/b').suffixes, []) 942 self.assertEqual(P('c:a/b.py').suffixes, ['.py']) 943 self.assertEqual(P('c:/a/b.py').suffixes, ['.py']) 944 self.assertEqual(P('c:a/.hgrc').suffixes, []) 945 self.assertEqual(P('c:/a/.hgrc').suffixes, []) 946 self.assertEqual(P('c:a/.hg.rc').suffixes, ['.rc']) 947 self.assertEqual(P('c:/a/.hg.rc').suffixes, ['.rc']) 948 self.assertEqual(P('c:a/b.tar.gz').suffixes, ['.tar', '.gz']) 949 self.assertEqual(P('c:/a/b.tar.gz').suffixes, ['.tar', '.gz']) 950 self.assertEqual(P('//My.py/Share.php').suffixes, []) 951 self.assertEqual(P('//My.py/Share.php/a/b').suffixes, []) 952 self.assertEqual(P('c:a/Some name. Ending with a dot.').suffixes, []) 953 self.assertEqual(P('c:/a/Some name. Ending with a dot.').suffixes, []) 954 955 def test_stem(self): 956 P = self.cls 957 self.assertEqual(P('c:').stem, '') 958 self.assertEqual(P('c:.').stem, '') 959 self.assertEqual(P('c:..').stem, '..') 960 self.assertEqual(P('c:/').stem, '') 961 self.assertEqual(P('c:a/b').stem, 'b') 962 self.assertEqual(P('c:a/b.py').stem, 'b') 963 self.assertEqual(P('c:a/.hgrc').stem, '.hgrc') 964 self.assertEqual(P('c:a/.hg.rc').stem, '.hg') 965 self.assertEqual(P('c:a/b.tar.gz').stem, 'b.tar') 966 self.assertEqual(P('c:a/Some name. Ending with a dot.').stem, 967 'Some name. Ending with a dot.') 968 969 def test_with_name(self): 970 P = self.cls 971 self.assertEqual(P('c:a/b').with_name('d.xml'), P('c:a/d.xml')) 972 self.assertEqual(P('c:/a/b').with_name('d.xml'), P('c:/a/d.xml')) 973 self.assertEqual(P('c:a/Dot ending.').with_name('d.xml'), P('c:a/d.xml')) 974 self.assertEqual(P('c:/a/Dot ending.').with_name('d.xml'), P('c:/a/d.xml')) 975 self.assertRaises(ValueError, P('c:').with_name, 'd.xml') 976 self.assertRaises(ValueError, P('c:/').with_name, 'd.xml') 977 self.assertRaises(ValueError, P('//My/Share').with_name, 'd.xml') 978 self.assertRaises(ValueError, P('c:a/b').with_name, 'd:') 979 self.assertRaises(ValueError, P('c:a/b').with_name, 'd:e') 980 self.assertRaises(ValueError, P('c:a/b').with_name, 'd:/e') 981 self.assertRaises(ValueError, P('c:a/b').with_name, '//My/Share') 982 983 def test_with_suffix(self): 984 P = self.cls 985 self.assertEqual(P('c:a/b').with_suffix('.gz'), P('c:a/b.gz')) 986 self.assertEqual(P('c:/a/b').with_suffix('.gz'), P('c:/a/b.gz')) 987 self.assertEqual(P('c:a/b.py').with_suffix('.gz'), P('c:a/b.gz')) 988 self.assertEqual(P('c:/a/b.py').with_suffix('.gz'), P('c:/a/b.gz')) 989 # Path doesn't have a "filename" component. 990 self.assertRaises(ValueError, P('').with_suffix, '.gz') 991 self.assertRaises(ValueError, P('.').with_suffix, '.gz') 992 self.assertRaises(ValueError, P('/').with_suffix, '.gz') 993 self.assertRaises(ValueError, P('//My/Share').with_suffix, '.gz') 994 # Invalid suffix. 995 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'gz') 996 self.assertRaises(ValueError, P('c:a/b').with_suffix, '/') 997 self.assertRaises(ValueError, P('c:a/b').with_suffix, '\\') 998 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c:') 999 self.assertRaises(ValueError, P('c:a/b').with_suffix, '/.gz') 1000 self.assertRaises(ValueError, P('c:a/b').with_suffix, '\\.gz') 1001 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c:.gz') 1002 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c/d') 1003 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c\\d') 1004 self.assertRaises(ValueError, P('c:a/b').with_suffix, '.c/d') 1005 self.assertRaises(ValueError, P('c:a/b').with_suffix, '.c\\d') 1006 1007 def test_relative_to(self): 1008 P = self.cls 1009 p = P('C:Foo/Bar') 1010 self.assertEqual(p.relative_to(P('c:')), P('Foo/Bar')) 1011 self.assertEqual(p.relative_to('c:'), P('Foo/Bar')) 1012 self.assertEqual(p.relative_to(P('c:foO')), P('Bar')) 1013 self.assertEqual(p.relative_to('c:foO'), P('Bar')) 1014 self.assertEqual(p.relative_to('c:foO/'), P('Bar')) 1015 self.assertEqual(p.relative_to(P('c:foO/baR')), P()) 1016 self.assertEqual(p.relative_to('c:foO/baR'), P()) 1017 # Unrelated paths. 1018 self.assertRaises(ValueError, p.relative_to, P()) 1019 self.assertRaises(ValueError, p.relative_to, '') 1020 self.assertRaises(ValueError, p.relative_to, P('d:')) 1021 self.assertRaises(ValueError, p.relative_to, P('/')) 1022 self.assertRaises(ValueError, p.relative_to, P('Foo')) 1023 self.assertRaises(ValueError, p.relative_to, P('/Foo')) 1024 self.assertRaises(ValueError, p.relative_to, P('C:/Foo')) 1025 self.assertRaises(ValueError, p.relative_to, P('C:Foo/Bar/Baz')) 1026 self.assertRaises(ValueError, p.relative_to, P('C:Foo/Baz')) 1027 p = P('C:/Foo/Bar') 1028 self.assertEqual(p.relative_to(P('c:')), P('/Foo/Bar')) 1029 self.assertEqual(p.relative_to('c:'), P('/Foo/Bar')) 1030 self.assertEqual(str(p.relative_to(P('c:'))), '\\Foo\\Bar') 1031 self.assertEqual(str(p.relative_to('c:')), '\\Foo\\Bar') 1032 self.assertEqual(p.relative_to(P('c:/')), P('Foo/Bar')) 1033 self.assertEqual(p.relative_to('c:/'), P('Foo/Bar')) 1034 self.assertEqual(p.relative_to(P('c:/foO')), P('Bar')) 1035 self.assertEqual(p.relative_to('c:/foO'), P('Bar')) 1036 self.assertEqual(p.relative_to('c:/foO/'), P('Bar')) 1037 self.assertEqual(p.relative_to(P('c:/foO/baR')), P()) 1038 self.assertEqual(p.relative_to('c:/foO/baR'), P()) 1039 # Unrelated paths. 1040 self.assertRaises(ValueError, p.relative_to, P('C:/Baz')) 1041 self.assertRaises(ValueError, p.relative_to, P('C:/Foo/Bar/Baz')) 1042 self.assertRaises(ValueError, p.relative_to, P('C:/Foo/Baz')) 1043 self.assertRaises(ValueError, p.relative_to, P('C:Foo')) 1044 self.assertRaises(ValueError, p.relative_to, P('d:')) 1045 self.assertRaises(ValueError, p.relative_to, P('d:/')) 1046 self.assertRaises(ValueError, p.relative_to, P('/')) 1047 self.assertRaises(ValueError, p.relative_to, P('/Foo')) 1048 self.assertRaises(ValueError, p.relative_to, P('//C/Foo')) 1049 # UNC paths. 1050 p = P('//Server/Share/Foo/Bar') 1051 self.assertEqual(p.relative_to(P('//sErver/sHare')), P('Foo/Bar')) 1052 self.assertEqual(p.relative_to('//sErver/sHare'), P('Foo/Bar')) 1053 self.assertEqual(p.relative_to('//sErver/sHare/'), P('Foo/Bar')) 1054 self.assertEqual(p.relative_to(P('//sErver/sHare/Foo')), P('Bar')) 1055 self.assertEqual(p.relative_to('//sErver/sHare/Foo'), P('Bar')) 1056 self.assertEqual(p.relative_to('//sErver/sHare/Foo/'), P('Bar')) 1057 self.assertEqual(p.relative_to(P('//sErver/sHare/Foo/Bar')), P()) 1058 self.assertEqual(p.relative_to('//sErver/sHare/Foo/Bar'), P()) 1059 # Unrelated paths. 1060 self.assertRaises(ValueError, p.relative_to, P('/Server/Share/Foo')) 1061 self.assertRaises(ValueError, p.relative_to, P('c:/Server/Share/Foo')) 1062 self.assertRaises(ValueError, p.relative_to, P('//z/Share/Foo')) 1063 self.assertRaises(ValueError, p.relative_to, P('//Server/z/Foo')) 1064 1065 def test_is_absolute(self): 1066 P = self.cls 1067 # Under NT, only paths with both a drive and a root are 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('/').is_absolute()) 1072 self.assertFalse(P('/a').is_absolute()) 1073 self.assertFalse(P('/a/b/').is_absolute()) 1074 self.assertFalse(P('c:').is_absolute()) 1075 self.assertFalse(P('c:a').is_absolute()) 1076 self.assertFalse(P('c:a/b/').is_absolute()) 1077 self.assertTrue(P('c:/').is_absolute()) 1078 self.assertTrue(P('c:/a').is_absolute()) 1079 self.assertTrue(P('c:/a/b/').is_absolute()) 1080 # UNC paths are absolute by definition. 1081 self.assertTrue(P('//a/b').is_absolute()) 1082 self.assertTrue(P('//a/b/').is_absolute()) 1083 self.assertTrue(P('//a/b/c').is_absolute()) 1084 self.assertTrue(P('//a/b/c/d').is_absolute()) 1085 1086 def test_join(self): 1087 P = self.cls 1088 p = P('C:/a/b') 1089 pp = p.joinpath('x/y') 1090 self.assertEqual(pp, P('C:/a/b/x/y')) 1091 pp = p.joinpath('/x/y') 1092 self.assertEqual(pp, P('C:/x/y')) 1093 # Joining with a different drive => the first path is ignored, even 1094 # if the second path is relative. 1095 pp = p.joinpath('D:x/y') 1096 self.assertEqual(pp, P('D:x/y')) 1097 pp = p.joinpath('D:/x/y') 1098 self.assertEqual(pp, P('D:/x/y')) 1099 pp = p.joinpath('//host/share/x/y') 1100 self.assertEqual(pp, P('//host/share/x/y')) 1101 # Joining with the same drive => the first path is appended to if 1102 # the second path is relative. 1103 pp = p.joinpath('c:x/y') 1104 self.assertEqual(pp, P('C:/a/b/x/y')) 1105 pp = p.joinpath('c:/x/y') 1106 self.assertEqual(pp, P('C:/x/y')) 1107 1108 def test_div(self): 1109 # Basically the same as joinpath(). 1110 P = self.cls 1111 p = P('C:/a/b') 1112 self.assertEqual(p / 'x/y', P('C:/a/b/x/y')) 1113 self.assertEqual(p / 'x' / 'y', P('C:/a/b/x/y')) 1114 self.assertEqual(p / '/x/y', P('C:/x/y')) 1115 self.assertEqual(p / '/x' / 'y', P('C:/x/y')) 1116 # Joining with a different drive => the first path is ignored, even 1117 # if the second path is relative. 1118 self.assertEqual(p / 'D:x/y', P('D:x/y')) 1119 self.assertEqual(p / 'D:' / 'x/y', P('D:x/y')) 1120 self.assertEqual(p / 'D:/x/y', P('D:/x/y')) 1121 self.assertEqual(p / 'D:' / '/x/y', P('D:/x/y')) 1122 self.assertEqual(p / '//host/share/x/y', P('//host/share/x/y')) 1123 # Joining with the same drive => the first path is appended to if 1124 # the second path is relative. 1125 self.assertEqual(p / 'c:x/y', P('C:/a/b/x/y')) 1126 self.assertEqual(p / 'c:/x/y', P('C:/x/y')) 1127 1128 def test_is_reserved(self): 1129 P = self.cls 1130 self.assertIs(False, P('').is_reserved()) 1131 self.assertIs(False, P('/').is_reserved()) 1132 self.assertIs(False, P('/foo/bar').is_reserved()) 1133 self.assertIs(True, P('con').is_reserved()) 1134 self.assertIs(True, P('NUL').is_reserved()) 1135 self.assertIs(True, P('NUL.txt').is_reserved()) 1136 self.assertIs(True, P('com1').is_reserved()) 1137 self.assertIs(True, P('com9.bar').is_reserved()) 1138 self.assertIs(False, P('bar.com9').is_reserved()) 1139 self.assertIs(True, P('lpt1').is_reserved()) 1140 self.assertIs(True, P('lpt9.bar').is_reserved()) 1141 self.assertIs(False, P('bar.lpt9').is_reserved()) 1142 # Only the last component matters. 1143 self.assertIs(False, P('c:/NUL/con/baz').is_reserved()) 1144 # UNC paths are never reserved. 1145 self.assertIs(False, P('//my/share/nul/con/aux').is_reserved()) 1146 1147class PurePathTest(_BasePurePathTest, unittest.TestCase): 1148 cls = pathlib.PurePath 1149 1150 def test_concrete_class(self): 1151 p = self.cls('a') 1152 self.assertIs(type(p), 1153 pathlib.PureWindowsPath if os.name == 'nt' else pathlib.PurePosixPath) 1154 1155 def test_different_flavours_unequal(self): 1156 p = pathlib.PurePosixPath('a') 1157 q = pathlib.PureWindowsPath('a') 1158 self.assertNotEqual(p, q) 1159 1160 def test_different_flavours_unordered(self): 1161 p = pathlib.PurePosixPath('a') 1162 q = pathlib.PureWindowsPath('a') 1163 with self.assertRaises(TypeError): 1164 p < q 1165 with self.assertRaises(TypeError): 1166 p <= q 1167 with self.assertRaises(TypeError): 1168 p > q 1169 with self.assertRaises(TypeError): 1170 p >= q 1171 1172 1173# 1174# Tests for the concrete classes. 1175# 1176 1177# Make sure any symbolic links in the base test path are resolved. 1178BASE = os.path.realpath(TESTFN) 1179join = lambda *x: os.path.join(BASE, *x) 1180rel_join = lambda *x: os.path.join(TESTFN, *x) 1181 1182only_nt = unittest.skipIf(os.name != 'nt', 1183 'test requires a Windows-compatible system') 1184only_posix = unittest.skipIf(os.name == 'nt', 1185 'test requires a POSIX-compatible system') 1186 1187@only_posix 1188class PosixPathAsPureTest(PurePosixPathTest): 1189 cls = pathlib.PosixPath 1190 1191@only_nt 1192class WindowsPathAsPureTest(PureWindowsPathTest): 1193 cls = pathlib.WindowsPath 1194 1195 def test_owner(self): 1196 P = self.cls 1197 with self.assertRaises(NotImplementedError): 1198 P('c:/').owner() 1199 1200 def test_group(self): 1201 P = self.cls 1202 with self.assertRaises(NotImplementedError): 1203 P('c:/').group() 1204 1205 1206class _BasePathTest(object): 1207 """Tests for the FS-accessing functionalities of the Path classes.""" 1208 1209 # (BASE) 1210 # | 1211 # |-- brokenLink -> non-existing 1212 # |-- dirA 1213 # | `-- linkC -> ../dirB 1214 # |-- dirB 1215 # | |-- fileB 1216 # | `-- linkD -> ../dirB 1217 # |-- dirC 1218 # | |-- dirD 1219 # | | `-- fileD 1220 # | `-- fileC 1221 # |-- dirE # No permissions 1222 # |-- fileA 1223 # |-- linkA -> fileA 1224 # |-- linkB -> dirB 1225 # `-- brokenLinkLoop -> brokenLinkLoop 1226 # 1227 1228 def setUp(self): 1229 def cleanup(): 1230 os.chmod(join('dirE'), 0o777) 1231 support.rmtree(BASE) 1232 self.addCleanup(cleanup) 1233 os.mkdir(BASE) 1234 os.mkdir(join('dirA')) 1235 os.mkdir(join('dirB')) 1236 os.mkdir(join('dirC')) 1237 os.mkdir(join('dirC', 'dirD')) 1238 os.mkdir(join('dirE')) 1239 with open(join('fileA'), 'wb') as f: 1240 f.write(b"this is file A\n") 1241 with open(join('dirB', 'fileB'), 'wb') as f: 1242 f.write(b"this is file B\n") 1243 with open(join('dirC', 'fileC'), 'wb') as f: 1244 f.write(b"this is file C\n") 1245 with open(join('dirC', 'dirD', 'fileD'), 'wb') as f: 1246 f.write(b"this is file D\n") 1247 os.chmod(join('dirE'), 0) 1248 if support.can_symlink(): 1249 # Relative symlinks. 1250 os.symlink('fileA', join('linkA')) 1251 os.symlink('non-existing', join('brokenLink')) 1252 self.dirlink('dirB', join('linkB')) 1253 self.dirlink(os.path.join('..', 'dirB'), join('dirA', 'linkC')) 1254 # This one goes upwards, creating a loop. 1255 self.dirlink(os.path.join('..', 'dirB'), join('dirB', 'linkD')) 1256 # Broken symlink (pointing to itself). 1257 os.symlink('brokenLinkLoop', join('brokenLinkLoop')) 1258 1259 if os.name == 'nt': 1260 # Workaround for http://bugs.python.org/issue13772. 1261 def dirlink(self, src, dest): 1262 os.symlink(src, dest, target_is_directory=True) 1263 else: 1264 def dirlink(self, src, dest): 1265 os.symlink(src, dest) 1266 1267 def assertSame(self, path_a, path_b): 1268 self.assertTrue(os.path.samefile(str(path_a), str(path_b)), 1269 "%r and %r don't point to the same file" % 1270 (path_a, path_b)) 1271 1272 def assertFileNotFound(self, func, *args, **kwargs): 1273 with self.assertRaises(FileNotFoundError) as cm: 1274 func(*args, **kwargs) 1275 self.assertEqual(cm.exception.errno, errno.ENOENT) 1276 1277 def assertEqualNormCase(self, path_a, path_b): 1278 self.assertEqual(os.path.normcase(path_a), os.path.normcase(path_b)) 1279 1280 def _test_cwd(self, p): 1281 q = self.cls(os.getcwd()) 1282 self.assertEqual(p, q) 1283 self.assertEqualNormCase(str(p), str(q)) 1284 self.assertIs(type(p), type(q)) 1285 self.assertTrue(p.is_absolute()) 1286 1287 def test_cwd(self): 1288 p = self.cls.cwd() 1289 self._test_cwd(p) 1290 1291 def _test_home(self, p): 1292 q = self.cls(os.path.expanduser('~')) 1293 self.assertEqual(p, q) 1294 self.assertEqualNormCase(str(p), str(q)) 1295 self.assertIs(type(p), type(q)) 1296 self.assertTrue(p.is_absolute()) 1297 1298 def test_home(self): 1299 p = self.cls.home() 1300 self._test_home(p) 1301 1302 def test_samefile(self): 1303 fileA_path = os.path.join(BASE, 'fileA') 1304 fileB_path = os.path.join(BASE, 'dirB', 'fileB') 1305 p = self.cls(fileA_path) 1306 pp = self.cls(fileA_path) 1307 q = self.cls(fileB_path) 1308 self.assertTrue(p.samefile(fileA_path)) 1309 self.assertTrue(p.samefile(pp)) 1310 self.assertFalse(p.samefile(fileB_path)) 1311 self.assertFalse(p.samefile(q)) 1312 # Test the non-existent file case 1313 non_existent = os.path.join(BASE, 'foo') 1314 r = self.cls(non_existent) 1315 self.assertRaises(FileNotFoundError, p.samefile, r) 1316 self.assertRaises(FileNotFoundError, p.samefile, non_existent) 1317 self.assertRaises(FileNotFoundError, r.samefile, p) 1318 self.assertRaises(FileNotFoundError, r.samefile, non_existent) 1319 self.assertRaises(FileNotFoundError, r.samefile, r) 1320 self.assertRaises(FileNotFoundError, r.samefile, non_existent) 1321 1322 def test_empty_path(self): 1323 # The empty path points to '.' 1324 p = self.cls('') 1325 self.assertEqual(p.stat(), os.stat('.')) 1326 1327 def test_expanduser_common(self): 1328 P = self.cls 1329 p = P('~') 1330 self.assertEqual(p.expanduser(), P(os.path.expanduser('~'))) 1331 p = P('foo') 1332 self.assertEqual(p.expanduser(), p) 1333 p = P('/~') 1334 self.assertEqual(p.expanduser(), p) 1335 p = P('../~') 1336 self.assertEqual(p.expanduser(), p) 1337 p = P(P('').absolute().anchor) / '~' 1338 self.assertEqual(p.expanduser(), p) 1339 1340 def test_exists(self): 1341 P = self.cls 1342 p = P(BASE) 1343 self.assertIs(True, p.exists()) 1344 self.assertIs(True, (p / 'dirA').exists()) 1345 self.assertIs(True, (p / 'fileA').exists()) 1346 self.assertIs(False, (p / 'fileA' / 'bah').exists()) 1347 if support.can_symlink(): 1348 self.assertIs(True, (p / 'linkA').exists()) 1349 self.assertIs(True, (p / 'linkB').exists()) 1350 self.assertIs(True, (p / 'linkB' / 'fileB').exists()) 1351 self.assertIs(False, (p / 'linkA' / 'bah').exists()) 1352 self.assertIs(False, (p / 'foo').exists()) 1353 self.assertIs(False, P('/xyzzy').exists()) 1354 self.assertIs(False, P(BASE + '\udfff').exists()) 1355 self.assertIs(False, P(BASE + '\x00').exists()) 1356 1357 def test_open_common(self): 1358 p = self.cls(BASE) 1359 with (p / 'fileA').open('r') as f: 1360 self.assertIsInstance(f, io.TextIOBase) 1361 self.assertEqual(f.read(), "this is file A\n") 1362 with (p / 'fileA').open('rb') as f: 1363 self.assertIsInstance(f, io.BufferedIOBase) 1364 self.assertEqual(f.read().strip(), b"this is file A") 1365 with (p / 'fileA').open('rb', buffering=0) as f: 1366 self.assertIsInstance(f, io.RawIOBase) 1367 self.assertEqual(f.read().strip(), b"this is file A") 1368 1369 def test_read_write_bytes(self): 1370 p = self.cls(BASE) 1371 (p / 'fileA').write_bytes(b'abcdefg') 1372 self.assertEqual((p / 'fileA').read_bytes(), b'abcdefg') 1373 # Check that trying to write str does not truncate the file. 1374 self.assertRaises(TypeError, (p / 'fileA').write_bytes, 'somestr') 1375 self.assertEqual((p / 'fileA').read_bytes(), b'abcdefg') 1376 1377 def test_read_write_text(self): 1378 p = self.cls(BASE) 1379 (p / 'fileA').write_text('äbcdefg', encoding='latin-1') 1380 self.assertEqual((p / 'fileA').read_text( 1381 encoding='utf-8', errors='ignore'), 'bcdefg') 1382 # Check that trying to write bytes does not truncate the file. 1383 self.assertRaises(TypeError, (p / 'fileA').write_text, b'somebytes') 1384 self.assertEqual((p / 'fileA').read_text(encoding='latin-1'), 'äbcdefg') 1385 1386 def test_iterdir(self): 1387 P = self.cls 1388 p = P(BASE) 1389 it = p.iterdir() 1390 paths = set(it) 1391 expected = ['dirA', 'dirB', 'dirC', 'dirE', 'fileA'] 1392 if support.can_symlink(): 1393 expected += ['linkA', 'linkB', 'brokenLink', 'brokenLinkLoop'] 1394 self.assertEqual(paths, { P(BASE, q) for q in expected }) 1395 1396 @support.skip_unless_symlink 1397 def test_iterdir_symlink(self): 1398 # __iter__ on a symlink to a directory. 1399 P = self.cls 1400 p = P(BASE, 'linkB') 1401 paths = set(p.iterdir()) 1402 expected = { P(BASE, 'linkB', q) for q in ['fileB', 'linkD'] } 1403 self.assertEqual(paths, expected) 1404 1405 def test_iterdir_nodir(self): 1406 # __iter__ on something that is not a directory. 1407 p = self.cls(BASE, 'fileA') 1408 with self.assertRaises(OSError) as cm: 1409 next(p.iterdir()) 1410 # ENOENT or EINVAL under Windows, ENOTDIR otherwise 1411 # (see issue #12802). 1412 self.assertIn(cm.exception.errno, (errno.ENOTDIR, 1413 errno.ENOENT, errno.EINVAL)) 1414 1415 def test_glob_common(self): 1416 def _check(glob, expected): 1417 self.assertEqual(set(glob), { P(BASE, q) for q in expected }) 1418 P = self.cls 1419 p = P(BASE) 1420 it = p.glob("fileA") 1421 self.assertIsInstance(it, collections.abc.Iterator) 1422 _check(it, ["fileA"]) 1423 _check(p.glob("fileB"), []) 1424 _check(p.glob("dir*/file*"), ["dirB/fileB", "dirC/fileC"]) 1425 if not support.can_symlink(): 1426 _check(p.glob("*A"), ['dirA', 'fileA']) 1427 else: 1428 _check(p.glob("*A"), ['dirA', 'fileA', 'linkA']) 1429 if not support.can_symlink(): 1430 _check(p.glob("*B/*"), ['dirB/fileB']) 1431 else: 1432 _check(p.glob("*B/*"), ['dirB/fileB', 'dirB/linkD', 1433 'linkB/fileB', 'linkB/linkD']) 1434 if not support.can_symlink(): 1435 _check(p.glob("*/fileB"), ['dirB/fileB']) 1436 else: 1437 _check(p.glob("*/fileB"), ['dirB/fileB', 'linkB/fileB']) 1438 1439 def test_rglob_common(self): 1440 def _check(glob, expected): 1441 self.assertEqual(set(glob), { P(BASE, q) for q in expected }) 1442 P = self.cls 1443 p = P(BASE) 1444 it = p.rglob("fileA") 1445 self.assertIsInstance(it, collections.abc.Iterator) 1446 _check(it, ["fileA"]) 1447 _check(p.rglob("fileB"), ["dirB/fileB"]) 1448 _check(p.rglob("*/fileA"), []) 1449 if not support.can_symlink(): 1450 _check(p.rglob("*/fileB"), ["dirB/fileB"]) 1451 else: 1452 _check(p.rglob("*/fileB"), ["dirB/fileB", "dirB/linkD/fileB", 1453 "linkB/fileB", "dirA/linkC/fileB"]) 1454 _check(p.rglob("file*"), ["fileA", "dirB/fileB", 1455 "dirC/fileC", "dirC/dirD/fileD"]) 1456 p = P(BASE, "dirC") 1457 _check(p.rglob("file*"), ["dirC/fileC", "dirC/dirD/fileD"]) 1458 _check(p.rglob("*/*"), ["dirC/dirD/fileD"]) 1459 1460 @support.skip_unless_symlink 1461 def test_rglob_symlink_loop(self): 1462 # Don't get fooled by symlink loops (Issue #26012). 1463 P = self.cls 1464 p = P(BASE) 1465 given = set(p.rglob('*')) 1466 expect = {'brokenLink', 1467 'dirA', 'dirA/linkC', 1468 'dirB', 'dirB/fileB', 'dirB/linkD', 1469 'dirC', 'dirC/dirD', 'dirC/dirD/fileD', 'dirC/fileC', 1470 'dirE', 1471 'fileA', 1472 'linkA', 1473 'linkB', 1474 'brokenLinkLoop', 1475 } 1476 self.assertEqual(given, {p / x for x in expect}) 1477 1478 def test_glob_many_open_files(self): 1479 depth = 30 1480 P = self.cls 1481 base = P(BASE) / 'deep' 1482 p = P(base, *(['d']*depth)) 1483 p.mkdir(parents=True) 1484 pattern = '/'.join(['*'] * depth) 1485 iters = [base.glob(pattern) for j in range(100)] 1486 for it in iters: 1487 self.assertEqual(next(it), p) 1488 iters = [base.rglob('d') for j in range(100)] 1489 p = base 1490 for i in range(depth): 1491 p = p / 'd' 1492 for it in iters: 1493 self.assertEqual(next(it), p) 1494 1495 def test_glob_dotdot(self): 1496 # ".." is not special in globs. 1497 P = self.cls 1498 p = P(BASE) 1499 self.assertEqual(set(p.glob("..")), { P(BASE, "..") }) 1500 self.assertEqual(set(p.glob("dirA/../file*")), { P(BASE, "dirA/../fileA") }) 1501 self.assertEqual(set(p.glob("../xyzzy")), set()) 1502 1503 1504 def _check_resolve(self, p, expected, strict=True): 1505 q = p.resolve(strict) 1506 self.assertEqual(q, expected) 1507 1508 # This can be used to check both relative and absolute resolutions. 1509 _check_resolve_relative = _check_resolve_absolute = _check_resolve 1510 1511 @support.skip_unless_symlink 1512 def test_resolve_common(self): 1513 P = self.cls 1514 p = P(BASE, 'foo') 1515 with self.assertRaises(OSError) as cm: 1516 p.resolve(strict=True) 1517 self.assertEqual(cm.exception.errno, errno.ENOENT) 1518 # Non-strict 1519 self.assertEqualNormCase(str(p.resolve(strict=False)), 1520 os.path.join(BASE, 'foo')) 1521 p = P(BASE, 'foo', 'in', 'spam') 1522 self.assertEqualNormCase(str(p.resolve(strict=False)), 1523 os.path.join(BASE, 'foo', 'in', 'spam')) 1524 p = P(BASE, '..', 'foo', 'in', 'spam') 1525 self.assertEqualNormCase(str(p.resolve(strict=False)), 1526 os.path.abspath(os.path.join('foo', 'in', 'spam'))) 1527 # These are all relative symlinks. 1528 p = P(BASE, 'dirB', 'fileB') 1529 self._check_resolve_relative(p, p) 1530 p = P(BASE, 'linkA') 1531 self._check_resolve_relative(p, P(BASE, 'fileA')) 1532 p = P(BASE, 'dirA', 'linkC', 'fileB') 1533 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB')) 1534 p = P(BASE, 'dirB', 'linkD', 'fileB') 1535 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB')) 1536 # Non-strict 1537 p = P(BASE, 'dirA', 'linkC', 'fileB', 'foo', 'in', 'spam') 1538 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB', 'foo', 'in', 1539 'spam'), False) 1540 p = P(BASE, 'dirA', 'linkC', '..', 'foo', 'in', 'spam') 1541 if os.name == 'nt': 1542 # In Windows, if linkY points to dirB, 'dirA\linkY\..' 1543 # resolves to 'dirA' without resolving linkY first. 1544 self._check_resolve_relative(p, P(BASE, 'dirA', 'foo', 'in', 1545 'spam'), False) 1546 else: 1547 # In Posix, if linkY points to dirB, 'dirA/linkY/..' 1548 # resolves to 'dirB/..' first before resolving to parent of dirB. 1549 self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False) 1550 # Now create absolute symlinks. 1551 d = support._longpath(tempfile.mkdtemp(suffix='-dirD', dir=os.getcwd())) 1552 self.addCleanup(support.rmtree, d) 1553 os.symlink(os.path.join(d), join('dirA', 'linkX')) 1554 os.symlink(join('dirB'), os.path.join(d, 'linkY')) 1555 p = P(BASE, 'dirA', 'linkX', 'linkY', 'fileB') 1556 self._check_resolve_absolute(p, P(BASE, 'dirB', 'fileB')) 1557 # Non-strict 1558 p = P(BASE, 'dirA', 'linkX', 'linkY', 'foo', 'in', 'spam') 1559 self._check_resolve_relative(p, P(BASE, 'dirB', 'foo', 'in', 'spam'), 1560 False) 1561 p = P(BASE, 'dirA', 'linkX', 'linkY', '..', 'foo', 'in', 'spam') 1562 if os.name == 'nt': 1563 # In Windows, if linkY points to dirB, 'dirA\linkY\..' 1564 # resolves to 'dirA' without resolving linkY first. 1565 self._check_resolve_relative(p, P(d, 'foo', 'in', 'spam'), False) 1566 else: 1567 # In Posix, if linkY points to dirB, 'dirA/linkY/..' 1568 # resolves to 'dirB/..' first before resolving to parent of dirB. 1569 self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False) 1570 1571 @support.skip_unless_symlink 1572 def test_resolve_dot(self): 1573 # See https://bitbucket.org/pitrou/pathlib/issue/9/pathresolve-fails-on-complex-symlinks 1574 p = self.cls(BASE) 1575 self.dirlink('.', join('0')) 1576 self.dirlink(os.path.join('0', '0'), join('1')) 1577 self.dirlink(os.path.join('1', '1'), join('2')) 1578 q = p / '2' 1579 self.assertEqual(q.resolve(strict=True), p) 1580 r = q / '3' / '4' 1581 self.assertRaises(FileNotFoundError, r.resolve, strict=True) 1582 # Non-strict 1583 self.assertEqual(r.resolve(strict=False), p / '3' / '4') 1584 1585 def test_with(self): 1586 p = self.cls(BASE) 1587 it = p.iterdir() 1588 it2 = p.iterdir() 1589 next(it2) 1590 with p: 1591 pass 1592 # I/O operation on closed path. 1593 self.assertRaises(ValueError, next, it) 1594 self.assertRaises(ValueError, next, it2) 1595 self.assertRaises(ValueError, p.open) 1596 self.assertRaises(ValueError, p.resolve) 1597 self.assertRaises(ValueError, p.absolute) 1598 self.assertRaises(ValueError, p.__enter__) 1599 1600 def test_chmod(self): 1601 p = self.cls(BASE) / 'fileA' 1602 mode = p.stat().st_mode 1603 # Clear writable bit. 1604 new_mode = mode & ~0o222 1605 p.chmod(new_mode) 1606 self.assertEqual(p.stat().st_mode, new_mode) 1607 # Set writable bit. 1608 new_mode = mode | 0o222 1609 p.chmod(new_mode) 1610 self.assertEqual(p.stat().st_mode, new_mode) 1611 1612 # XXX also need a test for lchmod. 1613 1614 def test_stat(self): 1615 p = self.cls(BASE) / 'fileA' 1616 st = p.stat() 1617 self.assertEqual(p.stat(), st) 1618 # Change file mode by flipping write bit. 1619 p.chmod(st.st_mode ^ 0o222) 1620 self.addCleanup(p.chmod, st.st_mode) 1621 self.assertNotEqual(p.stat(), st) 1622 1623 @support.skip_unless_symlink 1624 def test_lstat(self): 1625 p = self.cls(BASE)/ 'linkA' 1626 st = p.stat() 1627 self.assertNotEqual(st, p.lstat()) 1628 1629 def test_lstat_nosymlink(self): 1630 p = self.cls(BASE) / 'fileA' 1631 st = p.stat() 1632 self.assertEqual(st, p.lstat()) 1633 1634 @unittest.skipUnless(pwd, "the pwd module is needed for this test") 1635 def test_owner(self): 1636 p = self.cls(BASE) / 'fileA' 1637 uid = p.stat().st_uid 1638 try: 1639 name = pwd.getpwuid(uid).pw_name 1640 except KeyError: 1641 self.skipTest( 1642 "user %d doesn't have an entry in the system database" % uid) 1643 self.assertEqual(name, p.owner()) 1644 1645 @unittest.skipUnless(grp, "the grp module is needed for this test") 1646 def test_group(self): 1647 p = self.cls(BASE) / 'fileA' 1648 gid = p.stat().st_gid 1649 try: 1650 name = grp.getgrgid(gid).gr_name 1651 except KeyError: 1652 self.skipTest( 1653 "group %d doesn't have an entry in the system database" % gid) 1654 self.assertEqual(name, p.group()) 1655 1656 def test_unlink(self): 1657 p = self.cls(BASE) / 'fileA' 1658 p.unlink() 1659 self.assertFileNotFound(p.stat) 1660 self.assertFileNotFound(p.unlink) 1661 1662 def test_unlink_missing_ok(self): 1663 p = self.cls(BASE) / 'fileAAA' 1664 self.assertFileNotFound(p.unlink) 1665 p.unlink(missing_ok=True) 1666 1667 def test_rmdir(self): 1668 p = self.cls(BASE) / 'dirA' 1669 for q in p.iterdir(): 1670 q.unlink() 1671 p.rmdir() 1672 self.assertFileNotFound(p.stat) 1673 self.assertFileNotFound(p.unlink) 1674 1675 @unittest.skipUnless(hasattr(os, "link"), "os.link() is not present") 1676 def test_link_to(self): 1677 P = self.cls(BASE) 1678 p = P / 'fileA' 1679 size = p.stat().st_size 1680 # linking to another path. 1681 q = P / 'dirA' / 'fileAA' 1682 try: 1683 p.link_to(q) 1684 except PermissionError as e: 1685 self.skipTest('os.link(): %s' % e) 1686 self.assertEqual(q.stat().st_size, size) 1687 self.assertEqual(os.path.samefile(p, q), True) 1688 self.assertTrue(p.stat) 1689 # Linking to a str of a relative path. 1690 r = rel_join('fileAAA') 1691 q.link_to(r) 1692 self.assertEqual(os.stat(r).st_size, size) 1693 self.assertTrue(q.stat) 1694 1695 @unittest.skipIf(hasattr(os, "link"), "os.link() is present") 1696 def test_link_to_not_implemented(self): 1697 P = self.cls(BASE) 1698 p = P / 'fileA' 1699 # linking to another path. 1700 q = P / 'dirA' / 'fileAA' 1701 with self.assertRaises(NotImplementedError): 1702 p.link_to(q) 1703 1704 def test_rename(self): 1705 P = self.cls(BASE) 1706 p = P / 'fileA' 1707 size = p.stat().st_size 1708 # Renaming to another path. 1709 q = P / 'dirA' / 'fileAA' 1710 renamed_p = p.rename(q) 1711 self.assertEqual(renamed_p, q) 1712 self.assertEqual(q.stat().st_size, size) 1713 self.assertFileNotFound(p.stat) 1714 # Renaming to a str of a relative path. 1715 r = rel_join('fileAAA') 1716 renamed_q = q.rename(r) 1717 self.assertEqual(renamed_q, self.cls(r)) 1718 self.assertEqual(os.stat(r).st_size, size) 1719 self.assertFileNotFound(q.stat) 1720 1721 def test_replace(self): 1722 P = self.cls(BASE) 1723 p = P / 'fileA' 1724 size = p.stat().st_size 1725 # Replacing a non-existing path. 1726 q = P / 'dirA' / 'fileAA' 1727 replaced_p = p.replace(q) 1728 self.assertEqual(replaced_p, q) 1729 self.assertEqual(q.stat().st_size, size) 1730 self.assertFileNotFound(p.stat) 1731 # Replacing another (existing) path. 1732 r = rel_join('dirB', 'fileB') 1733 replaced_q = q.replace(r) 1734 self.assertEqual(replaced_q, self.cls(r)) 1735 self.assertEqual(os.stat(r).st_size, size) 1736 self.assertFileNotFound(q.stat) 1737 1738 def test_touch_common(self): 1739 P = self.cls(BASE) 1740 p = P / 'newfileA' 1741 self.assertFalse(p.exists()) 1742 p.touch() 1743 self.assertTrue(p.exists()) 1744 st = p.stat() 1745 old_mtime = st.st_mtime 1746 old_mtime_ns = st.st_mtime_ns 1747 # Rewind the mtime sufficiently far in the past to work around 1748 # filesystem-specific timestamp granularity. 1749 os.utime(str(p), (old_mtime - 10, old_mtime - 10)) 1750 # The file mtime should be refreshed by calling touch() again. 1751 p.touch() 1752 st = p.stat() 1753 self.assertGreaterEqual(st.st_mtime_ns, old_mtime_ns) 1754 self.assertGreaterEqual(st.st_mtime, old_mtime) 1755 # Now with exist_ok=False. 1756 p = P / 'newfileB' 1757 self.assertFalse(p.exists()) 1758 p.touch(mode=0o700, exist_ok=False) 1759 self.assertTrue(p.exists()) 1760 self.assertRaises(OSError, p.touch, exist_ok=False) 1761 1762 def test_touch_nochange(self): 1763 P = self.cls(BASE) 1764 p = P / 'fileA' 1765 p.touch() 1766 with p.open('rb') as f: 1767 self.assertEqual(f.read().strip(), b"this is file A") 1768 1769 def test_mkdir(self): 1770 P = self.cls(BASE) 1771 p = P / 'newdirA' 1772 self.assertFalse(p.exists()) 1773 p.mkdir() 1774 self.assertTrue(p.exists()) 1775 self.assertTrue(p.is_dir()) 1776 with self.assertRaises(OSError) as cm: 1777 p.mkdir() 1778 self.assertEqual(cm.exception.errno, errno.EEXIST) 1779 1780 def test_mkdir_parents(self): 1781 # Creating a chain of directories. 1782 p = self.cls(BASE, 'newdirB', 'newdirC') 1783 self.assertFalse(p.exists()) 1784 with self.assertRaises(OSError) as cm: 1785 p.mkdir() 1786 self.assertEqual(cm.exception.errno, errno.ENOENT) 1787 p.mkdir(parents=True) 1788 self.assertTrue(p.exists()) 1789 self.assertTrue(p.is_dir()) 1790 with self.assertRaises(OSError) as cm: 1791 p.mkdir(parents=True) 1792 self.assertEqual(cm.exception.errno, errno.EEXIST) 1793 # Test `mode` arg. 1794 mode = stat.S_IMODE(p.stat().st_mode) # Default mode. 1795 p = self.cls(BASE, 'newdirD', 'newdirE') 1796 p.mkdir(0o555, parents=True) 1797 self.assertTrue(p.exists()) 1798 self.assertTrue(p.is_dir()) 1799 if os.name != 'nt': 1800 # The directory's permissions follow the mode argument. 1801 self.assertEqual(stat.S_IMODE(p.stat().st_mode), 0o7555 & mode) 1802 # The parent's permissions follow the default process settings. 1803 self.assertEqual(stat.S_IMODE(p.parent.stat().st_mode), mode) 1804 1805 def test_mkdir_exist_ok(self): 1806 p = self.cls(BASE, 'dirB') 1807 st_ctime_first = p.stat().st_ctime 1808 self.assertTrue(p.exists()) 1809 self.assertTrue(p.is_dir()) 1810 with self.assertRaises(FileExistsError) as cm: 1811 p.mkdir() 1812 self.assertEqual(cm.exception.errno, errno.EEXIST) 1813 p.mkdir(exist_ok=True) 1814 self.assertTrue(p.exists()) 1815 self.assertEqual(p.stat().st_ctime, st_ctime_first) 1816 1817 def test_mkdir_exist_ok_with_parent(self): 1818 p = self.cls(BASE, 'dirC') 1819 self.assertTrue(p.exists()) 1820 with self.assertRaises(FileExistsError) as cm: 1821 p.mkdir() 1822 self.assertEqual(cm.exception.errno, errno.EEXIST) 1823 p = p / 'newdirC' 1824 p.mkdir(parents=True) 1825 st_ctime_first = p.stat().st_ctime 1826 self.assertTrue(p.exists()) 1827 with self.assertRaises(FileExistsError) as cm: 1828 p.mkdir(parents=True) 1829 self.assertEqual(cm.exception.errno, errno.EEXIST) 1830 p.mkdir(parents=True, exist_ok=True) 1831 self.assertTrue(p.exists()) 1832 self.assertEqual(p.stat().st_ctime, st_ctime_first) 1833 1834 def test_mkdir_exist_ok_root(self): 1835 # Issue #25803: A drive root could raise PermissionError on Windows. 1836 self.cls('/').resolve().mkdir(exist_ok=True) 1837 self.cls('/').resolve().mkdir(parents=True, exist_ok=True) 1838 1839 @only_nt # XXX: not sure how to test this on POSIX. 1840 def test_mkdir_with_unknown_drive(self): 1841 for d in 'ZYXWVUTSRQPONMLKJIHGFEDCBA': 1842 p = self.cls(d + ':\\') 1843 if not p.is_dir(): 1844 break 1845 else: 1846 self.skipTest("cannot find a drive that doesn't exist") 1847 with self.assertRaises(OSError): 1848 (p / 'child' / 'path').mkdir(parents=True) 1849 1850 def test_mkdir_with_child_file(self): 1851 p = self.cls(BASE, 'dirB', 'fileB') 1852 self.assertTrue(p.exists()) 1853 # An exception is raised when the last path component is an existing 1854 # regular file, regardless of whether exist_ok is true or not. 1855 with self.assertRaises(FileExistsError) as cm: 1856 p.mkdir(parents=True) 1857 self.assertEqual(cm.exception.errno, errno.EEXIST) 1858 with self.assertRaises(FileExistsError) as cm: 1859 p.mkdir(parents=True, exist_ok=True) 1860 self.assertEqual(cm.exception.errno, errno.EEXIST) 1861 1862 def test_mkdir_no_parents_file(self): 1863 p = self.cls(BASE, 'fileA') 1864 self.assertTrue(p.exists()) 1865 # An exception is raised when the last path component is an existing 1866 # regular file, regardless of whether exist_ok is true or not. 1867 with self.assertRaises(FileExistsError) as cm: 1868 p.mkdir() 1869 self.assertEqual(cm.exception.errno, errno.EEXIST) 1870 with self.assertRaises(FileExistsError) as cm: 1871 p.mkdir(exist_ok=True) 1872 self.assertEqual(cm.exception.errno, errno.EEXIST) 1873 1874 def test_mkdir_concurrent_parent_creation(self): 1875 for pattern_num in range(32): 1876 p = self.cls(BASE, 'dirCPC%d' % pattern_num) 1877 self.assertFalse(p.exists()) 1878 1879 def my_mkdir(path, mode=0o777): 1880 path = str(path) 1881 # Emulate another process that would create the directory 1882 # just before we try to create it ourselves. We do it 1883 # in all possible pattern combinations, assuming that this 1884 # function is called at most 5 times (dirCPC/dir1/dir2, 1885 # dirCPC/dir1, dirCPC, dirCPC/dir1, dirCPC/dir1/dir2). 1886 if pattern.pop(): 1887 os.mkdir(path, mode) # From another process. 1888 concurrently_created.add(path) 1889 os.mkdir(path, mode) # Our real call. 1890 1891 pattern = [bool(pattern_num & (1 << n)) for n in range(5)] 1892 concurrently_created = set() 1893 p12 = p / 'dir1' / 'dir2' 1894 try: 1895 with mock.patch("pathlib._normal_accessor.mkdir", my_mkdir): 1896 p12.mkdir(parents=True, exist_ok=False) 1897 except FileExistsError: 1898 self.assertIn(str(p12), concurrently_created) 1899 else: 1900 self.assertNotIn(str(p12), concurrently_created) 1901 self.assertTrue(p.exists()) 1902 1903 @support.skip_unless_symlink 1904 def test_symlink_to(self): 1905 P = self.cls(BASE) 1906 target = P / 'fileA' 1907 # Symlinking a path target. 1908 link = P / 'dirA' / 'linkAA' 1909 link.symlink_to(target) 1910 self.assertEqual(link.stat(), target.stat()) 1911 self.assertNotEqual(link.lstat(), target.stat()) 1912 # Symlinking a str target. 1913 link = P / 'dirA' / 'linkAAA' 1914 link.symlink_to(str(target)) 1915 self.assertEqual(link.stat(), target.stat()) 1916 self.assertNotEqual(link.lstat(), target.stat()) 1917 self.assertFalse(link.is_dir()) 1918 # Symlinking to a directory. 1919 target = P / 'dirB' 1920 link = P / 'dirA' / 'linkAAAA' 1921 link.symlink_to(target, target_is_directory=True) 1922 self.assertEqual(link.stat(), target.stat()) 1923 self.assertNotEqual(link.lstat(), target.stat()) 1924 self.assertTrue(link.is_dir()) 1925 self.assertTrue(list(link.iterdir())) 1926 1927 def test_is_dir(self): 1928 P = self.cls(BASE) 1929 self.assertTrue((P / 'dirA').is_dir()) 1930 self.assertFalse((P / 'fileA').is_dir()) 1931 self.assertFalse((P / 'non-existing').is_dir()) 1932 self.assertFalse((P / 'fileA' / 'bah').is_dir()) 1933 if support.can_symlink(): 1934 self.assertFalse((P / 'linkA').is_dir()) 1935 self.assertTrue((P / 'linkB').is_dir()) 1936 self.assertFalse((P/ 'brokenLink').is_dir(), False) 1937 self.assertIs((P / 'dirA\udfff').is_dir(), False) 1938 self.assertIs((P / 'dirA\x00').is_dir(), False) 1939 1940 def test_is_file(self): 1941 P = self.cls(BASE) 1942 self.assertTrue((P / 'fileA').is_file()) 1943 self.assertFalse((P / 'dirA').is_file()) 1944 self.assertFalse((P / 'non-existing').is_file()) 1945 self.assertFalse((P / 'fileA' / 'bah').is_file()) 1946 if support.can_symlink(): 1947 self.assertTrue((P / 'linkA').is_file()) 1948 self.assertFalse((P / 'linkB').is_file()) 1949 self.assertFalse((P/ 'brokenLink').is_file()) 1950 self.assertIs((P / 'fileA\udfff').is_file(), False) 1951 self.assertIs((P / 'fileA\x00').is_file(), False) 1952 1953 @only_posix 1954 def test_is_mount(self): 1955 P = self.cls(BASE) 1956 R = self.cls('/') # TODO: Work out Windows. 1957 self.assertFalse((P / 'fileA').is_mount()) 1958 self.assertFalse((P / 'dirA').is_mount()) 1959 self.assertFalse((P / 'non-existing').is_mount()) 1960 self.assertFalse((P / 'fileA' / 'bah').is_mount()) 1961 self.assertTrue(R.is_mount()) 1962 if support.can_symlink(): 1963 self.assertFalse((P / 'linkA').is_mount()) 1964 self.assertIs(self.cls('/\udfff').is_mount(), False) 1965 self.assertIs(self.cls('/\x00').is_mount(), False) 1966 1967 def test_is_symlink(self): 1968 P = self.cls(BASE) 1969 self.assertFalse((P / 'fileA').is_symlink()) 1970 self.assertFalse((P / 'dirA').is_symlink()) 1971 self.assertFalse((P / 'non-existing').is_symlink()) 1972 self.assertFalse((P / 'fileA' / 'bah').is_symlink()) 1973 if support.can_symlink(): 1974 self.assertTrue((P / 'linkA').is_symlink()) 1975 self.assertTrue((P / 'linkB').is_symlink()) 1976 self.assertTrue((P/ 'brokenLink').is_symlink()) 1977 self.assertIs((P / 'fileA\udfff').is_file(), False) 1978 self.assertIs((P / 'fileA\x00').is_file(), False) 1979 if support.can_symlink(): 1980 self.assertIs((P / 'linkA\udfff').is_file(), False) 1981 self.assertIs((P / 'linkA\x00').is_file(), False) 1982 1983 def test_is_fifo_false(self): 1984 P = self.cls(BASE) 1985 self.assertFalse((P / 'fileA').is_fifo()) 1986 self.assertFalse((P / 'dirA').is_fifo()) 1987 self.assertFalse((P / 'non-existing').is_fifo()) 1988 self.assertFalse((P / 'fileA' / 'bah').is_fifo()) 1989 self.assertIs((P / 'fileA\udfff').is_fifo(), False) 1990 self.assertIs((P / 'fileA\x00').is_fifo(), False) 1991 1992 @unittest.skipUnless(hasattr(os, "mkfifo"), "os.mkfifo() required") 1993 def test_is_fifo_true(self): 1994 P = self.cls(BASE, 'myfifo') 1995 try: 1996 os.mkfifo(str(P)) 1997 except PermissionError as e: 1998 self.skipTest('os.mkfifo(): %s' % e) 1999 self.assertTrue(P.is_fifo()) 2000 self.assertFalse(P.is_socket()) 2001 self.assertFalse(P.is_file()) 2002 self.assertIs(self.cls(BASE, 'myfifo\udfff').is_fifo(), False) 2003 self.assertIs(self.cls(BASE, 'myfifo\x00').is_fifo(), False) 2004 2005 def test_is_socket_false(self): 2006 P = self.cls(BASE) 2007 self.assertFalse((P / 'fileA').is_socket()) 2008 self.assertFalse((P / 'dirA').is_socket()) 2009 self.assertFalse((P / 'non-existing').is_socket()) 2010 self.assertFalse((P / 'fileA' / 'bah').is_socket()) 2011 self.assertIs((P / 'fileA\udfff').is_socket(), False) 2012 self.assertIs((P / 'fileA\x00').is_socket(), False) 2013 2014 @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") 2015 def test_is_socket_true(self): 2016 P = self.cls(BASE, 'mysock') 2017 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 2018 self.addCleanup(sock.close) 2019 try: 2020 sock.bind(str(P)) 2021 except OSError as e: 2022 if (isinstance(e, PermissionError) or 2023 "AF_UNIX path too long" in str(e)): 2024 self.skipTest("cannot bind Unix socket: " + str(e)) 2025 self.assertTrue(P.is_socket()) 2026 self.assertFalse(P.is_fifo()) 2027 self.assertFalse(P.is_file()) 2028 self.assertIs(self.cls(BASE, 'mysock\udfff').is_socket(), False) 2029 self.assertIs(self.cls(BASE, 'mysock\x00').is_socket(), False) 2030 2031 def test_is_block_device_false(self): 2032 P = self.cls(BASE) 2033 self.assertFalse((P / 'fileA').is_block_device()) 2034 self.assertFalse((P / 'dirA').is_block_device()) 2035 self.assertFalse((P / 'non-existing').is_block_device()) 2036 self.assertFalse((P / 'fileA' / 'bah').is_block_device()) 2037 self.assertIs((P / 'fileA\udfff').is_block_device(), False) 2038 self.assertIs((P / 'fileA\x00').is_block_device(), False) 2039 2040 def test_is_char_device_false(self): 2041 P = self.cls(BASE) 2042 self.assertFalse((P / 'fileA').is_char_device()) 2043 self.assertFalse((P / 'dirA').is_char_device()) 2044 self.assertFalse((P / 'non-existing').is_char_device()) 2045 self.assertFalse((P / 'fileA' / 'bah').is_char_device()) 2046 self.assertIs((P / 'fileA\udfff').is_char_device(), False) 2047 self.assertIs((P / 'fileA\x00').is_char_device(), False) 2048 2049 def test_is_char_device_true(self): 2050 # Under Unix, /dev/null should generally be a char device. 2051 P = self.cls('/dev/null') 2052 if not P.exists(): 2053 self.skipTest("/dev/null required") 2054 self.assertTrue(P.is_char_device()) 2055 self.assertFalse(P.is_block_device()) 2056 self.assertFalse(P.is_file()) 2057 self.assertIs(self.cls('/dev/null\udfff').is_char_device(), False) 2058 self.assertIs(self.cls('/dev/null\x00').is_char_device(), False) 2059 2060 def test_pickling_common(self): 2061 p = self.cls(BASE, 'fileA') 2062 for proto in range(0, pickle.HIGHEST_PROTOCOL + 1): 2063 dumped = pickle.dumps(p, proto) 2064 pp = pickle.loads(dumped) 2065 self.assertEqual(pp.stat(), p.stat()) 2066 2067 def test_parts_interning(self): 2068 P = self.cls 2069 p = P('/usr/bin/foo') 2070 q = P('/usr/local/bin') 2071 # 'usr' 2072 self.assertIs(p.parts[1], q.parts[1]) 2073 # 'bin' 2074 self.assertIs(p.parts[2], q.parts[3]) 2075 2076 def _check_complex_symlinks(self, link0_target): 2077 # Test solving a non-looping chain of symlinks (issue #19887). 2078 P = self.cls(BASE) 2079 self.dirlink(os.path.join('link0', 'link0'), join('link1')) 2080 self.dirlink(os.path.join('link1', 'link1'), join('link2')) 2081 self.dirlink(os.path.join('link2', 'link2'), join('link3')) 2082 self.dirlink(link0_target, join('link0')) 2083 2084 # Resolve absolute paths. 2085 p = (P / 'link0').resolve() 2086 self.assertEqual(p, P) 2087 self.assertEqualNormCase(str(p), BASE) 2088 p = (P / 'link1').resolve() 2089 self.assertEqual(p, P) 2090 self.assertEqualNormCase(str(p), BASE) 2091 p = (P / 'link2').resolve() 2092 self.assertEqual(p, P) 2093 self.assertEqualNormCase(str(p), BASE) 2094 p = (P / 'link3').resolve() 2095 self.assertEqual(p, P) 2096 self.assertEqualNormCase(str(p), BASE) 2097 2098 # Resolve relative paths. 2099 old_path = os.getcwd() 2100 os.chdir(BASE) 2101 try: 2102 p = self.cls('link0').resolve() 2103 self.assertEqual(p, P) 2104 self.assertEqualNormCase(str(p), BASE) 2105 p = self.cls('link1').resolve() 2106 self.assertEqual(p, P) 2107 self.assertEqualNormCase(str(p), BASE) 2108 p = self.cls('link2').resolve() 2109 self.assertEqual(p, P) 2110 self.assertEqualNormCase(str(p), BASE) 2111 p = self.cls('link3').resolve() 2112 self.assertEqual(p, P) 2113 self.assertEqualNormCase(str(p), BASE) 2114 finally: 2115 os.chdir(old_path) 2116 2117 @support.skip_unless_symlink 2118 def test_complex_symlinks_absolute(self): 2119 self._check_complex_symlinks(BASE) 2120 2121 @support.skip_unless_symlink 2122 def test_complex_symlinks_relative(self): 2123 self._check_complex_symlinks('.') 2124 2125 @support.skip_unless_symlink 2126 def test_complex_symlinks_relative_dot_dot(self): 2127 self._check_complex_symlinks(os.path.join('dirA', '..')) 2128 2129 2130class PathTest(_BasePathTest, unittest.TestCase): 2131 cls = pathlib.Path 2132 2133 def test_concrete_class(self): 2134 p = self.cls('a') 2135 self.assertIs(type(p), 2136 pathlib.WindowsPath if os.name == 'nt' else pathlib.PosixPath) 2137 2138 def test_unsupported_flavour(self): 2139 if os.name == 'nt': 2140 self.assertRaises(NotImplementedError, pathlib.PosixPath) 2141 else: 2142 self.assertRaises(NotImplementedError, pathlib.WindowsPath) 2143 2144 def test_glob_empty_pattern(self): 2145 p = self.cls() 2146 with self.assertRaisesRegex(ValueError, 'Unacceptable pattern'): 2147 list(p.glob('')) 2148 2149 2150@only_posix 2151class PosixPathTest(_BasePathTest, unittest.TestCase): 2152 cls = pathlib.PosixPath 2153 2154 def _check_symlink_loop(self, *args, strict=True): 2155 path = self.cls(*args) 2156 with self.assertRaises(RuntimeError): 2157 print(path.resolve(strict)) 2158 2159 def test_open_mode(self): 2160 old_mask = os.umask(0) 2161 self.addCleanup(os.umask, old_mask) 2162 p = self.cls(BASE) 2163 with (p / 'new_file').open('wb'): 2164 pass 2165 st = os.stat(join('new_file')) 2166 self.assertEqual(stat.S_IMODE(st.st_mode), 0o666) 2167 os.umask(0o022) 2168 with (p / 'other_new_file').open('wb'): 2169 pass 2170 st = os.stat(join('other_new_file')) 2171 self.assertEqual(stat.S_IMODE(st.st_mode), 0o644) 2172 2173 def test_touch_mode(self): 2174 old_mask = os.umask(0) 2175 self.addCleanup(os.umask, old_mask) 2176 p = self.cls(BASE) 2177 (p / 'new_file').touch() 2178 st = os.stat(join('new_file')) 2179 self.assertEqual(stat.S_IMODE(st.st_mode), 0o666) 2180 os.umask(0o022) 2181 (p / 'other_new_file').touch() 2182 st = os.stat(join('other_new_file')) 2183 self.assertEqual(stat.S_IMODE(st.st_mode), 0o644) 2184 (p / 'masked_new_file').touch(mode=0o750) 2185 st = os.stat(join('masked_new_file')) 2186 self.assertEqual(stat.S_IMODE(st.st_mode), 0o750) 2187 2188 @support.skip_unless_symlink 2189 def test_resolve_loop(self): 2190 # Loops with relative symlinks. 2191 os.symlink('linkX/inside', join('linkX')) 2192 self._check_symlink_loop(BASE, 'linkX') 2193 os.symlink('linkY', join('linkY')) 2194 self._check_symlink_loop(BASE, 'linkY') 2195 os.symlink('linkZ/../linkZ', join('linkZ')) 2196 self._check_symlink_loop(BASE, 'linkZ') 2197 # Non-strict 2198 self._check_symlink_loop(BASE, 'linkZ', 'foo', strict=False) 2199 # Loops with absolute symlinks. 2200 os.symlink(join('linkU/inside'), join('linkU')) 2201 self._check_symlink_loop(BASE, 'linkU') 2202 os.symlink(join('linkV'), join('linkV')) 2203 self._check_symlink_loop(BASE, 'linkV') 2204 os.symlink(join('linkW/../linkW'), join('linkW')) 2205 self._check_symlink_loop(BASE, 'linkW') 2206 # Non-strict 2207 self._check_symlink_loop(BASE, 'linkW', 'foo', strict=False) 2208 2209 def test_glob(self): 2210 P = self.cls 2211 p = P(BASE) 2212 given = set(p.glob("FILEa")) 2213 expect = set() if not support.fs_is_case_insensitive(BASE) else given 2214 self.assertEqual(given, expect) 2215 self.assertEqual(set(p.glob("FILEa*")), set()) 2216 2217 def test_rglob(self): 2218 P = self.cls 2219 p = P(BASE, "dirC") 2220 given = set(p.rglob("FILEd")) 2221 expect = set() if not support.fs_is_case_insensitive(BASE) else given 2222 self.assertEqual(given, expect) 2223 self.assertEqual(set(p.rglob("FILEd*")), set()) 2224 2225 @unittest.skipUnless(hasattr(pwd, 'getpwall'), 2226 'pwd module does not expose getpwall()') 2227 def test_expanduser(self): 2228 P = self.cls 2229 support.import_module('pwd') 2230 import pwd 2231 pwdent = pwd.getpwuid(os.getuid()) 2232 username = pwdent.pw_name 2233 userhome = pwdent.pw_dir.rstrip('/') or '/' 2234 # Find arbitrary different user (if exists). 2235 for pwdent in pwd.getpwall(): 2236 othername = pwdent.pw_name 2237 otherhome = pwdent.pw_dir.rstrip('/') 2238 if othername != username and otherhome: 2239 break 2240 else: 2241 othername = username 2242 otherhome = userhome 2243 2244 p1 = P('~/Documents') 2245 p2 = P('~' + username + '/Documents') 2246 p3 = P('~' + othername + '/Documents') 2247 p4 = P('../~' + username + '/Documents') 2248 p5 = P('/~' + username + '/Documents') 2249 p6 = P('') 2250 p7 = P('~fakeuser/Documents') 2251 2252 with support.EnvironmentVarGuard() as env: 2253 env.pop('HOME', None) 2254 2255 self.assertEqual(p1.expanduser(), P(userhome) / 'Documents') 2256 self.assertEqual(p2.expanduser(), P(userhome) / 'Documents') 2257 self.assertEqual(p3.expanduser(), P(otherhome) / 'Documents') 2258 self.assertEqual(p4.expanduser(), p4) 2259 self.assertEqual(p5.expanduser(), p5) 2260 self.assertEqual(p6.expanduser(), p6) 2261 self.assertRaises(RuntimeError, p7.expanduser) 2262 2263 env['HOME'] = '/tmp' 2264 self.assertEqual(p1.expanduser(), P('/tmp/Documents')) 2265 self.assertEqual(p2.expanduser(), P(userhome) / 'Documents') 2266 self.assertEqual(p3.expanduser(), P(otherhome) / 'Documents') 2267 self.assertEqual(p4.expanduser(), p4) 2268 self.assertEqual(p5.expanduser(), p5) 2269 self.assertEqual(p6.expanduser(), p6) 2270 self.assertRaises(RuntimeError, p7.expanduser) 2271 2272 @unittest.skipIf(sys.platform != "darwin", 2273 "Bad file descriptor in /dev/fd affects only macOS") 2274 def test_handling_bad_descriptor(self): 2275 try: 2276 file_descriptors = list(pathlib.Path('/dev/fd').rglob("*"))[3:] 2277 if not file_descriptors: 2278 self.skipTest("no file descriptors - issue was not reproduced") 2279 # Checking all file descriptors because there is no guarantee 2280 # which one will fail. 2281 for f in file_descriptors: 2282 f.exists() 2283 f.is_dir() 2284 f.is_file() 2285 f.is_symlink() 2286 f.is_block_device() 2287 f.is_char_device() 2288 f.is_fifo() 2289 f.is_socket() 2290 except OSError as e: 2291 if e.errno == errno.EBADF: 2292 self.fail("Bad file descriptor not handled.") 2293 raise 2294 2295 2296@only_nt 2297class WindowsPathTest(_BasePathTest, unittest.TestCase): 2298 cls = pathlib.WindowsPath 2299 2300 def test_glob(self): 2301 P = self.cls 2302 p = P(BASE) 2303 self.assertEqual(set(p.glob("FILEa")), { P(BASE, "fileA") }) 2304 self.assertEqual(set(p.glob("F*a")), { P(BASE, "fileA") }) 2305 self.assertEqual(set(map(str, p.glob("FILEa"))), {f"{p}\\FILEa"}) 2306 self.assertEqual(set(map(str, p.glob("F*a"))), {f"{p}\\fileA"}) 2307 2308 def test_rglob(self): 2309 P = self.cls 2310 p = P(BASE, "dirC") 2311 self.assertEqual(set(p.rglob("FILEd")), { P(BASE, "dirC/dirD/fileD") }) 2312 self.assertEqual(set(map(str, p.rglob("FILEd"))), {f"{p}\\dirD\\FILEd"}) 2313 2314 def test_expanduser(self): 2315 P = self.cls 2316 with support.EnvironmentVarGuard() as env: 2317 env.pop('HOME', None) 2318 env.pop('USERPROFILE', None) 2319 env.pop('HOMEPATH', None) 2320 env.pop('HOMEDRIVE', None) 2321 env['USERNAME'] = 'alice' 2322 2323 # test that the path returns unchanged 2324 p1 = P('~/My Documents') 2325 p2 = P('~alice/My Documents') 2326 p3 = P('~bob/My Documents') 2327 p4 = P('/~/My Documents') 2328 p5 = P('d:~/My Documents') 2329 p6 = P('') 2330 self.assertRaises(RuntimeError, p1.expanduser) 2331 self.assertRaises(RuntimeError, p2.expanduser) 2332 self.assertRaises(RuntimeError, p3.expanduser) 2333 self.assertEqual(p4.expanduser(), p4) 2334 self.assertEqual(p5.expanduser(), p5) 2335 self.assertEqual(p6.expanduser(), p6) 2336 2337 def check(): 2338 env.pop('USERNAME', None) 2339 self.assertEqual(p1.expanduser(), 2340 P('C:/Users/alice/My Documents')) 2341 self.assertRaises(KeyError, p2.expanduser) 2342 env['USERNAME'] = 'alice' 2343 self.assertEqual(p2.expanduser(), 2344 P('C:/Users/alice/My Documents')) 2345 self.assertEqual(p3.expanduser(), 2346 P('C:/Users/bob/My Documents')) 2347 self.assertEqual(p4.expanduser(), p4) 2348 self.assertEqual(p5.expanduser(), p5) 2349 self.assertEqual(p6.expanduser(), p6) 2350 2351 # Test the first lookup key in the env vars. 2352 env['HOME'] = 'C:\\Users\\alice' 2353 check() 2354 2355 # Test that HOMEPATH is available instead. 2356 env.pop('HOME', None) 2357 env['HOMEPATH'] = 'C:\\Users\\alice' 2358 check() 2359 2360 env['HOMEDRIVE'] = 'C:\\' 2361 env['HOMEPATH'] = 'Users\\alice' 2362 check() 2363 2364 env.pop('HOMEDRIVE', None) 2365 env.pop('HOMEPATH', None) 2366 env['USERPROFILE'] = 'C:\\Users\\alice' 2367 check() 2368 2369 2370class CompatiblePathTest(unittest.TestCase): 2371 """ 2372 Test that a type can be made compatible with PurePath 2373 derivatives by implementing division operator overloads. 2374 """ 2375 2376 class CompatPath: 2377 """ 2378 Minimum viable class to test PurePath compatibility. 2379 Simply uses the division operator to join a given 2380 string and the string value of another object with 2381 a forward slash. 2382 """ 2383 def __init__(self, string): 2384 self.string = string 2385 2386 def __truediv__(self, other): 2387 return type(self)(f"{self.string}/{other}") 2388 2389 def __rtruediv__(self, other): 2390 return type(self)(f"{other}/{self.string}") 2391 2392 def test_truediv(self): 2393 result = pathlib.PurePath("test") / self.CompatPath("right") 2394 self.assertIsInstance(result, self.CompatPath) 2395 self.assertEqual(result.string, "test/right") 2396 2397 with self.assertRaises(TypeError): 2398 # Verify improper operations still raise a TypeError 2399 pathlib.PurePath("test") / 10 2400 2401 def test_rtruediv(self): 2402 result = self.CompatPath("left") / pathlib.PurePath("test") 2403 self.assertIsInstance(result, self.CompatPath) 2404 self.assertEqual(result.string, "left/test") 2405 2406 with self.assertRaises(TypeError): 2407 # Verify improper operations still raise a TypeError 2408 10 / pathlib.PurePath("test") 2409 2410 2411if __name__ == "__main__": 2412 unittest.main() 2413