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 with support.EnvironmentVarGuard() as env: 1300 self._test_home(self.cls.home()) 1301 1302 env.clear() 1303 env['USERPROFILE'] = os.path.join(BASE, 'userprofile') 1304 self._test_home(self.cls.home()) 1305 1306 # bpo-38883: ignore `HOME` when set on windows 1307 env['HOME'] = os.path.join(BASE, 'home') 1308 self._test_home(self.cls.home()) 1309 1310 def test_samefile(self): 1311 fileA_path = os.path.join(BASE, 'fileA') 1312 fileB_path = os.path.join(BASE, 'dirB', 'fileB') 1313 p = self.cls(fileA_path) 1314 pp = self.cls(fileA_path) 1315 q = self.cls(fileB_path) 1316 self.assertTrue(p.samefile(fileA_path)) 1317 self.assertTrue(p.samefile(pp)) 1318 self.assertFalse(p.samefile(fileB_path)) 1319 self.assertFalse(p.samefile(q)) 1320 # Test the non-existent file case 1321 non_existent = os.path.join(BASE, 'foo') 1322 r = self.cls(non_existent) 1323 self.assertRaises(FileNotFoundError, p.samefile, r) 1324 self.assertRaises(FileNotFoundError, p.samefile, non_existent) 1325 self.assertRaises(FileNotFoundError, r.samefile, p) 1326 self.assertRaises(FileNotFoundError, r.samefile, non_existent) 1327 self.assertRaises(FileNotFoundError, r.samefile, r) 1328 self.assertRaises(FileNotFoundError, r.samefile, non_existent) 1329 1330 def test_empty_path(self): 1331 # The empty path points to '.' 1332 p = self.cls('') 1333 self.assertEqual(p.stat(), os.stat('.')) 1334 1335 def test_expanduser_common(self): 1336 P = self.cls 1337 p = P('~') 1338 self.assertEqual(p.expanduser(), P(os.path.expanduser('~'))) 1339 p = P('foo') 1340 self.assertEqual(p.expanduser(), p) 1341 p = P('/~') 1342 self.assertEqual(p.expanduser(), p) 1343 p = P('../~') 1344 self.assertEqual(p.expanduser(), p) 1345 p = P(P('').absolute().anchor) / '~' 1346 self.assertEqual(p.expanduser(), p) 1347 1348 def test_exists(self): 1349 P = self.cls 1350 p = P(BASE) 1351 self.assertIs(True, p.exists()) 1352 self.assertIs(True, (p / 'dirA').exists()) 1353 self.assertIs(True, (p / 'fileA').exists()) 1354 self.assertIs(False, (p / 'fileA' / 'bah').exists()) 1355 if support.can_symlink(): 1356 self.assertIs(True, (p / 'linkA').exists()) 1357 self.assertIs(True, (p / 'linkB').exists()) 1358 self.assertIs(True, (p / 'linkB' / 'fileB').exists()) 1359 self.assertIs(False, (p / 'linkA' / 'bah').exists()) 1360 self.assertIs(False, (p / 'foo').exists()) 1361 self.assertIs(False, P('/xyzzy').exists()) 1362 self.assertIs(False, P(BASE + '\udfff').exists()) 1363 self.assertIs(False, P(BASE + '\x00').exists()) 1364 1365 def test_open_common(self): 1366 p = self.cls(BASE) 1367 with (p / 'fileA').open('r') as f: 1368 self.assertIsInstance(f, io.TextIOBase) 1369 self.assertEqual(f.read(), "this is file A\n") 1370 with (p / 'fileA').open('rb') as f: 1371 self.assertIsInstance(f, io.BufferedIOBase) 1372 self.assertEqual(f.read().strip(), b"this is file A") 1373 with (p / 'fileA').open('rb', buffering=0) as f: 1374 self.assertIsInstance(f, io.RawIOBase) 1375 self.assertEqual(f.read().strip(), b"this is file A") 1376 1377 def test_read_write_bytes(self): 1378 p = self.cls(BASE) 1379 (p / 'fileA').write_bytes(b'abcdefg') 1380 self.assertEqual((p / 'fileA').read_bytes(), b'abcdefg') 1381 # Check that trying to write str does not truncate the file. 1382 self.assertRaises(TypeError, (p / 'fileA').write_bytes, 'somestr') 1383 self.assertEqual((p / 'fileA').read_bytes(), b'abcdefg') 1384 1385 def test_read_write_text(self): 1386 p = self.cls(BASE) 1387 (p / 'fileA').write_text('äbcdefg', encoding='latin-1') 1388 self.assertEqual((p / 'fileA').read_text( 1389 encoding='utf-8', errors='ignore'), 'bcdefg') 1390 # Check that trying to write bytes does not truncate the file. 1391 self.assertRaises(TypeError, (p / 'fileA').write_text, b'somebytes') 1392 self.assertEqual((p / 'fileA').read_text(encoding='latin-1'), 'äbcdefg') 1393 1394 def test_iterdir(self): 1395 P = self.cls 1396 p = P(BASE) 1397 it = p.iterdir() 1398 paths = set(it) 1399 expected = ['dirA', 'dirB', 'dirC', 'dirE', 'fileA'] 1400 if support.can_symlink(): 1401 expected += ['linkA', 'linkB', 'brokenLink', 'brokenLinkLoop'] 1402 self.assertEqual(paths, { P(BASE, q) for q in expected }) 1403 1404 @support.skip_unless_symlink 1405 def test_iterdir_symlink(self): 1406 # __iter__ on a symlink to a directory. 1407 P = self.cls 1408 p = P(BASE, 'linkB') 1409 paths = set(p.iterdir()) 1410 expected = { P(BASE, 'linkB', q) for q in ['fileB', 'linkD'] } 1411 self.assertEqual(paths, expected) 1412 1413 def test_iterdir_nodir(self): 1414 # __iter__ on something that is not a directory. 1415 p = self.cls(BASE, 'fileA') 1416 with self.assertRaises(OSError) as cm: 1417 next(p.iterdir()) 1418 # ENOENT or EINVAL under Windows, ENOTDIR otherwise 1419 # (see issue #12802). 1420 self.assertIn(cm.exception.errno, (errno.ENOTDIR, 1421 errno.ENOENT, errno.EINVAL)) 1422 1423 def test_glob_common(self): 1424 def _check(glob, expected): 1425 self.assertEqual(set(glob), { P(BASE, q) for q in expected }) 1426 P = self.cls 1427 p = P(BASE) 1428 it = p.glob("fileA") 1429 self.assertIsInstance(it, collections.abc.Iterator) 1430 _check(it, ["fileA"]) 1431 _check(p.glob("fileB"), []) 1432 _check(p.glob("dir*/file*"), ["dirB/fileB", "dirC/fileC"]) 1433 if not support.can_symlink(): 1434 _check(p.glob("*A"), ['dirA', 'fileA']) 1435 else: 1436 _check(p.glob("*A"), ['dirA', 'fileA', 'linkA']) 1437 if not support.can_symlink(): 1438 _check(p.glob("*B/*"), ['dirB/fileB']) 1439 else: 1440 _check(p.glob("*B/*"), ['dirB/fileB', 'dirB/linkD', 1441 'linkB/fileB', 'linkB/linkD']) 1442 if not support.can_symlink(): 1443 _check(p.glob("*/fileB"), ['dirB/fileB']) 1444 else: 1445 _check(p.glob("*/fileB"), ['dirB/fileB', 'linkB/fileB']) 1446 1447 def test_rglob_common(self): 1448 def _check(glob, expected): 1449 self.assertEqual(set(glob), { P(BASE, q) for q in expected }) 1450 P = self.cls 1451 p = P(BASE) 1452 it = p.rglob("fileA") 1453 self.assertIsInstance(it, collections.abc.Iterator) 1454 _check(it, ["fileA"]) 1455 _check(p.rglob("fileB"), ["dirB/fileB"]) 1456 _check(p.rglob("*/fileA"), []) 1457 if not support.can_symlink(): 1458 _check(p.rglob("*/fileB"), ["dirB/fileB"]) 1459 else: 1460 _check(p.rglob("*/fileB"), ["dirB/fileB", "dirB/linkD/fileB", 1461 "linkB/fileB", "dirA/linkC/fileB"]) 1462 _check(p.rglob("file*"), ["fileA", "dirB/fileB", 1463 "dirC/fileC", "dirC/dirD/fileD"]) 1464 p = P(BASE, "dirC") 1465 _check(p.rglob("file*"), ["dirC/fileC", "dirC/dirD/fileD"]) 1466 _check(p.rglob("*/*"), ["dirC/dirD/fileD"]) 1467 1468 @support.skip_unless_symlink 1469 def test_rglob_symlink_loop(self): 1470 # Don't get fooled by symlink loops (Issue #26012). 1471 P = self.cls 1472 p = P(BASE) 1473 given = set(p.rglob('*')) 1474 expect = {'brokenLink', 1475 'dirA', 'dirA/linkC', 1476 'dirB', 'dirB/fileB', 'dirB/linkD', 1477 'dirC', 'dirC/dirD', 'dirC/dirD/fileD', 'dirC/fileC', 1478 'dirE', 1479 'fileA', 1480 'linkA', 1481 'linkB', 1482 'brokenLinkLoop', 1483 } 1484 self.assertEqual(given, {p / x for x in expect}) 1485 1486 def test_glob_many_open_files(self): 1487 depth = 30 1488 P = self.cls 1489 base = P(BASE) / 'deep' 1490 p = P(base, *(['d']*depth)) 1491 p.mkdir(parents=True) 1492 pattern = '/'.join(['*'] * depth) 1493 iters = [base.glob(pattern) for j in range(100)] 1494 for it in iters: 1495 self.assertEqual(next(it), p) 1496 iters = [base.rglob('d') for j in range(100)] 1497 p = base 1498 for i in range(depth): 1499 p = p / 'd' 1500 for it in iters: 1501 self.assertEqual(next(it), p) 1502 1503 def test_glob_dotdot(self): 1504 # ".." is not special in globs. 1505 P = self.cls 1506 p = P(BASE) 1507 self.assertEqual(set(p.glob("..")), { P(BASE, "..") }) 1508 self.assertEqual(set(p.glob("dirA/../file*")), { P(BASE, "dirA/../fileA") }) 1509 self.assertEqual(set(p.glob("../xyzzy")), set()) 1510 1511 @support.skip_unless_symlink 1512 def test_glob_permissions(self): 1513 # See bpo-38894 1514 P = self.cls 1515 base = P(BASE) / 'permissions' 1516 base.mkdir() 1517 1518 file1 = base / "file1" 1519 file1.touch() 1520 file2 = base / "file2" 1521 file2.touch() 1522 1523 subdir = base / "subdir" 1524 1525 file3 = base / "file3" 1526 file3.symlink_to(subdir / "other") 1527 1528 # Patching is needed to avoid relying on the filesystem 1529 # to return the order of the files as the error will not 1530 # happen if the symlink is the last item. 1531 1532 with mock.patch("os.scandir") as scandir: 1533 scandir.return_value = sorted(os.scandir(base)) 1534 self.assertEqual(len(set(base.glob("*"))), 3) 1535 1536 subdir.mkdir() 1537 1538 with mock.patch("os.scandir") as scandir: 1539 scandir.return_value = sorted(os.scandir(base)) 1540 self.assertEqual(len(set(base.glob("*"))), 4) 1541 1542 subdir.chmod(000) 1543 1544 with mock.patch("os.scandir") as scandir: 1545 scandir.return_value = sorted(os.scandir(base)) 1546 self.assertEqual(len(set(base.glob("*"))), 4) 1547 1548 def _check_resolve(self, p, expected, strict=True): 1549 q = p.resolve(strict) 1550 self.assertEqual(q, expected) 1551 1552 # This can be used to check both relative and absolute resolutions. 1553 _check_resolve_relative = _check_resolve_absolute = _check_resolve 1554 1555 @support.skip_unless_symlink 1556 def test_resolve_common(self): 1557 P = self.cls 1558 p = P(BASE, 'foo') 1559 with self.assertRaises(OSError) as cm: 1560 p.resolve(strict=True) 1561 self.assertEqual(cm.exception.errno, errno.ENOENT) 1562 # Non-strict 1563 self.assertEqualNormCase(str(p.resolve(strict=False)), 1564 os.path.join(BASE, 'foo')) 1565 p = P(BASE, 'foo', 'in', 'spam') 1566 self.assertEqualNormCase(str(p.resolve(strict=False)), 1567 os.path.join(BASE, 'foo', 'in', 'spam')) 1568 p = P(BASE, '..', 'foo', 'in', 'spam') 1569 self.assertEqualNormCase(str(p.resolve(strict=False)), 1570 os.path.abspath(os.path.join('foo', 'in', 'spam'))) 1571 # These are all relative symlinks. 1572 p = P(BASE, 'dirB', 'fileB') 1573 self._check_resolve_relative(p, p) 1574 p = P(BASE, 'linkA') 1575 self._check_resolve_relative(p, P(BASE, 'fileA')) 1576 p = P(BASE, 'dirA', 'linkC', 'fileB') 1577 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB')) 1578 p = P(BASE, 'dirB', 'linkD', 'fileB') 1579 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB')) 1580 # Non-strict 1581 p = P(BASE, 'dirA', 'linkC', 'fileB', 'foo', 'in', 'spam') 1582 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB', 'foo', 'in', 1583 'spam'), False) 1584 p = P(BASE, 'dirA', 'linkC', '..', 'foo', 'in', 'spam') 1585 if os.name == 'nt': 1586 # In Windows, if linkY points to dirB, 'dirA\linkY\..' 1587 # resolves to 'dirA' without resolving linkY first. 1588 self._check_resolve_relative(p, P(BASE, 'dirA', 'foo', 'in', 1589 'spam'), False) 1590 else: 1591 # In Posix, if linkY points to dirB, 'dirA/linkY/..' 1592 # resolves to 'dirB/..' first before resolving to parent of dirB. 1593 self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False) 1594 # Now create absolute symlinks. 1595 d = support._longpath(tempfile.mkdtemp(suffix='-dirD', dir=os.getcwd())) 1596 self.addCleanup(support.rmtree, d) 1597 os.symlink(os.path.join(d), join('dirA', 'linkX')) 1598 os.symlink(join('dirB'), os.path.join(d, 'linkY')) 1599 p = P(BASE, 'dirA', 'linkX', 'linkY', 'fileB') 1600 self._check_resolve_absolute(p, P(BASE, 'dirB', 'fileB')) 1601 # Non-strict 1602 p = P(BASE, 'dirA', 'linkX', 'linkY', 'foo', 'in', 'spam') 1603 self._check_resolve_relative(p, P(BASE, 'dirB', 'foo', 'in', 'spam'), 1604 False) 1605 p = P(BASE, 'dirA', 'linkX', 'linkY', '..', 'foo', 'in', 'spam') 1606 if os.name == 'nt': 1607 # In Windows, if linkY points to dirB, 'dirA\linkY\..' 1608 # resolves to 'dirA' without resolving linkY first. 1609 self._check_resolve_relative(p, P(d, 'foo', 'in', 'spam'), False) 1610 else: 1611 # In Posix, if linkY points to dirB, 'dirA/linkY/..' 1612 # resolves to 'dirB/..' first before resolving to parent of dirB. 1613 self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False) 1614 1615 @support.skip_unless_symlink 1616 def test_resolve_dot(self): 1617 # See https://bitbucket.org/pitrou/pathlib/issue/9/pathresolve-fails-on-complex-symlinks 1618 p = self.cls(BASE) 1619 self.dirlink('.', join('0')) 1620 self.dirlink(os.path.join('0', '0'), join('1')) 1621 self.dirlink(os.path.join('1', '1'), join('2')) 1622 q = p / '2' 1623 self.assertEqual(q.resolve(strict=True), p) 1624 r = q / '3' / '4' 1625 self.assertRaises(FileNotFoundError, r.resolve, strict=True) 1626 # Non-strict 1627 self.assertEqual(r.resolve(strict=False), p / '3' / '4') 1628 1629 def test_with(self): 1630 p = self.cls(BASE) 1631 it = p.iterdir() 1632 it2 = p.iterdir() 1633 next(it2) 1634 with p: 1635 pass 1636 # I/O operation on closed path. 1637 self.assertRaises(ValueError, next, it) 1638 self.assertRaises(ValueError, next, it2) 1639 self.assertRaises(ValueError, p.open) 1640 self.assertRaises(ValueError, p.resolve) 1641 self.assertRaises(ValueError, p.absolute) 1642 self.assertRaises(ValueError, p.__enter__) 1643 1644 def test_chmod(self): 1645 p = self.cls(BASE) / 'fileA' 1646 mode = p.stat().st_mode 1647 # Clear writable bit. 1648 new_mode = mode & ~0o222 1649 p.chmod(new_mode) 1650 self.assertEqual(p.stat().st_mode, new_mode) 1651 # Set writable bit. 1652 new_mode = mode | 0o222 1653 p.chmod(new_mode) 1654 self.assertEqual(p.stat().st_mode, new_mode) 1655 1656 # XXX also need a test for lchmod. 1657 1658 def test_stat(self): 1659 p = self.cls(BASE) / 'fileA' 1660 st = p.stat() 1661 self.assertEqual(p.stat(), st) 1662 # Change file mode by flipping write bit. 1663 p.chmod(st.st_mode ^ 0o222) 1664 self.addCleanup(p.chmod, st.st_mode) 1665 self.assertNotEqual(p.stat(), st) 1666 1667 @support.skip_unless_symlink 1668 def test_lstat(self): 1669 p = self.cls(BASE)/ 'linkA' 1670 st = p.stat() 1671 self.assertNotEqual(st, p.lstat()) 1672 1673 def test_lstat_nosymlink(self): 1674 p = self.cls(BASE) / 'fileA' 1675 st = p.stat() 1676 self.assertEqual(st, p.lstat()) 1677 1678 @unittest.skipUnless(pwd, "the pwd module is needed for this test") 1679 def test_owner(self): 1680 p = self.cls(BASE) / 'fileA' 1681 uid = p.stat().st_uid 1682 try: 1683 name = pwd.getpwuid(uid).pw_name 1684 except KeyError: 1685 self.skipTest( 1686 "user %d doesn't have an entry in the system database" % uid) 1687 self.assertEqual(name, p.owner()) 1688 1689 @unittest.skipUnless(grp, "the grp module is needed for this test") 1690 def test_group(self): 1691 p = self.cls(BASE) / 'fileA' 1692 gid = p.stat().st_gid 1693 try: 1694 name = grp.getgrgid(gid).gr_name 1695 except KeyError: 1696 self.skipTest( 1697 "group %d doesn't have an entry in the system database" % gid) 1698 self.assertEqual(name, p.group()) 1699 1700 def test_unlink(self): 1701 p = self.cls(BASE) / 'fileA' 1702 p.unlink() 1703 self.assertFileNotFound(p.stat) 1704 self.assertFileNotFound(p.unlink) 1705 1706 def test_unlink_missing_ok(self): 1707 p = self.cls(BASE) / 'fileAAA' 1708 self.assertFileNotFound(p.unlink) 1709 p.unlink(missing_ok=True) 1710 1711 def test_rmdir(self): 1712 p = self.cls(BASE) / 'dirA' 1713 for q in p.iterdir(): 1714 q.unlink() 1715 p.rmdir() 1716 self.assertFileNotFound(p.stat) 1717 self.assertFileNotFound(p.unlink) 1718 1719 @unittest.skipUnless(hasattr(os, "link"), "os.link() is not present") 1720 def test_link_to(self): 1721 P = self.cls(BASE) 1722 p = P / 'fileA' 1723 size = p.stat().st_size 1724 # linking to another path. 1725 q = P / 'dirA' / 'fileAA' 1726 try: 1727 p.link_to(q) 1728 except PermissionError as e: 1729 self.skipTest('os.link(): %s' % e) 1730 self.assertEqual(q.stat().st_size, size) 1731 self.assertEqual(os.path.samefile(p, q), True) 1732 self.assertTrue(p.stat) 1733 # Linking to a str of a relative path. 1734 r = rel_join('fileAAA') 1735 q.link_to(r) 1736 self.assertEqual(os.stat(r).st_size, size) 1737 self.assertTrue(q.stat) 1738 1739 @unittest.skipIf(hasattr(os, "link"), "os.link() is present") 1740 def test_link_to_not_implemented(self): 1741 P = self.cls(BASE) 1742 p = P / 'fileA' 1743 # linking to another path. 1744 q = P / 'dirA' / 'fileAA' 1745 with self.assertRaises(NotImplementedError): 1746 p.link_to(q) 1747 1748 def test_rename(self): 1749 P = self.cls(BASE) 1750 p = P / 'fileA' 1751 size = p.stat().st_size 1752 # Renaming to another path. 1753 q = P / 'dirA' / 'fileAA' 1754 renamed_p = p.rename(q) 1755 self.assertEqual(renamed_p, q) 1756 self.assertEqual(q.stat().st_size, size) 1757 self.assertFileNotFound(p.stat) 1758 # Renaming to a str of a relative path. 1759 r = rel_join('fileAAA') 1760 renamed_q = q.rename(r) 1761 self.assertEqual(renamed_q, self.cls(r)) 1762 self.assertEqual(os.stat(r).st_size, size) 1763 self.assertFileNotFound(q.stat) 1764 1765 def test_replace(self): 1766 P = self.cls(BASE) 1767 p = P / 'fileA' 1768 size = p.stat().st_size 1769 # Replacing a non-existing path. 1770 q = P / 'dirA' / 'fileAA' 1771 replaced_p = p.replace(q) 1772 self.assertEqual(replaced_p, q) 1773 self.assertEqual(q.stat().st_size, size) 1774 self.assertFileNotFound(p.stat) 1775 # Replacing another (existing) path. 1776 r = rel_join('dirB', 'fileB') 1777 replaced_q = q.replace(r) 1778 self.assertEqual(replaced_q, self.cls(r)) 1779 self.assertEqual(os.stat(r).st_size, size) 1780 self.assertFileNotFound(q.stat) 1781 1782 def test_touch_common(self): 1783 P = self.cls(BASE) 1784 p = P / 'newfileA' 1785 self.assertFalse(p.exists()) 1786 p.touch() 1787 self.assertTrue(p.exists()) 1788 st = p.stat() 1789 old_mtime = st.st_mtime 1790 old_mtime_ns = st.st_mtime_ns 1791 # Rewind the mtime sufficiently far in the past to work around 1792 # filesystem-specific timestamp granularity. 1793 os.utime(str(p), (old_mtime - 10, old_mtime - 10)) 1794 # The file mtime should be refreshed by calling touch() again. 1795 p.touch() 1796 st = p.stat() 1797 self.assertGreaterEqual(st.st_mtime_ns, old_mtime_ns) 1798 self.assertGreaterEqual(st.st_mtime, old_mtime) 1799 # Now with exist_ok=False. 1800 p = P / 'newfileB' 1801 self.assertFalse(p.exists()) 1802 p.touch(mode=0o700, exist_ok=False) 1803 self.assertTrue(p.exists()) 1804 self.assertRaises(OSError, p.touch, exist_ok=False) 1805 1806 def test_touch_nochange(self): 1807 P = self.cls(BASE) 1808 p = P / 'fileA' 1809 p.touch() 1810 with p.open('rb') as f: 1811 self.assertEqual(f.read().strip(), b"this is file A") 1812 1813 def test_mkdir(self): 1814 P = self.cls(BASE) 1815 p = P / 'newdirA' 1816 self.assertFalse(p.exists()) 1817 p.mkdir() 1818 self.assertTrue(p.exists()) 1819 self.assertTrue(p.is_dir()) 1820 with self.assertRaises(OSError) as cm: 1821 p.mkdir() 1822 self.assertEqual(cm.exception.errno, errno.EEXIST) 1823 1824 def test_mkdir_parents(self): 1825 # Creating a chain of directories. 1826 p = self.cls(BASE, 'newdirB', 'newdirC') 1827 self.assertFalse(p.exists()) 1828 with self.assertRaises(OSError) as cm: 1829 p.mkdir() 1830 self.assertEqual(cm.exception.errno, errno.ENOENT) 1831 p.mkdir(parents=True) 1832 self.assertTrue(p.exists()) 1833 self.assertTrue(p.is_dir()) 1834 with self.assertRaises(OSError) as cm: 1835 p.mkdir(parents=True) 1836 self.assertEqual(cm.exception.errno, errno.EEXIST) 1837 # Test `mode` arg. 1838 mode = stat.S_IMODE(p.stat().st_mode) # Default mode. 1839 p = self.cls(BASE, 'newdirD', 'newdirE') 1840 p.mkdir(0o555, parents=True) 1841 self.assertTrue(p.exists()) 1842 self.assertTrue(p.is_dir()) 1843 if os.name != 'nt': 1844 # The directory's permissions follow the mode argument. 1845 self.assertEqual(stat.S_IMODE(p.stat().st_mode), 0o7555 & mode) 1846 # The parent's permissions follow the default process settings. 1847 self.assertEqual(stat.S_IMODE(p.parent.stat().st_mode), mode) 1848 1849 def test_mkdir_exist_ok(self): 1850 p = self.cls(BASE, 'dirB') 1851 st_ctime_first = p.stat().st_ctime 1852 self.assertTrue(p.exists()) 1853 self.assertTrue(p.is_dir()) 1854 with self.assertRaises(FileExistsError) as cm: 1855 p.mkdir() 1856 self.assertEqual(cm.exception.errno, errno.EEXIST) 1857 p.mkdir(exist_ok=True) 1858 self.assertTrue(p.exists()) 1859 self.assertEqual(p.stat().st_ctime, st_ctime_first) 1860 1861 def test_mkdir_exist_ok_with_parent(self): 1862 p = self.cls(BASE, 'dirC') 1863 self.assertTrue(p.exists()) 1864 with self.assertRaises(FileExistsError) as cm: 1865 p.mkdir() 1866 self.assertEqual(cm.exception.errno, errno.EEXIST) 1867 p = p / 'newdirC' 1868 p.mkdir(parents=True) 1869 st_ctime_first = p.stat().st_ctime 1870 self.assertTrue(p.exists()) 1871 with self.assertRaises(FileExistsError) as cm: 1872 p.mkdir(parents=True) 1873 self.assertEqual(cm.exception.errno, errno.EEXIST) 1874 p.mkdir(parents=True, exist_ok=True) 1875 self.assertTrue(p.exists()) 1876 self.assertEqual(p.stat().st_ctime, st_ctime_first) 1877 1878 def test_mkdir_exist_ok_root(self): 1879 # Issue #25803: A drive root could raise PermissionError on Windows. 1880 self.cls('/').resolve().mkdir(exist_ok=True) 1881 self.cls('/').resolve().mkdir(parents=True, exist_ok=True) 1882 1883 @only_nt # XXX: not sure how to test this on POSIX. 1884 def test_mkdir_with_unknown_drive(self): 1885 for d in 'ZYXWVUTSRQPONMLKJIHGFEDCBA': 1886 p = self.cls(d + ':\\') 1887 if not p.is_dir(): 1888 break 1889 else: 1890 self.skipTest("cannot find a drive that doesn't exist") 1891 with self.assertRaises(OSError): 1892 (p / 'child' / 'path').mkdir(parents=True) 1893 1894 def test_mkdir_with_child_file(self): 1895 p = self.cls(BASE, 'dirB', 'fileB') 1896 self.assertTrue(p.exists()) 1897 # An exception is raised when the last path component is an existing 1898 # regular file, regardless of whether exist_ok is true or not. 1899 with self.assertRaises(FileExistsError) as cm: 1900 p.mkdir(parents=True) 1901 self.assertEqual(cm.exception.errno, errno.EEXIST) 1902 with self.assertRaises(FileExistsError) as cm: 1903 p.mkdir(parents=True, exist_ok=True) 1904 self.assertEqual(cm.exception.errno, errno.EEXIST) 1905 1906 def test_mkdir_no_parents_file(self): 1907 p = self.cls(BASE, 'fileA') 1908 self.assertTrue(p.exists()) 1909 # An exception is raised when the last path component is an existing 1910 # regular file, regardless of whether exist_ok is true or not. 1911 with self.assertRaises(FileExistsError) as cm: 1912 p.mkdir() 1913 self.assertEqual(cm.exception.errno, errno.EEXIST) 1914 with self.assertRaises(FileExistsError) as cm: 1915 p.mkdir(exist_ok=True) 1916 self.assertEqual(cm.exception.errno, errno.EEXIST) 1917 1918 def test_mkdir_concurrent_parent_creation(self): 1919 for pattern_num in range(32): 1920 p = self.cls(BASE, 'dirCPC%d' % pattern_num) 1921 self.assertFalse(p.exists()) 1922 1923 def my_mkdir(path, mode=0o777): 1924 path = str(path) 1925 # Emulate another process that would create the directory 1926 # just before we try to create it ourselves. We do it 1927 # in all possible pattern combinations, assuming that this 1928 # function is called at most 5 times (dirCPC/dir1/dir2, 1929 # dirCPC/dir1, dirCPC, dirCPC/dir1, dirCPC/dir1/dir2). 1930 if pattern.pop(): 1931 os.mkdir(path, mode) # From another process. 1932 concurrently_created.add(path) 1933 os.mkdir(path, mode) # Our real call. 1934 1935 pattern = [bool(pattern_num & (1 << n)) for n in range(5)] 1936 concurrently_created = set() 1937 p12 = p / 'dir1' / 'dir2' 1938 try: 1939 with mock.patch("pathlib._normal_accessor.mkdir", my_mkdir): 1940 p12.mkdir(parents=True, exist_ok=False) 1941 except FileExistsError: 1942 self.assertIn(str(p12), concurrently_created) 1943 else: 1944 self.assertNotIn(str(p12), concurrently_created) 1945 self.assertTrue(p.exists()) 1946 1947 @support.skip_unless_symlink 1948 def test_symlink_to(self): 1949 P = self.cls(BASE) 1950 target = P / 'fileA' 1951 # Symlinking a path target. 1952 link = P / 'dirA' / 'linkAA' 1953 link.symlink_to(target) 1954 self.assertEqual(link.stat(), target.stat()) 1955 self.assertNotEqual(link.lstat(), target.stat()) 1956 # Symlinking a str target. 1957 link = P / 'dirA' / 'linkAAA' 1958 link.symlink_to(str(target)) 1959 self.assertEqual(link.stat(), target.stat()) 1960 self.assertNotEqual(link.lstat(), target.stat()) 1961 self.assertFalse(link.is_dir()) 1962 # Symlinking to a directory. 1963 target = P / 'dirB' 1964 link = P / 'dirA' / 'linkAAAA' 1965 link.symlink_to(target, target_is_directory=True) 1966 self.assertEqual(link.stat(), target.stat()) 1967 self.assertNotEqual(link.lstat(), target.stat()) 1968 self.assertTrue(link.is_dir()) 1969 self.assertTrue(list(link.iterdir())) 1970 1971 def test_is_dir(self): 1972 P = self.cls(BASE) 1973 self.assertTrue((P / 'dirA').is_dir()) 1974 self.assertFalse((P / 'fileA').is_dir()) 1975 self.assertFalse((P / 'non-existing').is_dir()) 1976 self.assertFalse((P / 'fileA' / 'bah').is_dir()) 1977 if support.can_symlink(): 1978 self.assertFalse((P / 'linkA').is_dir()) 1979 self.assertTrue((P / 'linkB').is_dir()) 1980 self.assertFalse((P/ 'brokenLink').is_dir(), False) 1981 self.assertIs((P / 'dirA\udfff').is_dir(), False) 1982 self.assertIs((P / 'dirA\x00').is_dir(), False) 1983 1984 def test_is_file(self): 1985 P = self.cls(BASE) 1986 self.assertTrue((P / 'fileA').is_file()) 1987 self.assertFalse((P / 'dirA').is_file()) 1988 self.assertFalse((P / 'non-existing').is_file()) 1989 self.assertFalse((P / 'fileA' / 'bah').is_file()) 1990 if support.can_symlink(): 1991 self.assertTrue((P / 'linkA').is_file()) 1992 self.assertFalse((P / 'linkB').is_file()) 1993 self.assertFalse((P/ 'brokenLink').is_file()) 1994 self.assertIs((P / 'fileA\udfff').is_file(), False) 1995 self.assertIs((P / 'fileA\x00').is_file(), False) 1996 1997 @only_posix 1998 def test_is_mount(self): 1999 P = self.cls(BASE) 2000 R = self.cls('/') # TODO: Work out Windows. 2001 self.assertFalse((P / 'fileA').is_mount()) 2002 self.assertFalse((P / 'dirA').is_mount()) 2003 self.assertFalse((P / 'non-existing').is_mount()) 2004 self.assertFalse((P / 'fileA' / 'bah').is_mount()) 2005 self.assertTrue(R.is_mount()) 2006 if support.can_symlink(): 2007 self.assertFalse((P / 'linkA').is_mount()) 2008 self.assertIs(self.cls('/\udfff').is_mount(), False) 2009 self.assertIs(self.cls('/\x00').is_mount(), False) 2010 2011 def test_is_symlink(self): 2012 P = self.cls(BASE) 2013 self.assertFalse((P / 'fileA').is_symlink()) 2014 self.assertFalse((P / 'dirA').is_symlink()) 2015 self.assertFalse((P / 'non-existing').is_symlink()) 2016 self.assertFalse((P / 'fileA' / 'bah').is_symlink()) 2017 if support.can_symlink(): 2018 self.assertTrue((P / 'linkA').is_symlink()) 2019 self.assertTrue((P / 'linkB').is_symlink()) 2020 self.assertTrue((P/ 'brokenLink').is_symlink()) 2021 self.assertIs((P / 'fileA\udfff').is_file(), False) 2022 self.assertIs((P / 'fileA\x00').is_file(), False) 2023 if support.can_symlink(): 2024 self.assertIs((P / 'linkA\udfff').is_file(), False) 2025 self.assertIs((P / 'linkA\x00').is_file(), False) 2026 2027 def test_is_fifo_false(self): 2028 P = self.cls(BASE) 2029 self.assertFalse((P / 'fileA').is_fifo()) 2030 self.assertFalse((P / 'dirA').is_fifo()) 2031 self.assertFalse((P / 'non-existing').is_fifo()) 2032 self.assertFalse((P / 'fileA' / 'bah').is_fifo()) 2033 self.assertIs((P / 'fileA\udfff').is_fifo(), False) 2034 self.assertIs((P / 'fileA\x00').is_fifo(), False) 2035 2036 @unittest.skipUnless(hasattr(os, "mkfifo"), "os.mkfifo() required") 2037 def test_is_fifo_true(self): 2038 P = self.cls(BASE, 'myfifo') 2039 try: 2040 os.mkfifo(str(P)) 2041 except PermissionError as e: 2042 self.skipTest('os.mkfifo(): %s' % e) 2043 self.assertTrue(P.is_fifo()) 2044 self.assertFalse(P.is_socket()) 2045 self.assertFalse(P.is_file()) 2046 self.assertIs(self.cls(BASE, 'myfifo\udfff').is_fifo(), False) 2047 self.assertIs(self.cls(BASE, 'myfifo\x00').is_fifo(), False) 2048 2049 def test_is_socket_false(self): 2050 P = self.cls(BASE) 2051 self.assertFalse((P / 'fileA').is_socket()) 2052 self.assertFalse((P / 'dirA').is_socket()) 2053 self.assertFalse((P / 'non-existing').is_socket()) 2054 self.assertFalse((P / 'fileA' / 'bah').is_socket()) 2055 self.assertIs((P / 'fileA\udfff').is_socket(), False) 2056 self.assertIs((P / 'fileA\x00').is_socket(), False) 2057 2058 @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") 2059 def test_is_socket_true(self): 2060 P = self.cls(BASE, 'mysock') 2061 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 2062 self.addCleanup(sock.close) 2063 try: 2064 sock.bind(str(P)) 2065 except OSError as e: 2066 if (isinstance(e, PermissionError) or 2067 "AF_UNIX path too long" in str(e)): 2068 self.skipTest("cannot bind Unix socket: " + str(e)) 2069 self.assertTrue(P.is_socket()) 2070 self.assertFalse(P.is_fifo()) 2071 self.assertFalse(P.is_file()) 2072 self.assertIs(self.cls(BASE, 'mysock\udfff').is_socket(), False) 2073 self.assertIs(self.cls(BASE, 'mysock\x00').is_socket(), False) 2074 2075 def test_is_block_device_false(self): 2076 P = self.cls(BASE) 2077 self.assertFalse((P / 'fileA').is_block_device()) 2078 self.assertFalse((P / 'dirA').is_block_device()) 2079 self.assertFalse((P / 'non-existing').is_block_device()) 2080 self.assertFalse((P / 'fileA' / 'bah').is_block_device()) 2081 self.assertIs((P / 'fileA\udfff').is_block_device(), False) 2082 self.assertIs((P / 'fileA\x00').is_block_device(), False) 2083 2084 def test_is_char_device_false(self): 2085 P = self.cls(BASE) 2086 self.assertFalse((P / 'fileA').is_char_device()) 2087 self.assertFalse((P / 'dirA').is_char_device()) 2088 self.assertFalse((P / 'non-existing').is_char_device()) 2089 self.assertFalse((P / 'fileA' / 'bah').is_char_device()) 2090 self.assertIs((P / 'fileA\udfff').is_char_device(), False) 2091 self.assertIs((P / 'fileA\x00').is_char_device(), False) 2092 2093 def test_is_char_device_true(self): 2094 # Under Unix, /dev/null should generally be a char device. 2095 P = self.cls('/dev/null') 2096 if not P.exists(): 2097 self.skipTest("/dev/null required") 2098 self.assertTrue(P.is_char_device()) 2099 self.assertFalse(P.is_block_device()) 2100 self.assertFalse(P.is_file()) 2101 self.assertIs(self.cls('/dev/null\udfff').is_char_device(), False) 2102 self.assertIs(self.cls('/dev/null\x00').is_char_device(), False) 2103 2104 def test_pickling_common(self): 2105 p = self.cls(BASE, 'fileA') 2106 for proto in range(0, pickle.HIGHEST_PROTOCOL + 1): 2107 dumped = pickle.dumps(p, proto) 2108 pp = pickle.loads(dumped) 2109 self.assertEqual(pp.stat(), p.stat()) 2110 2111 def test_parts_interning(self): 2112 P = self.cls 2113 p = P('/usr/bin/foo') 2114 q = P('/usr/local/bin') 2115 # 'usr' 2116 self.assertIs(p.parts[1], q.parts[1]) 2117 # 'bin' 2118 self.assertIs(p.parts[2], q.parts[3]) 2119 2120 def _check_complex_symlinks(self, link0_target): 2121 # Test solving a non-looping chain of symlinks (issue #19887). 2122 P = self.cls(BASE) 2123 self.dirlink(os.path.join('link0', 'link0'), join('link1')) 2124 self.dirlink(os.path.join('link1', 'link1'), join('link2')) 2125 self.dirlink(os.path.join('link2', 'link2'), join('link3')) 2126 self.dirlink(link0_target, join('link0')) 2127 2128 # Resolve absolute paths. 2129 p = (P / 'link0').resolve() 2130 self.assertEqual(p, P) 2131 self.assertEqualNormCase(str(p), BASE) 2132 p = (P / 'link1').resolve() 2133 self.assertEqual(p, P) 2134 self.assertEqualNormCase(str(p), BASE) 2135 p = (P / 'link2').resolve() 2136 self.assertEqual(p, P) 2137 self.assertEqualNormCase(str(p), BASE) 2138 p = (P / 'link3').resolve() 2139 self.assertEqual(p, P) 2140 self.assertEqualNormCase(str(p), BASE) 2141 2142 # Resolve relative paths. 2143 old_path = os.getcwd() 2144 os.chdir(BASE) 2145 try: 2146 p = self.cls('link0').resolve() 2147 self.assertEqual(p, P) 2148 self.assertEqualNormCase(str(p), BASE) 2149 p = self.cls('link1').resolve() 2150 self.assertEqual(p, P) 2151 self.assertEqualNormCase(str(p), BASE) 2152 p = self.cls('link2').resolve() 2153 self.assertEqual(p, P) 2154 self.assertEqualNormCase(str(p), BASE) 2155 p = self.cls('link3').resolve() 2156 self.assertEqual(p, P) 2157 self.assertEqualNormCase(str(p), BASE) 2158 finally: 2159 os.chdir(old_path) 2160 2161 @support.skip_unless_symlink 2162 def test_complex_symlinks_absolute(self): 2163 self._check_complex_symlinks(BASE) 2164 2165 @support.skip_unless_symlink 2166 def test_complex_symlinks_relative(self): 2167 self._check_complex_symlinks('.') 2168 2169 @support.skip_unless_symlink 2170 def test_complex_symlinks_relative_dot_dot(self): 2171 self._check_complex_symlinks(os.path.join('dirA', '..')) 2172 2173 2174class PathTest(_BasePathTest, unittest.TestCase): 2175 cls = pathlib.Path 2176 2177 def test_concrete_class(self): 2178 p = self.cls('a') 2179 self.assertIs(type(p), 2180 pathlib.WindowsPath if os.name == 'nt' else pathlib.PosixPath) 2181 2182 def test_unsupported_flavour(self): 2183 if os.name == 'nt': 2184 self.assertRaises(NotImplementedError, pathlib.PosixPath) 2185 else: 2186 self.assertRaises(NotImplementedError, pathlib.WindowsPath) 2187 2188 def test_glob_empty_pattern(self): 2189 p = self.cls() 2190 with self.assertRaisesRegex(ValueError, 'Unacceptable pattern'): 2191 list(p.glob('')) 2192 2193 2194@only_posix 2195class PosixPathTest(_BasePathTest, unittest.TestCase): 2196 cls = pathlib.PosixPath 2197 2198 def _check_symlink_loop(self, *args, strict=True): 2199 path = self.cls(*args) 2200 with self.assertRaises(RuntimeError): 2201 print(path.resolve(strict)) 2202 2203 def test_open_mode(self): 2204 old_mask = os.umask(0) 2205 self.addCleanup(os.umask, old_mask) 2206 p = self.cls(BASE) 2207 with (p / 'new_file').open('wb'): 2208 pass 2209 st = os.stat(join('new_file')) 2210 self.assertEqual(stat.S_IMODE(st.st_mode), 0o666) 2211 os.umask(0o022) 2212 with (p / 'other_new_file').open('wb'): 2213 pass 2214 st = os.stat(join('other_new_file')) 2215 self.assertEqual(stat.S_IMODE(st.st_mode), 0o644) 2216 2217 def test_resolve_root(self): 2218 current_directory = os.getcwd() 2219 try: 2220 os.chdir('/') 2221 p = self.cls('spam') 2222 self.assertEqual(str(p.resolve()), '/spam') 2223 finally: 2224 os.chdir(current_directory) 2225 2226 def test_touch_mode(self): 2227 old_mask = os.umask(0) 2228 self.addCleanup(os.umask, old_mask) 2229 p = self.cls(BASE) 2230 (p / 'new_file').touch() 2231 st = os.stat(join('new_file')) 2232 self.assertEqual(stat.S_IMODE(st.st_mode), 0o666) 2233 os.umask(0o022) 2234 (p / 'other_new_file').touch() 2235 st = os.stat(join('other_new_file')) 2236 self.assertEqual(stat.S_IMODE(st.st_mode), 0o644) 2237 (p / 'masked_new_file').touch(mode=0o750) 2238 st = os.stat(join('masked_new_file')) 2239 self.assertEqual(stat.S_IMODE(st.st_mode), 0o750) 2240 2241 @support.skip_unless_symlink 2242 def test_resolve_loop(self): 2243 # Loops with relative symlinks. 2244 os.symlink('linkX/inside', join('linkX')) 2245 self._check_symlink_loop(BASE, 'linkX') 2246 os.symlink('linkY', join('linkY')) 2247 self._check_symlink_loop(BASE, 'linkY') 2248 os.symlink('linkZ/../linkZ', join('linkZ')) 2249 self._check_symlink_loop(BASE, 'linkZ') 2250 # Non-strict 2251 self._check_symlink_loop(BASE, 'linkZ', 'foo', strict=False) 2252 # Loops with absolute symlinks. 2253 os.symlink(join('linkU/inside'), join('linkU')) 2254 self._check_symlink_loop(BASE, 'linkU') 2255 os.symlink(join('linkV'), join('linkV')) 2256 self._check_symlink_loop(BASE, 'linkV') 2257 os.symlink(join('linkW/../linkW'), join('linkW')) 2258 self._check_symlink_loop(BASE, 'linkW') 2259 # Non-strict 2260 self._check_symlink_loop(BASE, 'linkW', 'foo', strict=False) 2261 2262 def test_glob(self): 2263 P = self.cls 2264 p = P(BASE) 2265 given = set(p.glob("FILEa")) 2266 expect = set() if not support.fs_is_case_insensitive(BASE) else given 2267 self.assertEqual(given, expect) 2268 self.assertEqual(set(p.glob("FILEa*")), set()) 2269 2270 def test_rglob(self): 2271 P = self.cls 2272 p = P(BASE, "dirC") 2273 given = set(p.rglob("FILEd")) 2274 expect = set() if not support.fs_is_case_insensitive(BASE) else given 2275 self.assertEqual(given, expect) 2276 self.assertEqual(set(p.rglob("FILEd*")), set()) 2277 2278 @unittest.skipUnless(hasattr(pwd, 'getpwall'), 2279 'pwd module does not expose getpwall()') 2280 def test_expanduser(self): 2281 P = self.cls 2282 support.import_module('pwd') 2283 import pwd 2284 pwdent = pwd.getpwuid(os.getuid()) 2285 username = pwdent.pw_name 2286 userhome = pwdent.pw_dir.rstrip('/') or '/' 2287 # Find arbitrary different user (if exists). 2288 for pwdent in pwd.getpwall(): 2289 othername = pwdent.pw_name 2290 otherhome = pwdent.pw_dir.rstrip('/') 2291 if othername != username and otherhome: 2292 break 2293 else: 2294 othername = username 2295 otherhome = userhome 2296 2297 p1 = P('~/Documents') 2298 p2 = P('~' + username + '/Documents') 2299 p3 = P('~' + othername + '/Documents') 2300 p4 = P('../~' + username + '/Documents') 2301 p5 = P('/~' + username + '/Documents') 2302 p6 = P('') 2303 p7 = P('~fakeuser/Documents') 2304 2305 with support.EnvironmentVarGuard() as env: 2306 env.pop('HOME', None) 2307 2308 self.assertEqual(p1.expanduser(), P(userhome) / 'Documents') 2309 self.assertEqual(p2.expanduser(), P(userhome) / 'Documents') 2310 self.assertEqual(p3.expanduser(), P(otherhome) / 'Documents') 2311 self.assertEqual(p4.expanduser(), p4) 2312 self.assertEqual(p5.expanduser(), p5) 2313 self.assertEqual(p6.expanduser(), p6) 2314 self.assertRaises(RuntimeError, p7.expanduser) 2315 2316 env['HOME'] = '/tmp' 2317 self.assertEqual(p1.expanduser(), P('/tmp/Documents')) 2318 self.assertEqual(p2.expanduser(), P(userhome) / 'Documents') 2319 self.assertEqual(p3.expanduser(), P(otherhome) / 'Documents') 2320 self.assertEqual(p4.expanduser(), p4) 2321 self.assertEqual(p5.expanduser(), p5) 2322 self.assertEqual(p6.expanduser(), p6) 2323 self.assertRaises(RuntimeError, p7.expanduser) 2324 2325 @unittest.skipIf(sys.platform != "darwin", 2326 "Bad file descriptor in /dev/fd affects only macOS") 2327 def test_handling_bad_descriptor(self): 2328 try: 2329 file_descriptors = list(pathlib.Path('/dev/fd').rglob("*"))[3:] 2330 if not file_descriptors: 2331 self.skipTest("no file descriptors - issue was not reproduced") 2332 # Checking all file descriptors because there is no guarantee 2333 # which one will fail. 2334 for f in file_descriptors: 2335 f.exists() 2336 f.is_dir() 2337 f.is_file() 2338 f.is_symlink() 2339 f.is_block_device() 2340 f.is_char_device() 2341 f.is_fifo() 2342 f.is_socket() 2343 except OSError as e: 2344 if e.errno == errno.EBADF: 2345 self.fail("Bad file descriptor not handled.") 2346 raise 2347 2348 2349@only_nt 2350class WindowsPathTest(_BasePathTest, unittest.TestCase): 2351 cls = pathlib.WindowsPath 2352 2353 def test_glob(self): 2354 P = self.cls 2355 p = P(BASE) 2356 self.assertEqual(set(p.glob("FILEa")), { P(BASE, "fileA") }) 2357 self.assertEqual(set(p.glob("F*a")), { P(BASE, "fileA") }) 2358 self.assertEqual(set(map(str, p.glob("FILEa"))), {f"{p}\\FILEa"}) 2359 self.assertEqual(set(map(str, p.glob("F*a"))), {f"{p}\\fileA"}) 2360 2361 def test_rglob(self): 2362 P = self.cls 2363 p = P(BASE, "dirC") 2364 self.assertEqual(set(p.rglob("FILEd")), { P(BASE, "dirC/dirD/fileD") }) 2365 self.assertEqual(set(map(str, p.rglob("FILEd"))), {f"{p}\\dirD\\FILEd"}) 2366 2367 def test_expanduser(self): 2368 P = self.cls 2369 with support.EnvironmentVarGuard() as env: 2370 env.pop('HOME', None) 2371 env.pop('USERPROFILE', None) 2372 env.pop('HOMEPATH', None) 2373 env.pop('HOMEDRIVE', None) 2374 env['USERNAME'] = 'alice' 2375 2376 # test that the path returns unchanged 2377 p1 = P('~/My Documents') 2378 p2 = P('~alice/My Documents') 2379 p3 = P('~bob/My Documents') 2380 p4 = P('/~/My Documents') 2381 p5 = P('d:~/My Documents') 2382 p6 = P('') 2383 self.assertRaises(RuntimeError, p1.expanduser) 2384 self.assertRaises(RuntimeError, p2.expanduser) 2385 self.assertRaises(RuntimeError, p3.expanduser) 2386 self.assertEqual(p4.expanduser(), p4) 2387 self.assertEqual(p5.expanduser(), p5) 2388 self.assertEqual(p6.expanduser(), p6) 2389 2390 def check(): 2391 env.pop('USERNAME', None) 2392 self.assertEqual(p1.expanduser(), 2393 P('C:/Users/alice/My Documents')) 2394 self.assertRaises(KeyError, p2.expanduser) 2395 env['USERNAME'] = 'alice' 2396 self.assertEqual(p2.expanduser(), 2397 P('C:/Users/alice/My Documents')) 2398 self.assertEqual(p3.expanduser(), 2399 P('C:/Users/bob/My Documents')) 2400 self.assertEqual(p4.expanduser(), p4) 2401 self.assertEqual(p5.expanduser(), p5) 2402 self.assertEqual(p6.expanduser(), p6) 2403 2404 env['HOMEPATH'] = 'C:\\Users\\alice' 2405 check() 2406 2407 env['HOMEDRIVE'] = 'C:\\' 2408 env['HOMEPATH'] = 'Users\\alice' 2409 check() 2410 2411 env.pop('HOMEDRIVE', None) 2412 env.pop('HOMEPATH', None) 2413 env['USERPROFILE'] = 'C:\\Users\\alice' 2414 check() 2415 2416 # bpo-38883: ignore `HOME` when set on windows 2417 env['HOME'] = 'C:\\Users\\eve' 2418 check() 2419 2420 2421class CompatiblePathTest(unittest.TestCase): 2422 """ 2423 Test that a type can be made compatible with PurePath 2424 derivatives by implementing division operator overloads. 2425 """ 2426 2427 class CompatPath: 2428 """ 2429 Minimum viable class to test PurePath compatibility. 2430 Simply uses the division operator to join a given 2431 string and the string value of another object with 2432 a forward slash. 2433 """ 2434 def __init__(self, string): 2435 self.string = string 2436 2437 def __truediv__(self, other): 2438 return type(self)(f"{self.string}/{other}") 2439 2440 def __rtruediv__(self, other): 2441 return type(self)(f"{other}/{self.string}") 2442 2443 def test_truediv(self): 2444 result = pathlib.PurePath("test") / self.CompatPath("right") 2445 self.assertIsInstance(result, self.CompatPath) 2446 self.assertEqual(result.string, "test/right") 2447 2448 with self.assertRaises(TypeError): 2449 # Verify improper operations still raise a TypeError 2450 pathlib.PurePath("test") / 10 2451 2452 def test_rtruediv(self): 2453 result = self.CompatPath("left") / pathlib.PurePath("test") 2454 self.assertIsInstance(result, self.CompatPath) 2455 self.assertEqual(result.string, "left/test") 2456 2457 with self.assertRaises(TypeError): 2458 # Verify improper operations still raise a TypeError 2459 10 / pathlib.PurePath("test") 2460 2461 2462if __name__ == "__main__": 2463 unittest.main() 2464