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