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.support import import_helper 15from test.support import os_helper 16from test.support.os_helper import TESTFN, FakePath 17 18try: 19 import grp, pwd 20except ImportError: 21 grp = pwd = None 22 23 24class _BaseFlavourTest(object): 25 26 def _check_parse_parts(self, arg, expected): 27 f = self.flavour.parse_parts 28 sep = self.flavour.sep 29 altsep = self.flavour.altsep 30 actual = f([x.replace('/', sep) for x in arg]) 31 self.assertEqual(actual, expected) 32 if altsep: 33 actual = f([x.replace('/', altsep) for x in arg]) 34 self.assertEqual(actual, expected) 35 36 def test_parse_parts_common(self): 37 check = self._check_parse_parts 38 sep = self.flavour.sep 39 # Unanchored parts. 40 check([], ('', '', [])) 41 check(['a'], ('', '', ['a'])) 42 check(['a/'], ('', '', ['a'])) 43 check(['a', 'b'], ('', '', ['a', 'b'])) 44 # Expansion. 45 check(['a/b'], ('', '', ['a', 'b'])) 46 check(['a/b/'], ('', '', ['a', 'b'])) 47 check(['a', 'b/c', 'd'], ('', '', ['a', 'b', 'c', 'd'])) 48 # Collapsing and stripping excess slashes. 49 check(['a', 'b//c', 'd'], ('', '', ['a', 'b', 'c', 'd'])) 50 check(['a', 'b/c/', 'd'], ('', '', ['a', 'b', 'c', 'd'])) 51 # Eliminating standalone dots. 52 check(['.'], ('', '', [])) 53 check(['.', '.', 'b'], ('', '', ['b'])) 54 check(['a', '.', 'b'], ('', '', ['a', 'b'])) 55 check(['a', '.', '.'], ('', '', ['a'])) 56 # The first part is anchored. 57 check(['/a/b'], ('', sep, [sep, 'a', 'b'])) 58 check(['/a', 'b'], ('', sep, [sep, 'a', 'b'])) 59 check(['/a/', 'b'], ('', sep, [sep, 'a', 'b'])) 60 # Ignoring parts before an anchored part. 61 check(['a', '/b', 'c'], ('', sep, [sep, 'b', 'c'])) 62 check(['a', '/b', '/c'], ('', sep, [sep, 'c'])) 63 64 65class PosixFlavourTest(_BaseFlavourTest, unittest.TestCase): 66 flavour = pathlib._posix_flavour 67 68 def test_parse_parts(self): 69 check = self._check_parse_parts 70 # Collapsing of excess leading slashes, except for the double-slash 71 # special case. 72 check(['//a', 'b'], ('', '//', ['//', 'a', 'b'])) 73 check(['///a', 'b'], ('', '/', ['/', 'a', 'b'])) 74 check(['////a', 'b'], ('', '/', ['/', 'a', 'b'])) 75 # Paths which look like NT paths aren't treated specially. 76 check(['c:a'], ('', '', ['c:a'])) 77 check(['c:\\a'], ('', '', ['c:\\a'])) 78 check(['\\a'], ('', '', ['\\a'])) 79 80 def test_splitroot(self): 81 f = self.flavour.splitroot 82 self.assertEqual(f(''), ('', '', '')) 83 self.assertEqual(f('a'), ('', '', 'a')) 84 self.assertEqual(f('a/b'), ('', '', 'a/b')) 85 self.assertEqual(f('a/b/'), ('', '', 'a/b/')) 86 self.assertEqual(f('/a'), ('', '/', 'a')) 87 self.assertEqual(f('/a/b'), ('', '/', 'a/b')) 88 self.assertEqual(f('/a/b/'), ('', '/', 'a/b/')) 89 # The root is collapsed when there are redundant slashes 90 # except when there are exactly two leading slashes, which 91 # is a special case in POSIX. 92 self.assertEqual(f('//a'), ('', '//', 'a')) 93 self.assertEqual(f('///a'), ('', '/', 'a')) 94 self.assertEqual(f('///a/b'), ('', '/', 'a/b')) 95 # Paths which look like NT paths aren't treated specially. 96 self.assertEqual(f('c:/a/b'), ('', '', 'c:/a/b')) 97 self.assertEqual(f('\\/a/b'), ('', '', '\\/a/b')) 98 self.assertEqual(f('\\a\\b'), ('', '', '\\a\\b')) 99 100 101class NTFlavourTest(_BaseFlavourTest, unittest.TestCase): 102 flavour = pathlib._windows_flavour 103 104 def test_parse_parts(self): 105 check = self._check_parse_parts 106 # First part is anchored. 107 check(['c:'], ('c:', '', ['c:'])) 108 check(['c:/'], ('c:', '\\', ['c:\\'])) 109 check(['/'], ('', '\\', ['\\'])) 110 check(['c:a'], ('c:', '', ['c:', 'a'])) 111 check(['c:/a'], ('c:', '\\', ['c:\\', 'a'])) 112 check(['/a'], ('', '\\', ['\\', 'a'])) 113 # UNC paths. 114 check(['//a/b'], ('\\\\a\\b', '\\', ['\\\\a\\b\\'])) 115 check(['//a/b/'], ('\\\\a\\b', '\\', ['\\\\a\\b\\'])) 116 check(['//a/b/c'], ('\\\\a\\b', '\\', ['\\\\a\\b\\', 'c'])) 117 # Second part is anchored, so that the first part is ignored. 118 check(['a', 'Z:b', 'c'], ('Z:', '', ['Z:', 'b', 'c'])) 119 check(['a', 'Z:/b', 'c'], ('Z:', '\\', ['Z:\\', 'b', 'c'])) 120 # UNC paths. 121 check(['a', '//b/c', 'd'], ('\\\\b\\c', '\\', ['\\\\b\\c\\', 'd'])) 122 # Collapsing and stripping excess slashes. 123 check(['a', 'Z://b//c/', 'd/'], ('Z:', '\\', ['Z:\\', 'b', 'c', 'd'])) 124 # UNC paths. 125 check(['a', '//b/c//', 'd'], ('\\\\b\\c', '\\', ['\\\\b\\c\\', 'd'])) 126 # Extended paths. 127 check(['//?/c:/'], ('\\\\?\\c:', '\\', ['\\\\?\\c:\\'])) 128 check(['//?/c:/a'], ('\\\\?\\c:', '\\', ['\\\\?\\c:\\', 'a'])) 129 check(['//?/c:/a', '/b'], ('\\\\?\\c:', '\\', ['\\\\?\\c:\\', 'b'])) 130 # Extended UNC paths (format is "\\?\UNC\server\share"). 131 check(['//?/UNC/b/c'], ('\\\\?\\UNC\\b\\c', '\\', ['\\\\?\\UNC\\b\\c\\'])) 132 check(['//?/UNC/b/c/d'], ('\\\\?\\UNC\\b\\c', '\\', ['\\\\?\\UNC\\b\\c\\', 'd'])) 133 # Second part has a root but not drive. 134 check(['a', '/b', 'c'], ('', '\\', ['\\', 'b', 'c'])) 135 check(['Z:/a', '/b', 'c'], ('Z:', '\\', ['Z:\\', 'b', 'c'])) 136 check(['//?/Z:/a', '/b', 'c'], ('\\\\?\\Z:', '\\', ['\\\\?\\Z:\\', 'b', 'c'])) 137 138 def test_splitroot(self): 139 f = self.flavour.splitroot 140 self.assertEqual(f(''), ('', '', '')) 141 self.assertEqual(f('a'), ('', '', 'a')) 142 self.assertEqual(f('a\\b'), ('', '', 'a\\b')) 143 self.assertEqual(f('\\a'), ('', '\\', 'a')) 144 self.assertEqual(f('\\a\\b'), ('', '\\', 'a\\b')) 145 self.assertEqual(f('c:a\\b'), ('c:', '', 'a\\b')) 146 self.assertEqual(f('c:\\a\\b'), ('c:', '\\', 'a\\b')) 147 # Redundant slashes in the root are collapsed. 148 self.assertEqual(f('\\\\a'), ('', '\\', 'a')) 149 self.assertEqual(f('\\\\\\a/b'), ('', '\\', 'a/b')) 150 self.assertEqual(f('c:\\\\a'), ('c:', '\\', 'a')) 151 self.assertEqual(f('c:\\\\\\a/b'), ('c:', '\\', 'a/b')) 152 # Valid UNC paths. 153 self.assertEqual(f('\\\\a\\b'), ('\\\\a\\b', '\\', '')) 154 self.assertEqual(f('\\\\a\\b\\'), ('\\\\a\\b', '\\', '')) 155 self.assertEqual(f('\\\\a\\b\\c\\d'), ('\\\\a\\b', '\\', 'c\\d')) 156 # These are non-UNC paths (according to ntpath.py and test_ntpath). 157 # However, command.com says such paths are invalid, so it's 158 # difficult to know what the right semantics are. 159 self.assertEqual(f('\\\\\\a\\b'), ('', '\\', 'a\\b')) 160 self.assertEqual(f('\\\\a'), ('', '\\', 'a')) 161 162 163# 164# Tests for the pure classes. 165# 166 167class _BasePurePathTest(object): 168 169 # Keys are canonical paths, values are list of tuples of arguments 170 # supposed to produce equal paths. 171 equivalences = { 172 'a/b': [ 173 ('a', 'b'), ('a/', 'b'), ('a', 'b/'), ('a/', 'b/'), 174 ('a/b/',), ('a//b',), ('a//b//',), 175 # Empty components get removed. 176 ('', 'a', 'b'), ('a', '', 'b'), ('a', 'b', ''), 177 ], 178 '/b/c/d': [ 179 ('a', '/b/c', 'd'), ('a', '///b//c', 'd/'), 180 ('/a', '/b/c', 'd'), 181 # Empty components get removed. 182 ('/', 'b', '', 'c/d'), ('/', '', 'b/c/d'), ('', '/b/c/d'), 183 ], 184 } 185 186 def setUp(self): 187 p = self.cls('a') 188 self.flavour = p._flavour 189 self.sep = self.flavour.sep 190 self.altsep = self.flavour.altsep 191 192 def test_constructor_common(self): 193 P = self.cls 194 p = P('a') 195 self.assertIsInstance(p, P) 196 P('a', 'b', 'c') 197 P('/a', 'b', 'c') 198 P('a/b/c') 199 P('/a/b/c') 200 P(FakePath("a/b/c")) 201 self.assertEqual(P(P('a')), P('a')) 202 self.assertEqual(P(P('a'), 'b'), P('a/b')) 203 self.assertEqual(P(P('a'), P('b')), P('a/b')) 204 self.assertEqual(P(P('a'), P('b'), P('c')), P(FakePath("a/b/c"))) 205 206 def _check_str_subclass(self, *args): 207 # Issue #21127: it should be possible to construct a PurePath object 208 # from a str subclass instance, and it then gets converted to 209 # a pure str object. 210 class StrSubclass(str): 211 pass 212 P = self.cls 213 p = P(*(StrSubclass(x) for x in args)) 214 self.assertEqual(p, P(*args)) 215 for part in p.parts: 216 self.assertIs(type(part), str) 217 218 def test_str_subclass_common(self): 219 self._check_str_subclass('') 220 self._check_str_subclass('.') 221 self._check_str_subclass('a') 222 self._check_str_subclass('a/b.txt') 223 self._check_str_subclass('/a/b.txt') 224 225 def test_join_common(self): 226 P = self.cls 227 p = P('a/b') 228 pp = p.joinpath('c') 229 self.assertEqual(pp, P('a/b/c')) 230 self.assertIs(type(pp), type(p)) 231 pp = p.joinpath('c', 'd') 232 self.assertEqual(pp, P('a/b/c/d')) 233 pp = p.joinpath(P('c')) 234 self.assertEqual(pp, P('a/b/c')) 235 pp = p.joinpath('/c') 236 self.assertEqual(pp, P('/c')) 237 238 def test_div_common(self): 239 # Basically the same as joinpath(). 240 P = self.cls 241 p = P('a/b') 242 pp = p / 'c' 243 self.assertEqual(pp, P('a/b/c')) 244 self.assertIs(type(pp), type(p)) 245 pp = p / 'c/d' 246 self.assertEqual(pp, P('a/b/c/d')) 247 pp = p / 'c' / 'd' 248 self.assertEqual(pp, P('a/b/c/d')) 249 pp = 'c' / p / 'd' 250 self.assertEqual(pp, P('c/a/b/d')) 251 pp = p / P('c') 252 self.assertEqual(pp, P('a/b/c')) 253 pp = p/ '/c' 254 self.assertEqual(pp, P('/c')) 255 256 def _check_str(self, expected, args): 257 p = self.cls(*args) 258 self.assertEqual(str(p), expected.replace('/', self.sep)) 259 260 def test_str_common(self): 261 # Canonicalized paths roundtrip. 262 for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'): 263 self._check_str(pathstr, (pathstr,)) 264 # Special case for the empty path. 265 self._check_str('.', ('',)) 266 # Other tests for str() are in test_equivalences(). 267 268 def test_as_posix_common(self): 269 P = self.cls 270 for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'): 271 self.assertEqual(P(pathstr).as_posix(), pathstr) 272 # Other tests for as_posix() are in test_equivalences(). 273 274 def test_as_bytes_common(self): 275 sep = os.fsencode(self.sep) 276 P = self.cls 277 self.assertEqual(bytes(P('a/b')), b'a' + sep + b'b') 278 279 def test_as_uri_common(self): 280 P = self.cls 281 with self.assertRaises(ValueError): 282 P('a').as_uri() 283 with self.assertRaises(ValueError): 284 P().as_uri() 285 286 def test_repr_common(self): 287 for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'): 288 p = self.cls(pathstr) 289 clsname = p.__class__.__name__ 290 r = repr(p) 291 # The repr() is in the form ClassName("forward-slashes path"). 292 self.assertTrue(r.startswith(clsname + '('), r) 293 self.assertTrue(r.endswith(')'), r) 294 inner = r[len(clsname) + 1 : -1] 295 self.assertEqual(eval(inner), p.as_posix()) 296 # The repr() roundtrips. 297 q = eval(r, pathlib.__dict__) 298 self.assertIs(q.__class__, p.__class__) 299 self.assertEqual(q, p) 300 self.assertEqual(repr(q), r) 301 302 def test_eq_common(self): 303 P = self.cls 304 self.assertEqual(P('a/b'), P('a/b')) 305 self.assertEqual(P('a/b'), P('a', 'b')) 306 self.assertNotEqual(P('a/b'), P('a')) 307 self.assertNotEqual(P('a/b'), P('/a/b')) 308 self.assertNotEqual(P('a/b'), P()) 309 self.assertNotEqual(P('/a/b'), P('/')) 310 self.assertNotEqual(P(), P('/')) 311 self.assertNotEqual(P(), "") 312 self.assertNotEqual(P(), {}) 313 self.assertNotEqual(P(), int) 314 315 def test_match_common(self): 316 P = self.cls 317 self.assertRaises(ValueError, P('a').match, '') 318 self.assertRaises(ValueError, P('a').match, '.') 319 # Simple relative pattern. 320 self.assertTrue(P('b.py').match('b.py')) 321 self.assertTrue(P('a/b.py').match('b.py')) 322 self.assertTrue(P('/a/b.py').match('b.py')) 323 self.assertFalse(P('a.py').match('b.py')) 324 self.assertFalse(P('b/py').match('b.py')) 325 self.assertFalse(P('/a.py').match('b.py')) 326 self.assertFalse(P('b.py/c').match('b.py')) 327 # Wildcard relative pattern. 328 self.assertTrue(P('b.py').match('*.py')) 329 self.assertTrue(P('a/b.py').match('*.py')) 330 self.assertTrue(P('/a/b.py').match('*.py')) 331 self.assertFalse(P('b.pyc').match('*.py')) 332 self.assertFalse(P('b./py').match('*.py')) 333 self.assertFalse(P('b.py/c').match('*.py')) 334 # Multi-part relative pattern. 335 self.assertTrue(P('ab/c.py').match('a*/*.py')) 336 self.assertTrue(P('/d/ab/c.py').match('a*/*.py')) 337 self.assertFalse(P('a.py').match('a*/*.py')) 338 self.assertFalse(P('/dab/c.py').match('a*/*.py')) 339 self.assertFalse(P('ab/c.py/d').match('a*/*.py')) 340 # Absolute pattern. 341 self.assertTrue(P('/b.py').match('/*.py')) 342 self.assertFalse(P('b.py').match('/*.py')) 343 self.assertFalse(P('a/b.py').match('/*.py')) 344 self.assertFalse(P('/a/b.py').match('/*.py')) 345 # Multi-part absolute pattern. 346 self.assertTrue(P('/a/b.py').match('/a/*.py')) 347 self.assertFalse(P('/ab.py').match('/a/*.py')) 348 self.assertFalse(P('/a/b/c.py').match('/a/*.py')) 349 # Multi-part glob-style pattern. 350 self.assertFalse(P('/a/b/c.py').match('/**/*.py')) 351 self.assertTrue(P('/a/b/c.py').match('/a/**/*.py')) 352 353 def test_ordering_common(self): 354 # Ordering is tuple-alike. 355 def assertLess(a, b): 356 self.assertLess(a, b) 357 self.assertGreater(b, a) 358 P = self.cls 359 a = P('a') 360 b = P('a/b') 361 c = P('abc') 362 d = P('b') 363 assertLess(a, b) 364 assertLess(a, c) 365 assertLess(a, d) 366 assertLess(b, c) 367 assertLess(c, d) 368 P = self.cls 369 a = P('/a') 370 b = P('/a/b') 371 c = P('/abc') 372 d = P('/b') 373 assertLess(a, b) 374 assertLess(a, c) 375 assertLess(a, d) 376 assertLess(b, c) 377 assertLess(c, d) 378 with self.assertRaises(TypeError): 379 P() < {} 380 381 def test_parts_common(self): 382 # `parts` returns a tuple. 383 sep = self.sep 384 P = self.cls 385 p = P('a/b') 386 parts = p.parts 387 self.assertEqual(parts, ('a', 'b')) 388 # The object gets reused. 389 self.assertIs(parts, p.parts) 390 # When the path is absolute, the anchor is a separate part. 391 p = P('/a/b') 392 parts = p.parts 393 self.assertEqual(parts, (sep, 'a', 'b')) 394 395 def test_fspath_common(self): 396 P = self.cls 397 p = P('a/b') 398 self._check_str(p.__fspath__(), ('a/b',)) 399 self._check_str(os.fspath(p), ('a/b',)) 400 401 def test_equivalences(self): 402 for k, tuples in self.equivalences.items(): 403 canon = k.replace('/', self.sep) 404 posix = k.replace(self.sep, '/') 405 if canon != posix: 406 tuples = tuples + [ 407 tuple(part.replace('/', self.sep) for part in t) 408 for t in tuples 409 ] 410 tuples.append((posix, )) 411 pcanon = self.cls(canon) 412 for t in tuples: 413 p = self.cls(*t) 414 self.assertEqual(p, pcanon, "failed with args {}".format(t)) 415 self.assertEqual(hash(p), hash(pcanon)) 416 self.assertEqual(str(p), canon) 417 self.assertEqual(p.as_posix(), posix) 418 419 def test_parent_common(self): 420 # Relative 421 P = self.cls 422 p = P('a/b/c') 423 self.assertEqual(p.parent, P('a/b')) 424 self.assertEqual(p.parent.parent, P('a')) 425 self.assertEqual(p.parent.parent.parent, P()) 426 self.assertEqual(p.parent.parent.parent.parent, P()) 427 # Anchored 428 p = P('/a/b/c') 429 self.assertEqual(p.parent, P('/a/b')) 430 self.assertEqual(p.parent.parent, P('/a')) 431 self.assertEqual(p.parent.parent.parent, P('/')) 432 self.assertEqual(p.parent.parent.parent.parent, P('/')) 433 434 def test_parents_common(self): 435 # Relative 436 P = self.cls 437 p = P('a/b/c') 438 par = p.parents 439 self.assertEqual(len(par), 3) 440 self.assertEqual(par[0], P('a/b')) 441 self.assertEqual(par[1], P('a')) 442 self.assertEqual(par[2], P('.')) 443 self.assertEqual(par[-1], P('.')) 444 self.assertEqual(par[-2], P('a')) 445 self.assertEqual(par[-3], P('a/b')) 446 self.assertEqual(par[0:1], (P('a/b'),)) 447 self.assertEqual(par[:2], (P('a/b'), P('a'))) 448 self.assertEqual(par[:-1], (P('a/b'), P('a'))) 449 self.assertEqual(par[1:], (P('a'), P('.'))) 450 self.assertEqual(par[::2], (P('a/b'), P('.'))) 451 self.assertEqual(par[::-1], (P('.'), P('a'), P('a/b'))) 452 self.assertEqual(list(par), [P('a/b'), P('a'), P('.')]) 453 with self.assertRaises(IndexError): 454 par[-4] 455 with self.assertRaises(IndexError): 456 par[3] 457 with self.assertRaises(TypeError): 458 par[0] = p 459 # Anchored 460 p = P('/a/b/c') 461 par = p.parents 462 self.assertEqual(len(par), 3) 463 self.assertEqual(par[0], P('/a/b')) 464 self.assertEqual(par[1], P('/a')) 465 self.assertEqual(par[2], P('/')) 466 self.assertEqual(par[0:1], (P('/a/b'),)) 467 self.assertEqual(par[:2], (P('/a/b'), P('/a'))) 468 self.assertEqual(par[:-1], (P('/a/b'), P('/a'))) 469 self.assertEqual(par[1:], (P('/a'), P('/'))) 470 self.assertEqual(par[::2], (P('/a/b'), P('/'))) 471 self.assertEqual(par[::-1], (P('/'), P('/a'), P('/a/b'))) 472 self.assertEqual(list(par), [P('/a/b'), P('/a'), P('/')]) 473 with self.assertRaises(IndexError): 474 par[3] 475 476 def test_drive_common(self): 477 P = self.cls 478 self.assertEqual(P('a/b').drive, '') 479 self.assertEqual(P('/a/b').drive, '') 480 self.assertEqual(P('').drive, '') 481 482 def test_root_common(self): 483 P = self.cls 484 sep = self.sep 485 self.assertEqual(P('').root, '') 486 self.assertEqual(P('a/b').root, '') 487 self.assertEqual(P('/').root, sep) 488 self.assertEqual(P('/a/b').root, sep) 489 490 def test_anchor_common(self): 491 P = self.cls 492 sep = self.sep 493 self.assertEqual(P('').anchor, '') 494 self.assertEqual(P('a/b').anchor, '') 495 self.assertEqual(P('/').anchor, sep) 496 self.assertEqual(P('/a/b').anchor, sep) 497 498 def test_name_common(self): 499 P = self.cls 500 self.assertEqual(P('').name, '') 501 self.assertEqual(P('.').name, '') 502 self.assertEqual(P('/').name, '') 503 self.assertEqual(P('a/b').name, 'b') 504 self.assertEqual(P('/a/b').name, 'b') 505 self.assertEqual(P('/a/b/.').name, 'b') 506 self.assertEqual(P('a/b.py').name, 'b.py') 507 self.assertEqual(P('/a/b.py').name, 'b.py') 508 509 def test_suffix_common(self): 510 P = self.cls 511 self.assertEqual(P('').suffix, '') 512 self.assertEqual(P('.').suffix, '') 513 self.assertEqual(P('..').suffix, '') 514 self.assertEqual(P('/').suffix, '') 515 self.assertEqual(P('a/b').suffix, '') 516 self.assertEqual(P('/a/b').suffix, '') 517 self.assertEqual(P('/a/b/.').suffix, '') 518 self.assertEqual(P('a/b.py').suffix, '.py') 519 self.assertEqual(P('/a/b.py').suffix, '.py') 520 self.assertEqual(P('a/.hgrc').suffix, '') 521 self.assertEqual(P('/a/.hgrc').suffix, '') 522 self.assertEqual(P('a/.hg.rc').suffix, '.rc') 523 self.assertEqual(P('/a/.hg.rc').suffix, '.rc') 524 self.assertEqual(P('a/b.tar.gz').suffix, '.gz') 525 self.assertEqual(P('/a/b.tar.gz').suffix, '.gz') 526 self.assertEqual(P('a/Some name. Ending with a dot.').suffix, '') 527 self.assertEqual(P('/a/Some name. Ending with a dot.').suffix, '') 528 529 def test_suffixes_common(self): 530 P = self.cls 531 self.assertEqual(P('').suffixes, []) 532 self.assertEqual(P('.').suffixes, []) 533 self.assertEqual(P('/').suffixes, []) 534 self.assertEqual(P('a/b').suffixes, []) 535 self.assertEqual(P('/a/b').suffixes, []) 536 self.assertEqual(P('/a/b/.').suffixes, []) 537 self.assertEqual(P('a/b.py').suffixes, ['.py']) 538 self.assertEqual(P('/a/b.py').suffixes, ['.py']) 539 self.assertEqual(P('a/.hgrc').suffixes, []) 540 self.assertEqual(P('/a/.hgrc').suffixes, []) 541 self.assertEqual(P('a/.hg.rc').suffixes, ['.rc']) 542 self.assertEqual(P('/a/.hg.rc').suffixes, ['.rc']) 543 self.assertEqual(P('a/b.tar.gz').suffixes, ['.tar', '.gz']) 544 self.assertEqual(P('/a/b.tar.gz').suffixes, ['.tar', '.gz']) 545 self.assertEqual(P('a/Some name. Ending with a dot.').suffixes, []) 546 self.assertEqual(P('/a/Some name. Ending with a dot.').suffixes, []) 547 548 def test_stem_common(self): 549 P = self.cls 550 self.assertEqual(P('').stem, '') 551 self.assertEqual(P('.').stem, '') 552 self.assertEqual(P('..').stem, '..') 553 self.assertEqual(P('/').stem, '') 554 self.assertEqual(P('a/b').stem, 'b') 555 self.assertEqual(P('a/b.py').stem, 'b') 556 self.assertEqual(P('a/.hgrc').stem, '.hgrc') 557 self.assertEqual(P('a/.hg.rc').stem, '.hg') 558 self.assertEqual(P('a/b.tar.gz').stem, 'b.tar') 559 self.assertEqual(P('a/Some name. Ending with a dot.').stem, 560 'Some name. Ending with a dot.') 561 562 def test_with_name_common(self): 563 P = self.cls 564 self.assertEqual(P('a/b').with_name('d.xml'), P('a/d.xml')) 565 self.assertEqual(P('/a/b').with_name('d.xml'), P('/a/d.xml')) 566 self.assertEqual(P('a/b.py').with_name('d.xml'), P('a/d.xml')) 567 self.assertEqual(P('/a/b.py').with_name('d.xml'), P('/a/d.xml')) 568 self.assertEqual(P('a/Dot ending.').with_name('d.xml'), P('a/d.xml')) 569 self.assertEqual(P('/a/Dot ending.').with_name('d.xml'), P('/a/d.xml')) 570 self.assertRaises(ValueError, P('').with_name, 'd.xml') 571 self.assertRaises(ValueError, P('.').with_name, 'd.xml') 572 self.assertRaises(ValueError, P('/').with_name, 'd.xml') 573 self.assertRaises(ValueError, P('a/b').with_name, '') 574 self.assertRaises(ValueError, P('a/b').with_name, '/c') 575 self.assertRaises(ValueError, P('a/b').with_name, 'c/') 576 self.assertRaises(ValueError, P('a/b').with_name, 'c/d') 577 578 def test_with_stem_common(self): 579 P = self.cls 580 self.assertEqual(P('a/b').with_stem('d'), P('a/d')) 581 self.assertEqual(P('/a/b').with_stem('d'), P('/a/d')) 582 self.assertEqual(P('a/b.py').with_stem('d'), P('a/d.py')) 583 self.assertEqual(P('/a/b.py').with_stem('d'), P('/a/d.py')) 584 self.assertEqual(P('/a/b.tar.gz').with_stem('d'), P('/a/d.gz')) 585 self.assertEqual(P('a/Dot ending.').with_stem('d'), P('a/d')) 586 self.assertEqual(P('/a/Dot ending.').with_stem('d'), P('/a/d')) 587 self.assertRaises(ValueError, P('').with_stem, 'd') 588 self.assertRaises(ValueError, P('.').with_stem, 'd') 589 self.assertRaises(ValueError, P('/').with_stem, 'd') 590 self.assertRaises(ValueError, P('a/b').with_stem, '') 591 self.assertRaises(ValueError, P('a/b').with_stem, '/c') 592 self.assertRaises(ValueError, P('a/b').with_stem, 'c/') 593 self.assertRaises(ValueError, P('a/b').with_stem, 'c/d') 594 595 def test_with_suffix_common(self): 596 P = self.cls 597 self.assertEqual(P('a/b').with_suffix('.gz'), P('a/b.gz')) 598 self.assertEqual(P('/a/b').with_suffix('.gz'), P('/a/b.gz')) 599 self.assertEqual(P('a/b.py').with_suffix('.gz'), P('a/b.gz')) 600 self.assertEqual(P('/a/b.py').with_suffix('.gz'), P('/a/b.gz')) 601 # Stripping suffix. 602 self.assertEqual(P('a/b.py').with_suffix(''), P('a/b')) 603 self.assertEqual(P('/a/b').with_suffix(''), P('/a/b')) 604 # Path doesn't have a "filename" component. 605 self.assertRaises(ValueError, P('').with_suffix, '.gz') 606 self.assertRaises(ValueError, P('.').with_suffix, '.gz') 607 self.assertRaises(ValueError, P('/').with_suffix, '.gz') 608 # Invalid suffix. 609 self.assertRaises(ValueError, P('a/b').with_suffix, 'gz') 610 self.assertRaises(ValueError, P('a/b').with_suffix, '/') 611 self.assertRaises(ValueError, P('a/b').with_suffix, '.') 612 self.assertRaises(ValueError, P('a/b').with_suffix, '/.gz') 613 self.assertRaises(ValueError, P('a/b').with_suffix, 'c/d') 614 self.assertRaises(ValueError, P('a/b').with_suffix, '.c/.d') 615 self.assertRaises(ValueError, P('a/b').with_suffix, './.d') 616 self.assertRaises(ValueError, P('a/b').with_suffix, '.d/.') 617 self.assertRaises(ValueError, P('a/b').with_suffix, 618 (self.flavour.sep, 'd')) 619 620 def test_relative_to_common(self): 621 P = self.cls 622 p = P('a/b') 623 self.assertRaises(TypeError, p.relative_to) 624 self.assertRaises(TypeError, p.relative_to, b'a') 625 self.assertEqual(p.relative_to(P()), P('a/b')) 626 self.assertEqual(p.relative_to(''), P('a/b')) 627 self.assertEqual(p.relative_to(P('a')), P('b')) 628 self.assertEqual(p.relative_to('a'), P('b')) 629 self.assertEqual(p.relative_to('a/'), P('b')) 630 self.assertEqual(p.relative_to(P('a/b')), P()) 631 self.assertEqual(p.relative_to('a/b'), P()) 632 # With several args. 633 self.assertEqual(p.relative_to('a', 'b'), P()) 634 # Unrelated paths. 635 self.assertRaises(ValueError, p.relative_to, P('c')) 636 self.assertRaises(ValueError, p.relative_to, P('a/b/c')) 637 self.assertRaises(ValueError, p.relative_to, P('a/c')) 638 self.assertRaises(ValueError, p.relative_to, P('/a')) 639 p = P('/a/b') 640 self.assertEqual(p.relative_to(P('/')), P('a/b')) 641 self.assertEqual(p.relative_to('/'), P('a/b')) 642 self.assertEqual(p.relative_to(P('/a')), P('b')) 643 self.assertEqual(p.relative_to('/a'), P('b')) 644 self.assertEqual(p.relative_to('/a/'), P('b')) 645 self.assertEqual(p.relative_to(P('/a/b')), P()) 646 self.assertEqual(p.relative_to('/a/b'), P()) 647 # Unrelated paths. 648 self.assertRaises(ValueError, p.relative_to, P('/c')) 649 self.assertRaises(ValueError, p.relative_to, P('/a/b/c')) 650 self.assertRaises(ValueError, p.relative_to, P('/a/c')) 651 self.assertRaises(ValueError, p.relative_to, P()) 652 self.assertRaises(ValueError, p.relative_to, '') 653 self.assertRaises(ValueError, p.relative_to, P('a')) 654 655 def test_is_relative_to_common(self): 656 P = self.cls 657 p = P('a/b') 658 self.assertRaises(TypeError, p.is_relative_to) 659 self.assertRaises(TypeError, p.is_relative_to, b'a') 660 self.assertTrue(p.is_relative_to(P())) 661 self.assertTrue(p.is_relative_to('')) 662 self.assertTrue(p.is_relative_to(P('a'))) 663 self.assertTrue(p.is_relative_to('a/')) 664 self.assertTrue(p.is_relative_to(P('a/b'))) 665 self.assertTrue(p.is_relative_to('a/b')) 666 # With several args. 667 self.assertTrue(p.is_relative_to('a', 'b')) 668 # Unrelated paths. 669 self.assertFalse(p.is_relative_to(P('c'))) 670 self.assertFalse(p.is_relative_to(P('a/b/c'))) 671 self.assertFalse(p.is_relative_to(P('a/c'))) 672 self.assertFalse(p.is_relative_to(P('/a'))) 673 p = P('/a/b') 674 self.assertTrue(p.is_relative_to(P('/'))) 675 self.assertTrue(p.is_relative_to('/')) 676 self.assertTrue(p.is_relative_to(P('/a'))) 677 self.assertTrue(p.is_relative_to('/a')) 678 self.assertTrue(p.is_relative_to('/a/')) 679 self.assertTrue(p.is_relative_to(P('/a/b'))) 680 self.assertTrue(p.is_relative_to('/a/b')) 681 # Unrelated paths. 682 self.assertFalse(p.is_relative_to(P('/c'))) 683 self.assertFalse(p.is_relative_to(P('/a/b/c'))) 684 self.assertFalse(p.is_relative_to(P('/a/c'))) 685 self.assertFalse(p.is_relative_to(P())) 686 self.assertFalse(p.is_relative_to('')) 687 self.assertFalse(p.is_relative_to(P('a'))) 688 689 def test_pickling_common(self): 690 P = self.cls 691 p = P('/a/b') 692 for proto in range(0, pickle.HIGHEST_PROTOCOL + 1): 693 dumped = pickle.dumps(p, proto) 694 pp = pickle.loads(dumped) 695 self.assertIs(pp.__class__, p.__class__) 696 self.assertEqual(pp, p) 697 self.assertEqual(hash(pp), hash(p)) 698 self.assertEqual(str(pp), str(p)) 699 700 701class PurePosixPathTest(_BasePurePathTest, unittest.TestCase): 702 cls = pathlib.PurePosixPath 703 704 def test_root(self): 705 P = self.cls 706 self.assertEqual(P('/a/b').root, '/') 707 self.assertEqual(P('///a/b').root, '/') 708 # POSIX special case for two leading slashes. 709 self.assertEqual(P('//a/b').root, '//') 710 711 def test_eq(self): 712 P = self.cls 713 self.assertNotEqual(P('a/b'), P('A/b')) 714 self.assertEqual(P('/a'), P('///a')) 715 self.assertNotEqual(P('/a'), P('//a')) 716 717 def test_as_uri(self): 718 P = self.cls 719 self.assertEqual(P('/').as_uri(), 'file:///') 720 self.assertEqual(P('/a/b.c').as_uri(), 'file:///a/b.c') 721 self.assertEqual(P('/a/b%#c').as_uri(), 'file:///a/b%25%23c') 722 723 def test_as_uri_non_ascii(self): 724 from urllib.parse import quote_from_bytes 725 P = self.cls 726 try: 727 os.fsencode('\xe9') 728 except UnicodeEncodeError: 729 self.skipTest("\\xe9 cannot be encoded to the filesystem encoding") 730 self.assertEqual(P('/a/b\xe9').as_uri(), 731 'file:///a/b' + quote_from_bytes(os.fsencode('\xe9'))) 732 733 def test_match(self): 734 P = self.cls 735 self.assertFalse(P('A.py').match('a.PY')) 736 737 def test_is_absolute(self): 738 P = self.cls 739 self.assertFalse(P().is_absolute()) 740 self.assertFalse(P('a').is_absolute()) 741 self.assertFalse(P('a/b/').is_absolute()) 742 self.assertTrue(P('/').is_absolute()) 743 self.assertTrue(P('/a').is_absolute()) 744 self.assertTrue(P('/a/b/').is_absolute()) 745 self.assertTrue(P('//a').is_absolute()) 746 self.assertTrue(P('//a/b').is_absolute()) 747 748 def test_is_reserved(self): 749 P = self.cls 750 self.assertIs(False, P('').is_reserved()) 751 self.assertIs(False, P('/').is_reserved()) 752 self.assertIs(False, P('/foo/bar').is_reserved()) 753 self.assertIs(False, P('/dev/con/PRN/NUL').is_reserved()) 754 755 def test_join(self): 756 P = self.cls 757 p = P('//a') 758 pp = p.joinpath('b') 759 self.assertEqual(pp, P('//a/b')) 760 pp = P('/a').joinpath('//c') 761 self.assertEqual(pp, P('//c')) 762 pp = P('//a').joinpath('/c') 763 self.assertEqual(pp, P('/c')) 764 765 def test_div(self): 766 # Basically the same as joinpath(). 767 P = self.cls 768 p = P('//a') 769 pp = p / 'b' 770 self.assertEqual(pp, P('//a/b')) 771 pp = P('/a') / '//c' 772 self.assertEqual(pp, P('//c')) 773 pp = P('//a') / '/c' 774 self.assertEqual(pp, P('/c')) 775 776 777class PureWindowsPathTest(_BasePurePathTest, unittest.TestCase): 778 cls = pathlib.PureWindowsPath 779 780 equivalences = _BasePurePathTest.equivalences.copy() 781 equivalences.update({ 782 'c:a': [ ('c:', 'a'), ('c:', 'a/'), ('/', 'c:', 'a') ], 783 'c:/a': [ 784 ('c:/', 'a'), ('c:', '/', 'a'), ('c:', '/a'), 785 ('/z', 'c:/', 'a'), ('//x/y', 'c:/', 'a'), 786 ], 787 '//a/b/': [ ('//a/b',) ], 788 '//a/b/c': [ 789 ('//a/b', 'c'), ('//a/b/', 'c'), 790 ], 791 }) 792 793 def test_str(self): 794 p = self.cls('a/b/c') 795 self.assertEqual(str(p), 'a\\b\\c') 796 p = self.cls('c:/a/b/c') 797 self.assertEqual(str(p), 'c:\\a\\b\\c') 798 p = self.cls('//a/b') 799 self.assertEqual(str(p), '\\\\a\\b\\') 800 p = self.cls('//a/b/c') 801 self.assertEqual(str(p), '\\\\a\\b\\c') 802 p = self.cls('//a/b/c/d') 803 self.assertEqual(str(p), '\\\\a\\b\\c\\d') 804 805 def test_str_subclass(self): 806 self._check_str_subclass('c:') 807 self._check_str_subclass('c:a') 808 self._check_str_subclass('c:a\\b.txt') 809 self._check_str_subclass('c:\\') 810 self._check_str_subclass('c:\\a') 811 self._check_str_subclass('c:\\a\\b.txt') 812 self._check_str_subclass('\\\\some\\share') 813 self._check_str_subclass('\\\\some\\share\\a') 814 self._check_str_subclass('\\\\some\\share\\a\\b.txt') 815 816 def test_eq(self): 817 P = self.cls 818 self.assertEqual(P('c:a/b'), P('c:a/b')) 819 self.assertEqual(P('c:a/b'), P('c:', 'a', 'b')) 820 self.assertNotEqual(P('c:a/b'), P('d:a/b')) 821 self.assertNotEqual(P('c:a/b'), P('c:/a/b')) 822 self.assertNotEqual(P('/a/b'), P('c:/a/b')) 823 # Case-insensitivity. 824 self.assertEqual(P('a/B'), P('A/b')) 825 self.assertEqual(P('C:a/B'), P('c:A/b')) 826 self.assertEqual(P('//Some/SHARE/a/B'), P('//somE/share/A/b')) 827 828 def test_as_uri(self): 829 P = self.cls 830 with self.assertRaises(ValueError): 831 P('/a/b').as_uri() 832 with self.assertRaises(ValueError): 833 P('c:a/b').as_uri() 834 self.assertEqual(P('c:/').as_uri(), 'file:///c:/') 835 self.assertEqual(P('c:/a/b.c').as_uri(), 'file:///c:/a/b.c') 836 self.assertEqual(P('c:/a/b%#c').as_uri(), 'file:///c:/a/b%25%23c') 837 self.assertEqual(P('c:/a/b\xe9').as_uri(), 'file:///c:/a/b%C3%A9') 838 self.assertEqual(P('//some/share/').as_uri(), 'file://some/share/') 839 self.assertEqual(P('//some/share/a/b.c').as_uri(), 840 'file://some/share/a/b.c') 841 self.assertEqual(P('//some/share/a/b%#c\xe9').as_uri(), 842 'file://some/share/a/b%25%23c%C3%A9') 843 844 def test_match_common(self): 845 P = self.cls 846 # Absolute patterns. 847 self.assertTrue(P('c:/b.py').match('/*.py')) 848 self.assertTrue(P('c:/b.py').match('c:*.py')) 849 self.assertTrue(P('c:/b.py').match('c:/*.py')) 850 self.assertFalse(P('d:/b.py').match('c:/*.py')) # wrong drive 851 self.assertFalse(P('b.py').match('/*.py')) 852 self.assertFalse(P('b.py').match('c:*.py')) 853 self.assertFalse(P('b.py').match('c:/*.py')) 854 self.assertFalse(P('c:b.py').match('/*.py')) 855 self.assertFalse(P('c:b.py').match('c:/*.py')) 856 self.assertFalse(P('/b.py').match('c:*.py')) 857 self.assertFalse(P('/b.py').match('c:/*.py')) 858 # UNC patterns. 859 self.assertTrue(P('//some/share/a.py').match('/*.py')) 860 self.assertTrue(P('//some/share/a.py').match('//some/share/*.py')) 861 self.assertFalse(P('//other/share/a.py').match('//some/share/*.py')) 862 self.assertFalse(P('//some/share/a/b.py').match('//some/share/*.py')) 863 # Case-insensitivity. 864 self.assertTrue(P('B.py').match('b.PY')) 865 self.assertTrue(P('c:/a/B.Py').match('C:/A/*.pY')) 866 self.assertTrue(P('//Some/Share/B.Py').match('//somE/sharE/*.pY')) 867 868 def test_ordering_common(self): 869 # Case-insensitivity. 870 def assertOrderedEqual(a, b): 871 self.assertLessEqual(a, b) 872 self.assertGreaterEqual(b, a) 873 P = self.cls 874 p = P('c:A/b') 875 q = P('C:a/B') 876 assertOrderedEqual(p, q) 877 self.assertFalse(p < q) 878 self.assertFalse(p > q) 879 p = P('//some/Share/A/b') 880 q = P('//Some/SHARE/a/B') 881 assertOrderedEqual(p, q) 882 self.assertFalse(p < q) 883 self.assertFalse(p > q) 884 885 def test_parts(self): 886 P = self.cls 887 p = P('c:a/b') 888 parts = p.parts 889 self.assertEqual(parts, ('c:', 'a', 'b')) 890 p = P('c:/a/b') 891 parts = p.parts 892 self.assertEqual(parts, ('c:\\', 'a', 'b')) 893 p = P('//a/b/c/d') 894 parts = p.parts 895 self.assertEqual(parts, ('\\\\a\\b\\', 'c', 'd')) 896 897 def test_parent(self): 898 # Anchored 899 P = self.cls 900 p = P('z:a/b/c') 901 self.assertEqual(p.parent, P('z:a/b')) 902 self.assertEqual(p.parent.parent, P('z:a')) 903 self.assertEqual(p.parent.parent.parent, P('z:')) 904 self.assertEqual(p.parent.parent.parent.parent, P('z:')) 905 p = P('z:/a/b/c') 906 self.assertEqual(p.parent, P('z:/a/b')) 907 self.assertEqual(p.parent.parent, P('z:/a')) 908 self.assertEqual(p.parent.parent.parent, P('z:/')) 909 self.assertEqual(p.parent.parent.parent.parent, P('z:/')) 910 p = P('//a/b/c/d') 911 self.assertEqual(p.parent, P('//a/b/c')) 912 self.assertEqual(p.parent.parent, P('//a/b')) 913 self.assertEqual(p.parent.parent.parent, P('//a/b')) 914 915 def test_parents(self): 916 # Anchored 917 P = self.cls 918 p = P('z:a/b/') 919 par = p.parents 920 self.assertEqual(len(par), 2) 921 self.assertEqual(par[0], P('z:a')) 922 self.assertEqual(par[1], P('z:')) 923 self.assertEqual(par[0:1], (P('z:a'),)) 924 self.assertEqual(par[:-1], (P('z:a'),)) 925 self.assertEqual(par[:2], (P('z:a'), P('z:'))) 926 self.assertEqual(par[1:], (P('z:'),)) 927 self.assertEqual(par[::2], (P('z:a'),)) 928 self.assertEqual(par[::-1], (P('z:'), P('z:a'))) 929 self.assertEqual(list(par), [P('z:a'), P('z:')]) 930 with self.assertRaises(IndexError): 931 par[2] 932 p = P('z:/a/b/') 933 par = p.parents 934 self.assertEqual(len(par), 2) 935 self.assertEqual(par[0], P('z:/a')) 936 self.assertEqual(par[1], P('z:/')) 937 self.assertEqual(par[0:1], (P('z:/a'),)) 938 self.assertEqual(par[0:-1], (P('z:/a'),)) 939 self.assertEqual(par[:2], (P('z:/a'), P('z:/'))) 940 self.assertEqual(par[1:], (P('z:/'),)) 941 self.assertEqual(par[::2], (P('z:/a'),)) 942 self.assertEqual(par[::-1], (P('z:/'), P('z:/a'),)) 943 self.assertEqual(list(par), [P('z:/a'), P('z:/')]) 944 with self.assertRaises(IndexError): 945 par[2] 946 p = P('//a/b/c/d') 947 par = p.parents 948 self.assertEqual(len(par), 2) 949 self.assertEqual(par[0], P('//a/b/c')) 950 self.assertEqual(par[1], P('//a/b')) 951 self.assertEqual(par[0:1], (P('//a/b/c'),)) 952 self.assertEqual(par[0:-1], (P('//a/b/c'),)) 953 self.assertEqual(par[:2], (P('//a/b/c'), P('//a/b'))) 954 self.assertEqual(par[1:], (P('//a/b'),)) 955 self.assertEqual(par[::2], (P('//a/b/c'),)) 956 self.assertEqual(par[::-1], (P('//a/b'), P('//a/b/c'))) 957 self.assertEqual(list(par), [P('//a/b/c'), P('//a/b')]) 958 with self.assertRaises(IndexError): 959 par[2] 960 961 def test_drive(self): 962 P = self.cls 963 self.assertEqual(P('c:').drive, 'c:') 964 self.assertEqual(P('c:a/b').drive, 'c:') 965 self.assertEqual(P('c:/').drive, 'c:') 966 self.assertEqual(P('c:/a/b/').drive, 'c:') 967 self.assertEqual(P('//a/b').drive, '\\\\a\\b') 968 self.assertEqual(P('//a/b/').drive, '\\\\a\\b') 969 self.assertEqual(P('//a/b/c/d').drive, '\\\\a\\b') 970 971 def test_root(self): 972 P = self.cls 973 self.assertEqual(P('c:').root, '') 974 self.assertEqual(P('c:a/b').root, '') 975 self.assertEqual(P('c:/').root, '\\') 976 self.assertEqual(P('c:/a/b/').root, '\\') 977 self.assertEqual(P('//a/b').root, '\\') 978 self.assertEqual(P('//a/b/').root, '\\') 979 self.assertEqual(P('//a/b/c/d').root, '\\') 980 981 def test_anchor(self): 982 P = self.cls 983 self.assertEqual(P('c:').anchor, 'c:') 984 self.assertEqual(P('c:a/b').anchor, 'c:') 985 self.assertEqual(P('c:/').anchor, 'c:\\') 986 self.assertEqual(P('c:/a/b/').anchor, 'c:\\') 987 self.assertEqual(P('//a/b').anchor, '\\\\a\\b\\') 988 self.assertEqual(P('//a/b/').anchor, '\\\\a\\b\\') 989 self.assertEqual(P('//a/b/c/d').anchor, '\\\\a\\b\\') 990 991 def test_name(self): 992 P = self.cls 993 self.assertEqual(P('c:').name, '') 994 self.assertEqual(P('c:/').name, '') 995 self.assertEqual(P('c:a/b').name, 'b') 996 self.assertEqual(P('c:/a/b').name, 'b') 997 self.assertEqual(P('c:a/b.py').name, 'b.py') 998 self.assertEqual(P('c:/a/b.py').name, 'b.py') 999 self.assertEqual(P('//My.py/Share.php').name, '') 1000 self.assertEqual(P('//My.py/Share.php/a/b').name, 'b') 1001 1002 def test_suffix(self): 1003 P = self.cls 1004 self.assertEqual(P('c:').suffix, '') 1005 self.assertEqual(P('c:/').suffix, '') 1006 self.assertEqual(P('c:a/b').suffix, '') 1007 self.assertEqual(P('c:/a/b').suffix, '') 1008 self.assertEqual(P('c:a/b.py').suffix, '.py') 1009 self.assertEqual(P('c:/a/b.py').suffix, '.py') 1010 self.assertEqual(P('c:a/.hgrc').suffix, '') 1011 self.assertEqual(P('c:/a/.hgrc').suffix, '') 1012 self.assertEqual(P('c:a/.hg.rc').suffix, '.rc') 1013 self.assertEqual(P('c:/a/.hg.rc').suffix, '.rc') 1014 self.assertEqual(P('c:a/b.tar.gz').suffix, '.gz') 1015 self.assertEqual(P('c:/a/b.tar.gz').suffix, '.gz') 1016 self.assertEqual(P('c:a/Some name. Ending with a dot.').suffix, '') 1017 self.assertEqual(P('c:/a/Some name. Ending with a dot.').suffix, '') 1018 self.assertEqual(P('//My.py/Share.php').suffix, '') 1019 self.assertEqual(P('//My.py/Share.php/a/b').suffix, '') 1020 1021 def test_suffixes(self): 1022 P = self.cls 1023 self.assertEqual(P('c:').suffixes, []) 1024 self.assertEqual(P('c:/').suffixes, []) 1025 self.assertEqual(P('c:a/b').suffixes, []) 1026 self.assertEqual(P('c:/a/b').suffixes, []) 1027 self.assertEqual(P('c:a/b.py').suffixes, ['.py']) 1028 self.assertEqual(P('c:/a/b.py').suffixes, ['.py']) 1029 self.assertEqual(P('c:a/.hgrc').suffixes, []) 1030 self.assertEqual(P('c:/a/.hgrc').suffixes, []) 1031 self.assertEqual(P('c:a/.hg.rc').suffixes, ['.rc']) 1032 self.assertEqual(P('c:/a/.hg.rc').suffixes, ['.rc']) 1033 self.assertEqual(P('c:a/b.tar.gz').suffixes, ['.tar', '.gz']) 1034 self.assertEqual(P('c:/a/b.tar.gz').suffixes, ['.tar', '.gz']) 1035 self.assertEqual(P('//My.py/Share.php').suffixes, []) 1036 self.assertEqual(P('//My.py/Share.php/a/b').suffixes, []) 1037 self.assertEqual(P('c:a/Some name. Ending with a dot.').suffixes, []) 1038 self.assertEqual(P('c:/a/Some name. Ending with a dot.').suffixes, []) 1039 1040 def test_stem(self): 1041 P = self.cls 1042 self.assertEqual(P('c:').stem, '') 1043 self.assertEqual(P('c:.').stem, '') 1044 self.assertEqual(P('c:..').stem, '..') 1045 self.assertEqual(P('c:/').stem, '') 1046 self.assertEqual(P('c:a/b').stem, 'b') 1047 self.assertEqual(P('c:a/b.py').stem, 'b') 1048 self.assertEqual(P('c:a/.hgrc').stem, '.hgrc') 1049 self.assertEqual(P('c:a/.hg.rc').stem, '.hg') 1050 self.assertEqual(P('c:a/b.tar.gz').stem, 'b.tar') 1051 self.assertEqual(P('c:a/Some name. Ending with a dot.').stem, 1052 'Some name. Ending with a dot.') 1053 1054 def test_with_name(self): 1055 P = self.cls 1056 self.assertEqual(P('c:a/b').with_name('d.xml'), P('c:a/d.xml')) 1057 self.assertEqual(P('c:/a/b').with_name('d.xml'), P('c:/a/d.xml')) 1058 self.assertEqual(P('c:a/Dot ending.').with_name('d.xml'), P('c:a/d.xml')) 1059 self.assertEqual(P('c:/a/Dot ending.').with_name('d.xml'), P('c:/a/d.xml')) 1060 self.assertRaises(ValueError, P('c:').with_name, 'd.xml') 1061 self.assertRaises(ValueError, P('c:/').with_name, 'd.xml') 1062 self.assertRaises(ValueError, P('//My/Share').with_name, 'd.xml') 1063 self.assertRaises(ValueError, P('c:a/b').with_name, 'd:') 1064 self.assertRaises(ValueError, P('c:a/b').with_name, 'd:e') 1065 self.assertRaises(ValueError, P('c:a/b').with_name, 'd:/e') 1066 self.assertRaises(ValueError, P('c:a/b').with_name, '//My/Share') 1067 1068 def test_with_stem(self): 1069 P = self.cls 1070 self.assertEqual(P('c:a/b').with_stem('d'), P('c:a/d')) 1071 self.assertEqual(P('c:/a/b').with_stem('d'), P('c:/a/d')) 1072 self.assertEqual(P('c:a/Dot ending.').with_stem('d'), P('c:a/d')) 1073 self.assertEqual(P('c:/a/Dot ending.').with_stem('d'), P('c:/a/d')) 1074 self.assertRaises(ValueError, P('c:').with_stem, 'd') 1075 self.assertRaises(ValueError, P('c:/').with_stem, 'd') 1076 self.assertRaises(ValueError, P('//My/Share').with_stem, 'd') 1077 self.assertRaises(ValueError, P('c:a/b').with_stem, 'd:') 1078 self.assertRaises(ValueError, P('c:a/b').with_stem, 'd:e') 1079 self.assertRaises(ValueError, P('c:a/b').with_stem, 'd:/e') 1080 self.assertRaises(ValueError, P('c:a/b').with_stem, '//My/Share') 1081 1082 def test_with_suffix(self): 1083 P = self.cls 1084 self.assertEqual(P('c:a/b').with_suffix('.gz'), P('c:a/b.gz')) 1085 self.assertEqual(P('c:/a/b').with_suffix('.gz'), P('c:/a/b.gz')) 1086 self.assertEqual(P('c:a/b.py').with_suffix('.gz'), P('c:a/b.gz')) 1087 self.assertEqual(P('c:/a/b.py').with_suffix('.gz'), P('c:/a/b.gz')) 1088 # Path doesn't have a "filename" component. 1089 self.assertRaises(ValueError, P('').with_suffix, '.gz') 1090 self.assertRaises(ValueError, P('.').with_suffix, '.gz') 1091 self.assertRaises(ValueError, P('/').with_suffix, '.gz') 1092 self.assertRaises(ValueError, P('//My/Share').with_suffix, '.gz') 1093 # Invalid suffix. 1094 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'gz') 1095 self.assertRaises(ValueError, P('c:a/b').with_suffix, '/') 1096 self.assertRaises(ValueError, P('c:a/b').with_suffix, '\\') 1097 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c:') 1098 self.assertRaises(ValueError, P('c:a/b').with_suffix, '/.gz') 1099 self.assertRaises(ValueError, P('c:a/b').with_suffix, '\\.gz') 1100 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c:.gz') 1101 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c/d') 1102 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c\\d') 1103 self.assertRaises(ValueError, P('c:a/b').with_suffix, '.c/d') 1104 self.assertRaises(ValueError, P('c:a/b').with_suffix, '.c\\d') 1105 1106 def test_relative_to(self): 1107 P = self.cls 1108 p = P('C:Foo/Bar') 1109 self.assertEqual(p.relative_to(P('c:')), P('Foo/Bar')) 1110 self.assertEqual(p.relative_to('c:'), P('Foo/Bar')) 1111 self.assertEqual(p.relative_to(P('c:foO')), P('Bar')) 1112 self.assertEqual(p.relative_to('c:foO'), P('Bar')) 1113 self.assertEqual(p.relative_to('c:foO/'), P('Bar')) 1114 self.assertEqual(p.relative_to(P('c:foO/baR')), P()) 1115 self.assertEqual(p.relative_to('c:foO/baR'), P()) 1116 # Unrelated paths. 1117 self.assertRaises(ValueError, p.relative_to, P()) 1118 self.assertRaises(ValueError, p.relative_to, '') 1119 self.assertRaises(ValueError, p.relative_to, P('d:')) 1120 self.assertRaises(ValueError, p.relative_to, P('/')) 1121 self.assertRaises(ValueError, p.relative_to, P('Foo')) 1122 self.assertRaises(ValueError, p.relative_to, P('/Foo')) 1123 self.assertRaises(ValueError, p.relative_to, P('C:/Foo')) 1124 self.assertRaises(ValueError, p.relative_to, P('C:Foo/Bar/Baz')) 1125 self.assertRaises(ValueError, p.relative_to, P('C:Foo/Baz')) 1126 p = P('C:/Foo/Bar') 1127 self.assertEqual(p.relative_to(P('c:')), P('/Foo/Bar')) 1128 self.assertEqual(p.relative_to('c:'), P('/Foo/Bar')) 1129 self.assertEqual(str(p.relative_to(P('c:'))), '\\Foo\\Bar') 1130 self.assertEqual(str(p.relative_to('c:')), '\\Foo\\Bar') 1131 self.assertEqual(p.relative_to(P('c:/')), P('Foo/Bar')) 1132 self.assertEqual(p.relative_to('c:/'), P('Foo/Bar')) 1133 self.assertEqual(p.relative_to(P('c:/foO')), P('Bar')) 1134 self.assertEqual(p.relative_to('c:/foO'), P('Bar')) 1135 self.assertEqual(p.relative_to('c:/foO/'), P('Bar')) 1136 self.assertEqual(p.relative_to(P('c:/foO/baR')), P()) 1137 self.assertEqual(p.relative_to('c:/foO/baR'), P()) 1138 # Unrelated paths. 1139 self.assertRaises(ValueError, p.relative_to, P('C:/Baz')) 1140 self.assertRaises(ValueError, p.relative_to, P('C:/Foo/Bar/Baz')) 1141 self.assertRaises(ValueError, p.relative_to, P('C:/Foo/Baz')) 1142 self.assertRaises(ValueError, p.relative_to, P('C:Foo')) 1143 self.assertRaises(ValueError, p.relative_to, P('d:')) 1144 self.assertRaises(ValueError, p.relative_to, P('d:/')) 1145 self.assertRaises(ValueError, p.relative_to, P('/')) 1146 self.assertRaises(ValueError, p.relative_to, P('/Foo')) 1147 self.assertRaises(ValueError, p.relative_to, P('//C/Foo')) 1148 # UNC paths. 1149 p = P('//Server/Share/Foo/Bar') 1150 self.assertEqual(p.relative_to(P('//sErver/sHare')), P('Foo/Bar')) 1151 self.assertEqual(p.relative_to('//sErver/sHare'), P('Foo/Bar')) 1152 self.assertEqual(p.relative_to('//sErver/sHare/'), P('Foo/Bar')) 1153 self.assertEqual(p.relative_to(P('//sErver/sHare/Foo')), P('Bar')) 1154 self.assertEqual(p.relative_to('//sErver/sHare/Foo'), P('Bar')) 1155 self.assertEqual(p.relative_to('//sErver/sHare/Foo/'), P('Bar')) 1156 self.assertEqual(p.relative_to(P('//sErver/sHare/Foo/Bar')), P()) 1157 self.assertEqual(p.relative_to('//sErver/sHare/Foo/Bar'), P()) 1158 # Unrelated paths. 1159 self.assertRaises(ValueError, p.relative_to, P('/Server/Share/Foo')) 1160 self.assertRaises(ValueError, p.relative_to, P('c:/Server/Share/Foo')) 1161 self.assertRaises(ValueError, p.relative_to, P('//z/Share/Foo')) 1162 self.assertRaises(ValueError, p.relative_to, P('//Server/z/Foo')) 1163 1164 def test_is_relative_to(self): 1165 P = self.cls 1166 p = P('C:Foo/Bar') 1167 self.assertTrue(p.is_relative_to(P('c:'))) 1168 self.assertTrue(p.is_relative_to('c:')) 1169 self.assertTrue(p.is_relative_to(P('c:foO'))) 1170 self.assertTrue(p.is_relative_to('c:foO')) 1171 self.assertTrue(p.is_relative_to('c:foO/')) 1172 self.assertTrue(p.is_relative_to(P('c:foO/baR'))) 1173 self.assertTrue(p.is_relative_to('c:foO/baR')) 1174 # Unrelated paths. 1175 self.assertFalse(p.is_relative_to(P())) 1176 self.assertFalse(p.is_relative_to('')) 1177 self.assertFalse(p.is_relative_to(P('d:'))) 1178 self.assertFalse(p.is_relative_to(P('/'))) 1179 self.assertFalse(p.is_relative_to(P('Foo'))) 1180 self.assertFalse(p.is_relative_to(P('/Foo'))) 1181 self.assertFalse(p.is_relative_to(P('C:/Foo'))) 1182 self.assertFalse(p.is_relative_to(P('C:Foo/Bar/Baz'))) 1183 self.assertFalse(p.is_relative_to(P('C:Foo/Baz'))) 1184 p = P('C:/Foo/Bar') 1185 self.assertTrue(p.is_relative_to('c:')) 1186 self.assertTrue(p.is_relative_to(P('c:/'))) 1187 self.assertTrue(p.is_relative_to(P('c:/foO'))) 1188 self.assertTrue(p.is_relative_to('c:/foO/')) 1189 self.assertTrue(p.is_relative_to(P('c:/foO/baR'))) 1190 self.assertTrue(p.is_relative_to('c:/foO/baR')) 1191 # Unrelated paths. 1192 self.assertFalse(p.is_relative_to(P('C:/Baz'))) 1193 self.assertFalse(p.is_relative_to(P('C:/Foo/Bar/Baz'))) 1194 self.assertFalse(p.is_relative_to(P('C:/Foo/Baz'))) 1195 self.assertFalse(p.is_relative_to(P('C:Foo'))) 1196 self.assertFalse(p.is_relative_to(P('d:'))) 1197 self.assertFalse(p.is_relative_to(P('d:/'))) 1198 self.assertFalse(p.is_relative_to(P('/'))) 1199 self.assertFalse(p.is_relative_to(P('/Foo'))) 1200 self.assertFalse(p.is_relative_to(P('//C/Foo'))) 1201 # UNC paths. 1202 p = P('//Server/Share/Foo/Bar') 1203 self.assertTrue(p.is_relative_to(P('//sErver/sHare'))) 1204 self.assertTrue(p.is_relative_to('//sErver/sHare')) 1205 self.assertTrue(p.is_relative_to('//sErver/sHare/')) 1206 self.assertTrue(p.is_relative_to(P('//sErver/sHare/Foo'))) 1207 self.assertTrue(p.is_relative_to('//sErver/sHare/Foo')) 1208 self.assertTrue(p.is_relative_to('//sErver/sHare/Foo/')) 1209 self.assertTrue(p.is_relative_to(P('//sErver/sHare/Foo/Bar'))) 1210 self.assertTrue(p.is_relative_to('//sErver/sHare/Foo/Bar')) 1211 # Unrelated paths. 1212 self.assertFalse(p.is_relative_to(P('/Server/Share/Foo'))) 1213 self.assertFalse(p.is_relative_to(P('c:/Server/Share/Foo'))) 1214 self.assertFalse(p.is_relative_to(P('//z/Share/Foo'))) 1215 self.assertFalse(p.is_relative_to(P('//Server/z/Foo'))) 1216 1217 def test_is_absolute(self): 1218 P = self.cls 1219 # Under NT, only paths with both a drive and a root are absolute. 1220 self.assertFalse(P().is_absolute()) 1221 self.assertFalse(P('a').is_absolute()) 1222 self.assertFalse(P('a/b/').is_absolute()) 1223 self.assertFalse(P('/').is_absolute()) 1224 self.assertFalse(P('/a').is_absolute()) 1225 self.assertFalse(P('/a/b/').is_absolute()) 1226 self.assertFalse(P('c:').is_absolute()) 1227 self.assertFalse(P('c:a').is_absolute()) 1228 self.assertFalse(P('c:a/b/').is_absolute()) 1229 self.assertTrue(P('c:/').is_absolute()) 1230 self.assertTrue(P('c:/a').is_absolute()) 1231 self.assertTrue(P('c:/a/b/').is_absolute()) 1232 # UNC paths are absolute by definition. 1233 self.assertTrue(P('//a/b').is_absolute()) 1234 self.assertTrue(P('//a/b/').is_absolute()) 1235 self.assertTrue(P('//a/b/c').is_absolute()) 1236 self.assertTrue(P('//a/b/c/d').is_absolute()) 1237 1238 def test_join(self): 1239 P = self.cls 1240 p = P('C:/a/b') 1241 pp = p.joinpath('x/y') 1242 self.assertEqual(pp, P('C:/a/b/x/y')) 1243 pp = p.joinpath('/x/y') 1244 self.assertEqual(pp, P('C:/x/y')) 1245 # Joining with a different drive => the first path is ignored, even 1246 # if the second path is relative. 1247 pp = p.joinpath('D:x/y') 1248 self.assertEqual(pp, P('D:x/y')) 1249 pp = p.joinpath('D:/x/y') 1250 self.assertEqual(pp, P('D:/x/y')) 1251 pp = p.joinpath('//host/share/x/y') 1252 self.assertEqual(pp, P('//host/share/x/y')) 1253 # Joining with the same drive => the first path is appended to if 1254 # the second path is relative. 1255 pp = p.joinpath('c:x/y') 1256 self.assertEqual(pp, P('C:/a/b/x/y')) 1257 pp = p.joinpath('c:/x/y') 1258 self.assertEqual(pp, P('C:/x/y')) 1259 1260 def test_div(self): 1261 # Basically the same as joinpath(). 1262 P = self.cls 1263 p = P('C:/a/b') 1264 self.assertEqual(p / 'x/y', P('C:/a/b/x/y')) 1265 self.assertEqual(p / 'x' / 'y', P('C:/a/b/x/y')) 1266 self.assertEqual(p / '/x/y', P('C:/x/y')) 1267 self.assertEqual(p / '/x' / 'y', P('C:/x/y')) 1268 # Joining with a different drive => the first path is ignored, even 1269 # if the second path is relative. 1270 self.assertEqual(p / 'D:x/y', P('D:x/y')) 1271 self.assertEqual(p / 'D:' / 'x/y', P('D:x/y')) 1272 self.assertEqual(p / 'D:/x/y', P('D:/x/y')) 1273 self.assertEqual(p / 'D:' / '/x/y', P('D:/x/y')) 1274 self.assertEqual(p / '//host/share/x/y', P('//host/share/x/y')) 1275 # Joining with the same drive => the first path is appended to if 1276 # the second path is relative. 1277 self.assertEqual(p / 'c:x/y', P('C:/a/b/x/y')) 1278 self.assertEqual(p / 'c:/x/y', P('C:/x/y')) 1279 1280 def test_is_reserved(self): 1281 P = self.cls 1282 self.assertIs(False, P('').is_reserved()) 1283 self.assertIs(False, P('/').is_reserved()) 1284 self.assertIs(False, P('/foo/bar').is_reserved()) 1285 # UNC paths are never reserved. 1286 self.assertIs(False, P('//my/share/nul/con/aux').is_reserved()) 1287 # Case-insensitive DOS-device names are reserved. 1288 self.assertIs(True, P('nul').is_reserved()) 1289 self.assertIs(True, P('aux').is_reserved()) 1290 self.assertIs(True, P('prn').is_reserved()) 1291 self.assertIs(True, P('con').is_reserved()) 1292 self.assertIs(True, P('conin$').is_reserved()) 1293 self.assertIs(True, P('conout$').is_reserved()) 1294 # COM/LPT + 1-9 or + superscript 1-3 are reserved. 1295 self.assertIs(True, P('COM1').is_reserved()) 1296 self.assertIs(True, P('LPT9').is_reserved()) 1297 self.assertIs(True, P('com\xb9').is_reserved()) 1298 self.assertIs(True, P('com\xb2').is_reserved()) 1299 self.assertIs(True, P('lpt\xb3').is_reserved()) 1300 # DOS-device name mataching ignores characters after a dot or 1301 # a colon and also ignores trailing spaces. 1302 self.assertIs(True, P('NUL.txt').is_reserved()) 1303 self.assertIs(True, P('PRN ').is_reserved()) 1304 self.assertIs(True, P('AUX .txt').is_reserved()) 1305 self.assertIs(True, P('COM1:bar').is_reserved()) 1306 self.assertIs(True, P('LPT9 :bar').is_reserved()) 1307 # DOS-device names are only matched at the beginning 1308 # of a path component. 1309 self.assertIs(False, P('bar.com9').is_reserved()) 1310 self.assertIs(False, P('bar.lpt9').is_reserved()) 1311 # Only the last path component matters. 1312 self.assertIs(True, P('c:/baz/con/NUL').is_reserved()) 1313 self.assertIs(False, P('c:/NUL/con/baz').is_reserved()) 1314 1315class PurePathTest(_BasePurePathTest, unittest.TestCase): 1316 cls = pathlib.PurePath 1317 1318 def test_concrete_class(self): 1319 p = self.cls('a') 1320 self.assertIs(type(p), 1321 pathlib.PureWindowsPath if os.name == 'nt' else pathlib.PurePosixPath) 1322 1323 def test_different_flavours_unequal(self): 1324 p = pathlib.PurePosixPath('a') 1325 q = pathlib.PureWindowsPath('a') 1326 self.assertNotEqual(p, q) 1327 1328 def test_different_flavours_unordered(self): 1329 p = pathlib.PurePosixPath('a') 1330 q = pathlib.PureWindowsPath('a') 1331 with self.assertRaises(TypeError): 1332 p < q 1333 with self.assertRaises(TypeError): 1334 p <= q 1335 with self.assertRaises(TypeError): 1336 p > q 1337 with self.assertRaises(TypeError): 1338 p >= q 1339 1340 1341# 1342# Tests for the concrete classes. 1343# 1344 1345# Make sure any symbolic links in the base test path are resolved. 1346BASE = os.path.realpath(TESTFN) 1347join = lambda *x: os.path.join(BASE, *x) 1348rel_join = lambda *x: os.path.join(TESTFN, *x) 1349 1350only_nt = unittest.skipIf(os.name != 'nt', 1351 'test requires a Windows-compatible system') 1352only_posix = unittest.skipIf(os.name == 'nt', 1353 'test requires a POSIX-compatible system') 1354 1355@only_posix 1356class PosixPathAsPureTest(PurePosixPathTest): 1357 cls = pathlib.PosixPath 1358 1359@only_nt 1360class WindowsPathAsPureTest(PureWindowsPathTest): 1361 cls = pathlib.WindowsPath 1362 1363 def test_owner(self): 1364 P = self.cls 1365 with self.assertRaises(NotImplementedError): 1366 P('c:/').owner() 1367 1368 def test_group(self): 1369 P = self.cls 1370 with self.assertRaises(NotImplementedError): 1371 P('c:/').group() 1372 1373 1374class _BasePathTest(object): 1375 """Tests for the FS-accessing functionalities of the Path classes.""" 1376 1377 # (BASE) 1378 # | 1379 # |-- brokenLink -> non-existing 1380 # |-- dirA 1381 # | `-- linkC -> ../dirB 1382 # |-- dirB 1383 # | |-- fileB 1384 # | `-- linkD -> ../dirB 1385 # |-- dirC 1386 # | |-- dirD 1387 # | | `-- fileD 1388 # | `-- fileC 1389 # |-- dirE # No permissions 1390 # |-- fileA 1391 # |-- linkA -> fileA 1392 # |-- linkB -> dirB 1393 # `-- brokenLinkLoop -> brokenLinkLoop 1394 # 1395 1396 def setUp(self): 1397 def cleanup(): 1398 os.chmod(join('dirE'), 0o777) 1399 os_helper.rmtree(BASE) 1400 self.addCleanup(cleanup) 1401 os.mkdir(BASE) 1402 os.mkdir(join('dirA')) 1403 os.mkdir(join('dirB')) 1404 os.mkdir(join('dirC')) 1405 os.mkdir(join('dirC', 'dirD')) 1406 os.mkdir(join('dirE')) 1407 with open(join('fileA'), 'wb') as f: 1408 f.write(b"this is file A\n") 1409 with open(join('dirB', 'fileB'), 'wb') as f: 1410 f.write(b"this is file B\n") 1411 with open(join('dirC', 'fileC'), 'wb') as f: 1412 f.write(b"this is file C\n") 1413 with open(join('dirC', 'dirD', 'fileD'), 'wb') as f: 1414 f.write(b"this is file D\n") 1415 os.chmod(join('dirE'), 0) 1416 if os_helper.can_symlink(): 1417 # Relative symlinks. 1418 os.symlink('fileA', join('linkA')) 1419 os.symlink('non-existing', join('brokenLink')) 1420 self.dirlink('dirB', join('linkB')) 1421 self.dirlink(os.path.join('..', 'dirB'), join('dirA', 'linkC')) 1422 # This one goes upwards, creating a loop. 1423 self.dirlink(os.path.join('..', 'dirB'), join('dirB', 'linkD')) 1424 # Broken symlink (pointing to itself). 1425 os.symlink('brokenLinkLoop', join('brokenLinkLoop')) 1426 1427 if os.name == 'nt': 1428 # Workaround for http://bugs.python.org/issue13772. 1429 def dirlink(self, src, dest): 1430 os.symlink(src, dest, target_is_directory=True) 1431 else: 1432 def dirlink(self, src, dest): 1433 os.symlink(src, dest) 1434 1435 def assertSame(self, path_a, path_b): 1436 self.assertTrue(os.path.samefile(str(path_a), str(path_b)), 1437 "%r and %r don't point to the same file" % 1438 (path_a, path_b)) 1439 1440 def assertFileNotFound(self, func, *args, **kwargs): 1441 with self.assertRaises(FileNotFoundError) as cm: 1442 func(*args, **kwargs) 1443 self.assertEqual(cm.exception.errno, errno.ENOENT) 1444 1445 def assertEqualNormCase(self, path_a, path_b): 1446 self.assertEqual(os.path.normcase(path_a), os.path.normcase(path_b)) 1447 1448 def _test_cwd(self, p): 1449 q = self.cls(os.getcwd()) 1450 self.assertEqual(p, q) 1451 self.assertEqualNormCase(str(p), str(q)) 1452 self.assertIs(type(p), type(q)) 1453 self.assertTrue(p.is_absolute()) 1454 1455 def test_cwd(self): 1456 p = self.cls.cwd() 1457 self._test_cwd(p) 1458 1459 def _test_home(self, p): 1460 q = self.cls(os.path.expanduser('~')) 1461 self.assertEqual(p, q) 1462 self.assertEqualNormCase(str(p), str(q)) 1463 self.assertIs(type(p), type(q)) 1464 self.assertTrue(p.is_absolute()) 1465 1466 def test_home(self): 1467 with os_helper.EnvironmentVarGuard() as env: 1468 self._test_home(self.cls.home()) 1469 1470 env.clear() 1471 env['USERPROFILE'] = os.path.join(BASE, 'userprofile') 1472 self._test_home(self.cls.home()) 1473 1474 # bpo-38883: ignore `HOME` when set on windows 1475 env['HOME'] = os.path.join(BASE, 'home') 1476 self._test_home(self.cls.home()) 1477 1478 def test_samefile(self): 1479 fileA_path = os.path.join(BASE, 'fileA') 1480 fileB_path = os.path.join(BASE, 'dirB', 'fileB') 1481 p = self.cls(fileA_path) 1482 pp = self.cls(fileA_path) 1483 q = self.cls(fileB_path) 1484 self.assertTrue(p.samefile(fileA_path)) 1485 self.assertTrue(p.samefile(pp)) 1486 self.assertFalse(p.samefile(fileB_path)) 1487 self.assertFalse(p.samefile(q)) 1488 # Test the non-existent file case 1489 non_existent = os.path.join(BASE, 'foo') 1490 r = self.cls(non_existent) 1491 self.assertRaises(FileNotFoundError, p.samefile, r) 1492 self.assertRaises(FileNotFoundError, p.samefile, non_existent) 1493 self.assertRaises(FileNotFoundError, r.samefile, p) 1494 self.assertRaises(FileNotFoundError, r.samefile, non_existent) 1495 self.assertRaises(FileNotFoundError, r.samefile, r) 1496 self.assertRaises(FileNotFoundError, r.samefile, non_existent) 1497 1498 def test_empty_path(self): 1499 # The empty path points to '.' 1500 p = self.cls('') 1501 self.assertEqual(p.stat(), os.stat('.')) 1502 1503 def test_expanduser_common(self): 1504 P = self.cls 1505 p = P('~') 1506 self.assertEqual(p.expanduser(), P(os.path.expanduser('~'))) 1507 p = P('foo') 1508 self.assertEqual(p.expanduser(), p) 1509 p = P('/~') 1510 self.assertEqual(p.expanduser(), p) 1511 p = P('../~') 1512 self.assertEqual(p.expanduser(), p) 1513 p = P(P('').absolute().anchor) / '~' 1514 self.assertEqual(p.expanduser(), p) 1515 1516 def test_exists(self): 1517 P = self.cls 1518 p = P(BASE) 1519 self.assertIs(True, p.exists()) 1520 self.assertIs(True, (p / 'dirA').exists()) 1521 self.assertIs(True, (p / 'fileA').exists()) 1522 self.assertIs(False, (p / 'fileA' / 'bah').exists()) 1523 if os_helper.can_symlink(): 1524 self.assertIs(True, (p / 'linkA').exists()) 1525 self.assertIs(True, (p / 'linkB').exists()) 1526 self.assertIs(True, (p / 'linkB' / 'fileB').exists()) 1527 self.assertIs(False, (p / 'linkA' / 'bah').exists()) 1528 self.assertIs(False, (p / 'foo').exists()) 1529 self.assertIs(False, P('/xyzzy').exists()) 1530 self.assertIs(False, P(BASE + '\udfff').exists()) 1531 self.assertIs(False, P(BASE + '\x00').exists()) 1532 1533 def test_open_common(self): 1534 p = self.cls(BASE) 1535 with (p / 'fileA').open('r') as f: 1536 self.assertIsInstance(f, io.TextIOBase) 1537 self.assertEqual(f.read(), "this is file A\n") 1538 with (p / 'fileA').open('rb') as f: 1539 self.assertIsInstance(f, io.BufferedIOBase) 1540 self.assertEqual(f.read().strip(), b"this is file A") 1541 with (p / 'fileA').open('rb', buffering=0) as f: 1542 self.assertIsInstance(f, io.RawIOBase) 1543 self.assertEqual(f.read().strip(), b"this is file A") 1544 1545 def test_read_write_bytes(self): 1546 p = self.cls(BASE) 1547 (p / 'fileA').write_bytes(b'abcdefg') 1548 self.assertEqual((p / 'fileA').read_bytes(), b'abcdefg') 1549 # Check that trying to write str does not truncate the file. 1550 self.assertRaises(TypeError, (p / 'fileA').write_bytes, 'somestr') 1551 self.assertEqual((p / 'fileA').read_bytes(), b'abcdefg') 1552 1553 def test_read_write_text(self): 1554 p = self.cls(BASE) 1555 (p / 'fileA').write_text('äbcdefg', encoding='latin-1') 1556 self.assertEqual((p / 'fileA').read_text( 1557 encoding='utf-8', errors='ignore'), 'bcdefg') 1558 # Check that trying to write bytes does not truncate the file. 1559 self.assertRaises(TypeError, (p / 'fileA').write_text, b'somebytes') 1560 self.assertEqual((p / 'fileA').read_text(encoding='latin-1'), 'äbcdefg') 1561 1562 def test_write_text_with_newlines(self): 1563 p = self.cls(BASE) 1564 # Check that `\n` character change nothing 1565 (p / 'fileA').write_text('abcde\r\nfghlk\n\rmnopq', newline='\n') 1566 self.assertEqual((p / 'fileA').read_bytes(), 1567 b'abcde\r\nfghlk\n\rmnopq') 1568 # Check that `\r` character replaces `\n` 1569 (p / 'fileA').write_text('abcde\r\nfghlk\n\rmnopq', newline='\r') 1570 self.assertEqual((p / 'fileA').read_bytes(), 1571 b'abcde\r\rfghlk\r\rmnopq') 1572 # Check that `\r\n` character replaces `\n` 1573 (p / 'fileA').write_text('abcde\r\nfghlk\n\rmnopq', newline='\r\n') 1574 self.assertEqual((p / 'fileA').read_bytes(), 1575 b'abcde\r\r\nfghlk\r\n\rmnopq') 1576 # Check that no argument passed will change `\n` to `os.linesep` 1577 os_linesep_byte = bytes(os.linesep, encoding='ascii') 1578 (p / 'fileA').write_text('abcde\nfghlk\n\rmnopq') 1579 self.assertEqual((p / 'fileA').read_bytes(), 1580 b'abcde' + os_linesep_byte + b'fghlk' + os_linesep_byte + b'\rmnopq') 1581 1582 def test_iterdir(self): 1583 P = self.cls 1584 p = P(BASE) 1585 it = p.iterdir() 1586 paths = set(it) 1587 expected = ['dirA', 'dirB', 'dirC', 'dirE', 'fileA'] 1588 if os_helper.can_symlink(): 1589 expected += ['linkA', 'linkB', 'brokenLink', 'brokenLinkLoop'] 1590 self.assertEqual(paths, { P(BASE, q) for q in expected }) 1591 1592 @os_helper.skip_unless_symlink 1593 def test_iterdir_symlink(self): 1594 # __iter__ on a symlink to a directory. 1595 P = self.cls 1596 p = P(BASE, 'linkB') 1597 paths = set(p.iterdir()) 1598 expected = { P(BASE, 'linkB', q) for q in ['fileB', 'linkD'] } 1599 self.assertEqual(paths, expected) 1600 1601 def test_iterdir_nodir(self): 1602 # __iter__ on something that is not a directory. 1603 p = self.cls(BASE, 'fileA') 1604 with self.assertRaises(OSError) as cm: 1605 next(p.iterdir()) 1606 # ENOENT or EINVAL under Windows, ENOTDIR otherwise 1607 # (see issue #12802). 1608 self.assertIn(cm.exception.errno, (errno.ENOTDIR, 1609 errno.ENOENT, errno.EINVAL)) 1610 1611 def test_glob_common(self): 1612 def _check(glob, expected): 1613 self.assertEqual(set(glob), { P(BASE, q) for q in expected }) 1614 P = self.cls 1615 p = P(BASE) 1616 it = p.glob("fileA") 1617 self.assertIsInstance(it, collections.abc.Iterator) 1618 _check(it, ["fileA"]) 1619 _check(p.glob("fileB"), []) 1620 _check(p.glob("dir*/file*"), ["dirB/fileB", "dirC/fileC"]) 1621 if not os_helper.can_symlink(): 1622 _check(p.glob("*A"), ['dirA', 'fileA']) 1623 else: 1624 _check(p.glob("*A"), ['dirA', 'fileA', 'linkA']) 1625 if not os_helper.can_symlink(): 1626 _check(p.glob("*B/*"), ['dirB/fileB']) 1627 else: 1628 _check(p.glob("*B/*"), ['dirB/fileB', 'dirB/linkD', 1629 'linkB/fileB', 'linkB/linkD']) 1630 if not os_helper.can_symlink(): 1631 _check(p.glob("*/fileB"), ['dirB/fileB']) 1632 else: 1633 _check(p.glob("*/fileB"), ['dirB/fileB', 'linkB/fileB']) 1634 1635 def test_rglob_common(self): 1636 def _check(glob, expected): 1637 self.assertEqual(set(glob), { P(BASE, q) for q in expected }) 1638 P = self.cls 1639 p = P(BASE) 1640 it = p.rglob("fileA") 1641 self.assertIsInstance(it, collections.abc.Iterator) 1642 _check(it, ["fileA"]) 1643 _check(p.rglob("fileB"), ["dirB/fileB"]) 1644 _check(p.rglob("*/fileA"), []) 1645 if not os_helper.can_symlink(): 1646 _check(p.rglob("*/fileB"), ["dirB/fileB"]) 1647 else: 1648 _check(p.rglob("*/fileB"), ["dirB/fileB", "dirB/linkD/fileB", 1649 "linkB/fileB", "dirA/linkC/fileB"]) 1650 _check(p.rglob("file*"), ["fileA", "dirB/fileB", 1651 "dirC/fileC", "dirC/dirD/fileD"]) 1652 p = P(BASE, "dirC") 1653 _check(p.rglob("file*"), ["dirC/fileC", "dirC/dirD/fileD"]) 1654 _check(p.rglob("*/*"), ["dirC/dirD/fileD"]) 1655 1656 @os_helper.skip_unless_symlink 1657 def test_rglob_symlink_loop(self): 1658 # Don't get fooled by symlink loops (Issue #26012). 1659 P = self.cls 1660 p = P(BASE) 1661 given = set(p.rglob('*')) 1662 expect = {'brokenLink', 1663 'dirA', 'dirA/linkC', 1664 'dirB', 'dirB/fileB', 'dirB/linkD', 1665 'dirC', 'dirC/dirD', 'dirC/dirD/fileD', 'dirC/fileC', 1666 'dirE', 1667 'fileA', 1668 'linkA', 1669 'linkB', 1670 'brokenLinkLoop', 1671 } 1672 self.assertEqual(given, {p / x for x in expect}) 1673 1674 def test_glob_many_open_files(self): 1675 depth = 30 1676 P = self.cls 1677 base = P(BASE) / 'deep' 1678 p = P(base, *(['d']*depth)) 1679 p.mkdir(parents=True) 1680 pattern = '/'.join(['*'] * depth) 1681 iters = [base.glob(pattern) for j in range(100)] 1682 for it in iters: 1683 self.assertEqual(next(it), p) 1684 iters = [base.rglob('d') for j in range(100)] 1685 p = base 1686 for i in range(depth): 1687 p = p / 'd' 1688 for it in iters: 1689 self.assertEqual(next(it), p) 1690 1691 def test_glob_dotdot(self): 1692 # ".." is not special in globs. 1693 P = self.cls 1694 p = P(BASE) 1695 self.assertEqual(set(p.glob("..")), { P(BASE, "..") }) 1696 self.assertEqual(set(p.glob("dirA/../file*")), { P(BASE, "dirA/../fileA") }) 1697 self.assertEqual(set(p.glob("../xyzzy")), set()) 1698 1699 @os_helper.skip_unless_symlink 1700 def test_glob_permissions(self): 1701 # See bpo-38894 1702 P = self.cls 1703 base = P(BASE) / 'permissions' 1704 base.mkdir() 1705 1706 file1 = base / "file1" 1707 file1.touch() 1708 file2 = base / "file2" 1709 file2.touch() 1710 1711 subdir = base / "subdir" 1712 1713 file3 = base / "file3" 1714 file3.symlink_to(subdir / "other") 1715 1716 # Patching is needed to avoid relying on the filesystem 1717 # to return the order of the files as the error will not 1718 # happen if the symlink is the last item. 1719 1720 with mock.patch("os.scandir") as scandir: 1721 scandir.return_value = sorted(os.scandir(base)) 1722 self.assertEqual(len(set(base.glob("*"))), 3) 1723 1724 subdir.mkdir() 1725 1726 with mock.patch("os.scandir") as scandir: 1727 scandir.return_value = sorted(os.scandir(base)) 1728 self.assertEqual(len(set(base.glob("*"))), 4) 1729 1730 subdir.chmod(000) 1731 1732 with mock.patch("os.scandir") as scandir: 1733 scandir.return_value = sorted(os.scandir(base)) 1734 self.assertEqual(len(set(base.glob("*"))), 4) 1735 1736 def _check_resolve(self, p, expected, strict=True): 1737 q = p.resolve(strict) 1738 self.assertEqual(q, expected) 1739 1740 # This can be used to check both relative and absolute resolutions. 1741 _check_resolve_relative = _check_resolve_absolute = _check_resolve 1742 1743 @os_helper.skip_unless_symlink 1744 def test_resolve_common(self): 1745 P = self.cls 1746 p = P(BASE, 'foo') 1747 with self.assertRaises(OSError) as cm: 1748 p.resolve(strict=True) 1749 self.assertEqual(cm.exception.errno, errno.ENOENT) 1750 # Non-strict 1751 self.assertEqualNormCase(str(p.resolve(strict=False)), 1752 os.path.join(BASE, 'foo')) 1753 p = P(BASE, 'foo', 'in', 'spam') 1754 self.assertEqualNormCase(str(p.resolve(strict=False)), 1755 os.path.join(BASE, 'foo', 'in', 'spam')) 1756 p = P(BASE, '..', 'foo', 'in', 'spam') 1757 self.assertEqualNormCase(str(p.resolve(strict=False)), 1758 os.path.abspath(os.path.join('foo', 'in', 'spam'))) 1759 # These are all relative symlinks. 1760 p = P(BASE, 'dirB', 'fileB') 1761 self._check_resolve_relative(p, p) 1762 p = P(BASE, 'linkA') 1763 self._check_resolve_relative(p, P(BASE, 'fileA')) 1764 p = P(BASE, 'dirA', 'linkC', 'fileB') 1765 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB')) 1766 p = P(BASE, 'dirB', 'linkD', 'fileB') 1767 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB')) 1768 # Non-strict 1769 p = P(BASE, 'dirA', 'linkC', 'fileB', 'foo', 'in', 'spam') 1770 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB', 'foo', 'in', 1771 'spam'), False) 1772 p = P(BASE, 'dirA', 'linkC', '..', 'foo', 'in', 'spam') 1773 if os.name == 'nt': 1774 # In Windows, if linkY points to dirB, 'dirA\linkY\..' 1775 # resolves to 'dirA' without resolving linkY first. 1776 self._check_resolve_relative(p, P(BASE, 'dirA', 'foo', 'in', 1777 'spam'), False) 1778 else: 1779 # In Posix, if linkY points to dirB, 'dirA/linkY/..' 1780 # resolves to 'dirB/..' first before resolving to parent of dirB. 1781 self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False) 1782 # Now create absolute symlinks. 1783 d = os_helper._longpath(tempfile.mkdtemp(suffix='-dirD', 1784 dir=os.getcwd())) 1785 self.addCleanup(os_helper.rmtree, d) 1786 os.symlink(os.path.join(d), join('dirA', 'linkX')) 1787 os.symlink(join('dirB'), os.path.join(d, 'linkY')) 1788 p = P(BASE, 'dirA', 'linkX', 'linkY', 'fileB') 1789 self._check_resolve_absolute(p, P(BASE, 'dirB', 'fileB')) 1790 # Non-strict 1791 p = P(BASE, 'dirA', 'linkX', 'linkY', 'foo', 'in', 'spam') 1792 self._check_resolve_relative(p, P(BASE, 'dirB', 'foo', 'in', 'spam'), 1793 False) 1794 p = P(BASE, 'dirA', 'linkX', 'linkY', '..', 'foo', 'in', 'spam') 1795 if os.name == 'nt': 1796 # In Windows, if linkY points to dirB, 'dirA\linkY\..' 1797 # resolves to 'dirA' without resolving linkY first. 1798 self._check_resolve_relative(p, P(d, 'foo', 'in', 'spam'), False) 1799 else: 1800 # In Posix, if linkY points to dirB, 'dirA/linkY/..' 1801 # resolves to 'dirB/..' first before resolving to parent of dirB. 1802 self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False) 1803 1804 @os_helper.skip_unless_symlink 1805 def test_resolve_dot(self): 1806 # See https://bitbucket.org/pitrou/pathlib/issue/9/pathresolve-fails-on-complex-symlinks 1807 p = self.cls(BASE) 1808 self.dirlink('.', join('0')) 1809 self.dirlink(os.path.join('0', '0'), join('1')) 1810 self.dirlink(os.path.join('1', '1'), join('2')) 1811 q = p / '2' 1812 self.assertEqual(q.resolve(strict=True), p) 1813 r = q / '3' / '4' 1814 self.assertRaises(FileNotFoundError, r.resolve, strict=True) 1815 # Non-strict 1816 self.assertEqual(r.resolve(strict=False), p / '3' / '4') 1817 1818 def test_resolve_nonexist_relative_issue38671(self): 1819 p = self.cls('non', 'exist') 1820 1821 old_cwd = os.getcwd() 1822 os.chdir(BASE) 1823 try: 1824 self.assertEqual(p.resolve(), self.cls(BASE, p)) 1825 finally: 1826 os.chdir(old_cwd) 1827 1828 def test_with(self): 1829 p = self.cls(BASE) 1830 it = p.iterdir() 1831 it2 = p.iterdir() 1832 next(it2) 1833 with p: 1834 pass 1835 # Using a path as a context manager is a no-op, thus the following 1836 # operations should still succeed after the context manage exits. 1837 next(it) 1838 next(it2) 1839 p.exists() 1840 p.resolve() 1841 p.absolute() 1842 with p: 1843 pass 1844 1845 def test_chmod(self): 1846 p = self.cls(BASE) / 'fileA' 1847 mode = p.stat().st_mode 1848 # Clear writable bit. 1849 new_mode = mode & ~0o222 1850 p.chmod(new_mode) 1851 self.assertEqual(p.stat().st_mode, new_mode) 1852 # Set writable bit. 1853 new_mode = mode | 0o222 1854 p.chmod(new_mode) 1855 self.assertEqual(p.stat().st_mode, new_mode) 1856 1857 # On Windows, os.chmod does not follow symlinks (issue #15411) 1858 @only_posix 1859 def test_chmod_follow_symlinks_true(self): 1860 p = self.cls(BASE) / 'linkA' 1861 q = p.resolve() 1862 mode = q.stat().st_mode 1863 # Clear writable bit. 1864 new_mode = mode & ~0o222 1865 p.chmod(new_mode, follow_symlinks=True) 1866 self.assertEqual(q.stat().st_mode, new_mode) 1867 # Set writable bit 1868 new_mode = mode | 0o222 1869 p.chmod(new_mode, follow_symlinks=True) 1870 self.assertEqual(q.stat().st_mode, new_mode) 1871 1872 # XXX also need a test for lchmod. 1873 1874 def test_stat(self): 1875 p = self.cls(BASE) / 'fileA' 1876 st = p.stat() 1877 self.assertEqual(p.stat(), st) 1878 # Change file mode by flipping write bit. 1879 p.chmod(st.st_mode ^ 0o222) 1880 self.addCleanup(p.chmod, st.st_mode) 1881 self.assertNotEqual(p.stat(), st) 1882 1883 @os_helper.skip_unless_symlink 1884 def test_stat_no_follow_symlinks(self): 1885 p = self.cls(BASE) / 'linkA' 1886 st = p.stat() 1887 self.assertNotEqual(st, p.stat(follow_symlinks=False)) 1888 1889 def test_stat_no_follow_symlinks_nosymlink(self): 1890 p = self.cls(BASE) / 'fileA' 1891 st = p.stat() 1892 self.assertEqual(st, p.stat(follow_symlinks=False)) 1893 1894 @os_helper.skip_unless_symlink 1895 def test_lstat(self): 1896 p = self.cls(BASE)/ 'linkA' 1897 st = p.stat() 1898 self.assertNotEqual(st, p.lstat()) 1899 1900 def test_lstat_nosymlink(self): 1901 p = self.cls(BASE) / 'fileA' 1902 st = p.stat() 1903 self.assertEqual(st, p.lstat()) 1904 1905 @unittest.skipUnless(pwd, "the pwd module is needed for this test") 1906 def test_owner(self): 1907 p = self.cls(BASE) / 'fileA' 1908 uid = p.stat().st_uid 1909 try: 1910 name = pwd.getpwuid(uid).pw_name 1911 except KeyError: 1912 self.skipTest( 1913 "user %d doesn't have an entry in the system database" % uid) 1914 self.assertEqual(name, p.owner()) 1915 1916 @unittest.skipUnless(grp, "the grp module is needed for this test") 1917 def test_group(self): 1918 p = self.cls(BASE) / 'fileA' 1919 gid = p.stat().st_gid 1920 try: 1921 name = grp.getgrgid(gid).gr_name 1922 except KeyError: 1923 self.skipTest( 1924 "group %d doesn't have an entry in the system database" % gid) 1925 self.assertEqual(name, p.group()) 1926 1927 def test_unlink(self): 1928 p = self.cls(BASE) / 'fileA' 1929 p.unlink() 1930 self.assertFileNotFound(p.stat) 1931 self.assertFileNotFound(p.unlink) 1932 1933 def test_unlink_missing_ok(self): 1934 p = self.cls(BASE) / 'fileAAA' 1935 self.assertFileNotFound(p.unlink) 1936 p.unlink(missing_ok=True) 1937 1938 def test_rmdir(self): 1939 p = self.cls(BASE) / 'dirA' 1940 for q in p.iterdir(): 1941 q.unlink() 1942 p.rmdir() 1943 self.assertFileNotFound(p.stat) 1944 self.assertFileNotFound(p.unlink) 1945 1946 @unittest.skipUnless(hasattr(os, "link"), "os.link() is not present") 1947 def test_link_to(self): 1948 P = self.cls(BASE) 1949 p = P / 'fileA' 1950 size = p.stat().st_size 1951 # linking to another path. 1952 q = P / 'dirA' / 'fileAA' 1953 try: 1954 with self.assertWarns(DeprecationWarning): 1955 p.link_to(q) 1956 except PermissionError as e: 1957 self.skipTest('os.link(): %s' % e) 1958 self.assertEqual(q.stat().st_size, size) 1959 self.assertEqual(os.path.samefile(p, q), True) 1960 self.assertTrue(p.stat) 1961 # Linking to a str of a relative path. 1962 r = rel_join('fileAAA') 1963 with self.assertWarns(DeprecationWarning): 1964 q.link_to(r) 1965 self.assertEqual(os.stat(r).st_size, size) 1966 self.assertTrue(q.stat) 1967 1968 @unittest.skipUnless(hasattr(os, "link"), "os.link() is not present") 1969 def test_hardlink_to(self): 1970 P = self.cls(BASE) 1971 target = P / 'fileA' 1972 size = target.stat().st_size 1973 # linking to another path. 1974 link = P / 'dirA' / 'fileAA' 1975 link.hardlink_to(target) 1976 self.assertEqual(link.stat().st_size, size) 1977 self.assertTrue(os.path.samefile(target, link)) 1978 self.assertTrue(target.exists()) 1979 # Linking to a str of a relative path. 1980 link2 = P / 'dirA' / 'fileAAA' 1981 target2 = rel_join('fileA') 1982 link2.hardlink_to(target2) 1983 self.assertEqual(os.stat(target2).st_size, size) 1984 self.assertTrue(link2.exists()) 1985 1986 @unittest.skipIf(hasattr(os, "link"), "os.link() is present") 1987 def test_link_to_not_implemented(self): 1988 P = self.cls(BASE) 1989 p = P / 'fileA' 1990 # linking to another path. 1991 q = P / 'dirA' / 'fileAA' 1992 with self.assertRaises(NotImplementedError): 1993 p.link_to(q) 1994 1995 def test_rename(self): 1996 P = self.cls(BASE) 1997 p = P / 'fileA' 1998 size = p.stat().st_size 1999 # Renaming to another path. 2000 q = P / 'dirA' / 'fileAA' 2001 renamed_p = p.rename(q) 2002 self.assertEqual(renamed_p, q) 2003 self.assertEqual(q.stat().st_size, size) 2004 self.assertFileNotFound(p.stat) 2005 # Renaming to a str of a relative path. 2006 r = rel_join('fileAAA') 2007 renamed_q = q.rename(r) 2008 self.assertEqual(renamed_q, self.cls(r)) 2009 self.assertEqual(os.stat(r).st_size, size) 2010 self.assertFileNotFound(q.stat) 2011 2012 def test_replace(self): 2013 P = self.cls(BASE) 2014 p = P / 'fileA' 2015 size = p.stat().st_size 2016 # Replacing a non-existing path. 2017 q = P / 'dirA' / 'fileAA' 2018 replaced_p = p.replace(q) 2019 self.assertEqual(replaced_p, q) 2020 self.assertEqual(q.stat().st_size, size) 2021 self.assertFileNotFound(p.stat) 2022 # Replacing another (existing) path. 2023 r = rel_join('dirB', 'fileB') 2024 replaced_q = q.replace(r) 2025 self.assertEqual(replaced_q, self.cls(r)) 2026 self.assertEqual(os.stat(r).st_size, size) 2027 self.assertFileNotFound(q.stat) 2028 2029 @os_helper.skip_unless_symlink 2030 def test_readlink(self): 2031 P = self.cls(BASE) 2032 self.assertEqual((P / 'linkA').readlink(), self.cls('fileA')) 2033 self.assertEqual((P / 'brokenLink').readlink(), 2034 self.cls('non-existing')) 2035 self.assertEqual((P / 'linkB').readlink(), self.cls('dirB')) 2036 with self.assertRaises(OSError): 2037 (P / 'fileA').readlink() 2038 2039 def test_touch_common(self): 2040 P = self.cls(BASE) 2041 p = P / 'newfileA' 2042 self.assertFalse(p.exists()) 2043 p.touch() 2044 self.assertTrue(p.exists()) 2045 st = p.stat() 2046 old_mtime = st.st_mtime 2047 old_mtime_ns = st.st_mtime_ns 2048 # Rewind the mtime sufficiently far in the past to work around 2049 # filesystem-specific timestamp granularity. 2050 os.utime(str(p), (old_mtime - 10, old_mtime - 10)) 2051 # The file mtime should be refreshed by calling touch() again. 2052 p.touch() 2053 st = p.stat() 2054 self.assertGreaterEqual(st.st_mtime_ns, old_mtime_ns) 2055 self.assertGreaterEqual(st.st_mtime, old_mtime) 2056 # Now with exist_ok=False. 2057 p = P / 'newfileB' 2058 self.assertFalse(p.exists()) 2059 p.touch(mode=0o700, exist_ok=False) 2060 self.assertTrue(p.exists()) 2061 self.assertRaises(OSError, p.touch, exist_ok=False) 2062 2063 def test_touch_nochange(self): 2064 P = self.cls(BASE) 2065 p = P / 'fileA' 2066 p.touch() 2067 with p.open('rb') as f: 2068 self.assertEqual(f.read().strip(), b"this is file A") 2069 2070 def test_mkdir(self): 2071 P = self.cls(BASE) 2072 p = P / 'newdirA' 2073 self.assertFalse(p.exists()) 2074 p.mkdir() 2075 self.assertTrue(p.exists()) 2076 self.assertTrue(p.is_dir()) 2077 with self.assertRaises(OSError) as cm: 2078 p.mkdir() 2079 self.assertEqual(cm.exception.errno, errno.EEXIST) 2080 2081 def test_mkdir_parents(self): 2082 # Creating a chain of directories. 2083 p = self.cls(BASE, 'newdirB', 'newdirC') 2084 self.assertFalse(p.exists()) 2085 with self.assertRaises(OSError) as cm: 2086 p.mkdir() 2087 self.assertEqual(cm.exception.errno, errno.ENOENT) 2088 p.mkdir(parents=True) 2089 self.assertTrue(p.exists()) 2090 self.assertTrue(p.is_dir()) 2091 with self.assertRaises(OSError) as cm: 2092 p.mkdir(parents=True) 2093 self.assertEqual(cm.exception.errno, errno.EEXIST) 2094 # Test `mode` arg. 2095 mode = stat.S_IMODE(p.stat().st_mode) # Default mode. 2096 p = self.cls(BASE, 'newdirD', 'newdirE') 2097 p.mkdir(0o555, parents=True) 2098 self.assertTrue(p.exists()) 2099 self.assertTrue(p.is_dir()) 2100 if os.name != 'nt': 2101 # The directory's permissions follow the mode argument. 2102 self.assertEqual(stat.S_IMODE(p.stat().st_mode), 0o7555 & mode) 2103 # The parent's permissions follow the default process settings. 2104 self.assertEqual(stat.S_IMODE(p.parent.stat().st_mode), mode) 2105 2106 def test_mkdir_exist_ok(self): 2107 p = self.cls(BASE, 'dirB') 2108 st_ctime_first = p.stat().st_ctime 2109 self.assertTrue(p.exists()) 2110 self.assertTrue(p.is_dir()) 2111 with self.assertRaises(FileExistsError) as cm: 2112 p.mkdir() 2113 self.assertEqual(cm.exception.errno, errno.EEXIST) 2114 p.mkdir(exist_ok=True) 2115 self.assertTrue(p.exists()) 2116 self.assertEqual(p.stat().st_ctime, st_ctime_first) 2117 2118 def test_mkdir_exist_ok_with_parent(self): 2119 p = self.cls(BASE, 'dirC') 2120 self.assertTrue(p.exists()) 2121 with self.assertRaises(FileExistsError) as cm: 2122 p.mkdir() 2123 self.assertEqual(cm.exception.errno, errno.EEXIST) 2124 p = p / 'newdirC' 2125 p.mkdir(parents=True) 2126 st_ctime_first = p.stat().st_ctime 2127 self.assertTrue(p.exists()) 2128 with self.assertRaises(FileExistsError) as cm: 2129 p.mkdir(parents=True) 2130 self.assertEqual(cm.exception.errno, errno.EEXIST) 2131 p.mkdir(parents=True, exist_ok=True) 2132 self.assertTrue(p.exists()) 2133 self.assertEqual(p.stat().st_ctime, st_ctime_first) 2134 2135 def test_mkdir_exist_ok_root(self): 2136 # Issue #25803: A drive root could raise PermissionError on Windows. 2137 self.cls('/').resolve().mkdir(exist_ok=True) 2138 self.cls('/').resolve().mkdir(parents=True, exist_ok=True) 2139 2140 @only_nt # XXX: not sure how to test this on POSIX. 2141 def test_mkdir_with_unknown_drive(self): 2142 for d in 'ZYXWVUTSRQPONMLKJIHGFEDCBA': 2143 p = self.cls(d + ':\\') 2144 if not p.is_dir(): 2145 break 2146 else: 2147 self.skipTest("cannot find a drive that doesn't exist") 2148 with self.assertRaises(OSError): 2149 (p / 'child' / 'path').mkdir(parents=True) 2150 2151 def test_mkdir_with_child_file(self): 2152 p = self.cls(BASE, 'dirB', 'fileB') 2153 self.assertTrue(p.exists()) 2154 # An exception is raised when the last path component is an existing 2155 # regular file, regardless of whether exist_ok is true or not. 2156 with self.assertRaises(FileExistsError) as cm: 2157 p.mkdir(parents=True) 2158 self.assertEqual(cm.exception.errno, errno.EEXIST) 2159 with self.assertRaises(FileExistsError) as cm: 2160 p.mkdir(parents=True, exist_ok=True) 2161 self.assertEqual(cm.exception.errno, errno.EEXIST) 2162 2163 def test_mkdir_no_parents_file(self): 2164 p = self.cls(BASE, 'fileA') 2165 self.assertTrue(p.exists()) 2166 # An exception is raised when the last path component is an existing 2167 # regular file, regardless of whether exist_ok is true or not. 2168 with self.assertRaises(FileExistsError) as cm: 2169 p.mkdir() 2170 self.assertEqual(cm.exception.errno, errno.EEXIST) 2171 with self.assertRaises(FileExistsError) as cm: 2172 p.mkdir(exist_ok=True) 2173 self.assertEqual(cm.exception.errno, errno.EEXIST) 2174 2175 def test_mkdir_concurrent_parent_creation(self): 2176 for pattern_num in range(32): 2177 p = self.cls(BASE, 'dirCPC%d' % pattern_num) 2178 self.assertFalse(p.exists()) 2179 2180 def my_mkdir(path, mode=0o777): 2181 path = str(path) 2182 # Emulate another process that would create the directory 2183 # just before we try to create it ourselves. We do it 2184 # in all possible pattern combinations, assuming that this 2185 # function is called at most 5 times (dirCPC/dir1/dir2, 2186 # dirCPC/dir1, dirCPC, dirCPC/dir1, dirCPC/dir1/dir2). 2187 if pattern.pop(): 2188 os.mkdir(path, mode) # From another process. 2189 concurrently_created.add(path) 2190 os.mkdir(path, mode) # Our real call. 2191 2192 pattern = [bool(pattern_num & (1 << n)) for n in range(5)] 2193 concurrently_created = set() 2194 p12 = p / 'dir1' / 'dir2' 2195 try: 2196 with mock.patch("pathlib._normal_accessor.mkdir", my_mkdir): 2197 p12.mkdir(parents=True, exist_ok=False) 2198 except FileExistsError: 2199 self.assertIn(str(p12), concurrently_created) 2200 else: 2201 self.assertNotIn(str(p12), concurrently_created) 2202 self.assertTrue(p.exists()) 2203 2204 @os_helper.skip_unless_symlink 2205 def test_symlink_to(self): 2206 P = self.cls(BASE) 2207 target = P / 'fileA' 2208 # Symlinking a path target. 2209 link = P / 'dirA' / 'linkAA' 2210 link.symlink_to(target) 2211 self.assertEqual(link.stat(), target.stat()) 2212 self.assertNotEqual(link.lstat(), target.stat()) 2213 # Symlinking a str target. 2214 link = P / 'dirA' / 'linkAAA' 2215 link.symlink_to(str(target)) 2216 self.assertEqual(link.stat(), target.stat()) 2217 self.assertNotEqual(link.lstat(), target.stat()) 2218 self.assertFalse(link.is_dir()) 2219 # Symlinking to a directory. 2220 target = P / 'dirB' 2221 link = P / 'dirA' / 'linkAAAA' 2222 link.symlink_to(target, target_is_directory=True) 2223 self.assertEqual(link.stat(), target.stat()) 2224 self.assertNotEqual(link.lstat(), target.stat()) 2225 self.assertTrue(link.is_dir()) 2226 self.assertTrue(list(link.iterdir())) 2227 2228 def test_is_dir(self): 2229 P = self.cls(BASE) 2230 self.assertTrue((P / 'dirA').is_dir()) 2231 self.assertFalse((P / 'fileA').is_dir()) 2232 self.assertFalse((P / 'non-existing').is_dir()) 2233 self.assertFalse((P / 'fileA' / 'bah').is_dir()) 2234 if os_helper.can_symlink(): 2235 self.assertFalse((P / 'linkA').is_dir()) 2236 self.assertTrue((P / 'linkB').is_dir()) 2237 self.assertFalse((P/ 'brokenLink').is_dir(), False) 2238 self.assertIs((P / 'dirA\udfff').is_dir(), False) 2239 self.assertIs((P / 'dirA\x00').is_dir(), False) 2240 2241 def test_is_file(self): 2242 P = self.cls(BASE) 2243 self.assertTrue((P / 'fileA').is_file()) 2244 self.assertFalse((P / 'dirA').is_file()) 2245 self.assertFalse((P / 'non-existing').is_file()) 2246 self.assertFalse((P / 'fileA' / 'bah').is_file()) 2247 if os_helper.can_symlink(): 2248 self.assertTrue((P / 'linkA').is_file()) 2249 self.assertFalse((P / 'linkB').is_file()) 2250 self.assertFalse((P/ 'brokenLink').is_file()) 2251 self.assertIs((P / 'fileA\udfff').is_file(), False) 2252 self.assertIs((P / 'fileA\x00').is_file(), False) 2253 2254 @only_posix 2255 def test_is_mount(self): 2256 P = self.cls(BASE) 2257 R = self.cls('/') # TODO: Work out Windows. 2258 self.assertFalse((P / 'fileA').is_mount()) 2259 self.assertFalse((P / 'dirA').is_mount()) 2260 self.assertFalse((P / 'non-existing').is_mount()) 2261 self.assertFalse((P / 'fileA' / 'bah').is_mount()) 2262 self.assertTrue(R.is_mount()) 2263 if os_helper.can_symlink(): 2264 self.assertFalse((P / 'linkA').is_mount()) 2265 self.assertIs(self.cls('/\udfff').is_mount(), False) 2266 self.assertIs(self.cls('/\x00').is_mount(), False) 2267 2268 def test_is_symlink(self): 2269 P = self.cls(BASE) 2270 self.assertFalse((P / 'fileA').is_symlink()) 2271 self.assertFalse((P / 'dirA').is_symlink()) 2272 self.assertFalse((P / 'non-existing').is_symlink()) 2273 self.assertFalse((P / 'fileA' / 'bah').is_symlink()) 2274 if os_helper.can_symlink(): 2275 self.assertTrue((P / 'linkA').is_symlink()) 2276 self.assertTrue((P / 'linkB').is_symlink()) 2277 self.assertTrue((P/ 'brokenLink').is_symlink()) 2278 self.assertIs((P / 'fileA\udfff').is_file(), False) 2279 self.assertIs((P / 'fileA\x00').is_file(), False) 2280 if os_helper.can_symlink(): 2281 self.assertIs((P / 'linkA\udfff').is_file(), False) 2282 self.assertIs((P / 'linkA\x00').is_file(), False) 2283 2284 def test_is_fifo_false(self): 2285 P = self.cls(BASE) 2286 self.assertFalse((P / 'fileA').is_fifo()) 2287 self.assertFalse((P / 'dirA').is_fifo()) 2288 self.assertFalse((P / 'non-existing').is_fifo()) 2289 self.assertFalse((P / 'fileA' / 'bah').is_fifo()) 2290 self.assertIs((P / 'fileA\udfff').is_fifo(), False) 2291 self.assertIs((P / 'fileA\x00').is_fifo(), False) 2292 2293 @unittest.skipUnless(hasattr(os, "mkfifo"), "os.mkfifo() required") 2294 @unittest.skipIf(sys.platform == "vxworks", 2295 "fifo requires special path on VxWorks") 2296 def test_is_fifo_true(self): 2297 P = self.cls(BASE, 'myfifo') 2298 try: 2299 os.mkfifo(str(P)) 2300 except PermissionError as e: 2301 self.skipTest('os.mkfifo(): %s' % e) 2302 self.assertTrue(P.is_fifo()) 2303 self.assertFalse(P.is_socket()) 2304 self.assertFalse(P.is_file()) 2305 self.assertIs(self.cls(BASE, 'myfifo\udfff').is_fifo(), False) 2306 self.assertIs(self.cls(BASE, 'myfifo\x00').is_fifo(), False) 2307 2308 def test_is_socket_false(self): 2309 P = self.cls(BASE) 2310 self.assertFalse((P / 'fileA').is_socket()) 2311 self.assertFalse((P / 'dirA').is_socket()) 2312 self.assertFalse((P / 'non-existing').is_socket()) 2313 self.assertFalse((P / 'fileA' / 'bah').is_socket()) 2314 self.assertIs((P / 'fileA\udfff').is_socket(), False) 2315 self.assertIs((P / 'fileA\x00').is_socket(), False) 2316 2317 @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") 2318 def test_is_socket_true(self): 2319 P = self.cls(BASE, 'mysock') 2320 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 2321 self.addCleanup(sock.close) 2322 try: 2323 sock.bind(str(P)) 2324 except OSError as e: 2325 if (isinstance(e, PermissionError) or 2326 "AF_UNIX path too long" in str(e)): 2327 self.skipTest("cannot bind Unix socket: " + str(e)) 2328 self.assertTrue(P.is_socket()) 2329 self.assertFalse(P.is_fifo()) 2330 self.assertFalse(P.is_file()) 2331 self.assertIs(self.cls(BASE, 'mysock\udfff').is_socket(), False) 2332 self.assertIs(self.cls(BASE, 'mysock\x00').is_socket(), False) 2333 2334 def test_is_block_device_false(self): 2335 P = self.cls(BASE) 2336 self.assertFalse((P / 'fileA').is_block_device()) 2337 self.assertFalse((P / 'dirA').is_block_device()) 2338 self.assertFalse((P / 'non-existing').is_block_device()) 2339 self.assertFalse((P / 'fileA' / 'bah').is_block_device()) 2340 self.assertIs((P / 'fileA\udfff').is_block_device(), False) 2341 self.assertIs((P / 'fileA\x00').is_block_device(), False) 2342 2343 def test_is_char_device_false(self): 2344 P = self.cls(BASE) 2345 self.assertFalse((P / 'fileA').is_char_device()) 2346 self.assertFalse((P / 'dirA').is_char_device()) 2347 self.assertFalse((P / 'non-existing').is_char_device()) 2348 self.assertFalse((P / 'fileA' / 'bah').is_char_device()) 2349 self.assertIs((P / 'fileA\udfff').is_char_device(), False) 2350 self.assertIs((P / 'fileA\x00').is_char_device(), False) 2351 2352 def test_is_char_device_true(self): 2353 # Under Unix, /dev/null should generally be a char device. 2354 P = self.cls('/dev/null') 2355 if not P.exists(): 2356 self.skipTest("/dev/null required") 2357 self.assertTrue(P.is_char_device()) 2358 self.assertFalse(P.is_block_device()) 2359 self.assertFalse(P.is_file()) 2360 self.assertIs(self.cls('/dev/null\udfff').is_char_device(), False) 2361 self.assertIs(self.cls('/dev/null\x00').is_char_device(), False) 2362 2363 def test_pickling_common(self): 2364 p = self.cls(BASE, 'fileA') 2365 for proto in range(0, pickle.HIGHEST_PROTOCOL + 1): 2366 dumped = pickle.dumps(p, proto) 2367 pp = pickle.loads(dumped) 2368 self.assertEqual(pp.stat(), p.stat()) 2369 2370 def test_parts_interning(self): 2371 P = self.cls 2372 p = P('/usr/bin/foo') 2373 q = P('/usr/local/bin') 2374 # 'usr' 2375 self.assertIs(p.parts[1], q.parts[1]) 2376 # 'bin' 2377 self.assertIs(p.parts[2], q.parts[3]) 2378 2379 def _check_complex_symlinks(self, link0_target): 2380 # Test solving a non-looping chain of symlinks (issue #19887). 2381 P = self.cls(BASE) 2382 self.dirlink(os.path.join('link0', 'link0'), join('link1')) 2383 self.dirlink(os.path.join('link1', 'link1'), join('link2')) 2384 self.dirlink(os.path.join('link2', 'link2'), join('link3')) 2385 self.dirlink(link0_target, join('link0')) 2386 2387 # Resolve absolute paths. 2388 p = (P / 'link0').resolve() 2389 self.assertEqual(p, P) 2390 self.assertEqualNormCase(str(p), BASE) 2391 p = (P / 'link1').resolve() 2392 self.assertEqual(p, P) 2393 self.assertEqualNormCase(str(p), BASE) 2394 p = (P / 'link2').resolve() 2395 self.assertEqual(p, P) 2396 self.assertEqualNormCase(str(p), BASE) 2397 p = (P / 'link3').resolve() 2398 self.assertEqual(p, P) 2399 self.assertEqualNormCase(str(p), BASE) 2400 2401 # Resolve relative paths. 2402 old_path = os.getcwd() 2403 os.chdir(BASE) 2404 try: 2405 p = self.cls('link0').resolve() 2406 self.assertEqual(p, P) 2407 self.assertEqualNormCase(str(p), BASE) 2408 p = self.cls('link1').resolve() 2409 self.assertEqual(p, P) 2410 self.assertEqualNormCase(str(p), BASE) 2411 p = self.cls('link2').resolve() 2412 self.assertEqual(p, P) 2413 self.assertEqualNormCase(str(p), BASE) 2414 p = self.cls('link3').resolve() 2415 self.assertEqual(p, P) 2416 self.assertEqualNormCase(str(p), BASE) 2417 finally: 2418 os.chdir(old_path) 2419 2420 @os_helper.skip_unless_symlink 2421 def test_complex_symlinks_absolute(self): 2422 self._check_complex_symlinks(BASE) 2423 2424 @os_helper.skip_unless_symlink 2425 def test_complex_symlinks_relative(self): 2426 self._check_complex_symlinks('.') 2427 2428 @os_helper.skip_unless_symlink 2429 def test_complex_symlinks_relative_dot_dot(self): 2430 self._check_complex_symlinks(os.path.join('dirA', '..')) 2431 2432 2433class PathTest(_BasePathTest, unittest.TestCase): 2434 cls = pathlib.Path 2435 2436 def test_class_getitem(self): 2437 self.assertIs(self.cls[str], self.cls) 2438 2439 def test_concrete_class(self): 2440 p = self.cls('a') 2441 self.assertIs(type(p), 2442 pathlib.WindowsPath if os.name == 'nt' else pathlib.PosixPath) 2443 2444 def test_unsupported_flavour(self): 2445 if os.name == 'nt': 2446 self.assertRaises(NotImplementedError, pathlib.PosixPath) 2447 else: 2448 self.assertRaises(NotImplementedError, pathlib.WindowsPath) 2449 2450 def test_glob_empty_pattern(self): 2451 p = self.cls() 2452 with self.assertRaisesRegex(ValueError, 'Unacceptable pattern'): 2453 list(p.glob('')) 2454 2455 2456@only_posix 2457class PosixPathTest(_BasePathTest, unittest.TestCase): 2458 cls = pathlib.PosixPath 2459 2460 def _check_symlink_loop(self, *args, strict=True): 2461 path = self.cls(*args) 2462 with self.assertRaises(RuntimeError): 2463 print(path.resolve(strict)) 2464 2465 def test_open_mode(self): 2466 old_mask = os.umask(0) 2467 self.addCleanup(os.umask, old_mask) 2468 p = self.cls(BASE) 2469 with (p / 'new_file').open('wb'): 2470 pass 2471 st = os.stat(join('new_file')) 2472 self.assertEqual(stat.S_IMODE(st.st_mode), 0o666) 2473 os.umask(0o022) 2474 with (p / 'other_new_file').open('wb'): 2475 pass 2476 st = os.stat(join('other_new_file')) 2477 self.assertEqual(stat.S_IMODE(st.st_mode), 0o644) 2478 2479 def test_resolve_root(self): 2480 current_directory = os.getcwd() 2481 try: 2482 os.chdir('/') 2483 p = self.cls('spam') 2484 self.assertEqual(str(p.resolve()), '/spam') 2485 finally: 2486 os.chdir(current_directory) 2487 2488 def test_touch_mode(self): 2489 old_mask = os.umask(0) 2490 self.addCleanup(os.umask, old_mask) 2491 p = self.cls(BASE) 2492 (p / 'new_file').touch() 2493 st = os.stat(join('new_file')) 2494 self.assertEqual(stat.S_IMODE(st.st_mode), 0o666) 2495 os.umask(0o022) 2496 (p / 'other_new_file').touch() 2497 st = os.stat(join('other_new_file')) 2498 self.assertEqual(stat.S_IMODE(st.st_mode), 0o644) 2499 (p / 'masked_new_file').touch(mode=0o750) 2500 st = os.stat(join('masked_new_file')) 2501 self.assertEqual(stat.S_IMODE(st.st_mode), 0o750) 2502 2503 @os_helper.skip_unless_symlink 2504 def test_resolve_loop(self): 2505 # Loops with relative symlinks. 2506 os.symlink('linkX/inside', join('linkX')) 2507 self._check_symlink_loop(BASE, 'linkX') 2508 os.symlink('linkY', join('linkY')) 2509 self._check_symlink_loop(BASE, 'linkY') 2510 os.symlink('linkZ/../linkZ', join('linkZ')) 2511 self._check_symlink_loop(BASE, 'linkZ') 2512 # Non-strict 2513 self._check_symlink_loop(BASE, 'linkZ', 'foo', strict=False) 2514 # Loops with absolute symlinks. 2515 os.symlink(join('linkU/inside'), join('linkU')) 2516 self._check_symlink_loop(BASE, 'linkU') 2517 os.symlink(join('linkV'), join('linkV')) 2518 self._check_symlink_loop(BASE, 'linkV') 2519 os.symlink(join('linkW/../linkW'), join('linkW')) 2520 self._check_symlink_loop(BASE, 'linkW') 2521 # Non-strict 2522 self._check_symlink_loop(BASE, 'linkW', 'foo', strict=False) 2523 2524 def test_glob(self): 2525 P = self.cls 2526 p = P(BASE) 2527 given = set(p.glob("FILEa")) 2528 expect = set() if not os_helper.fs_is_case_insensitive(BASE) else given 2529 self.assertEqual(given, expect) 2530 self.assertEqual(set(p.glob("FILEa*")), set()) 2531 2532 def test_rglob(self): 2533 P = self.cls 2534 p = P(BASE, "dirC") 2535 given = set(p.rglob("FILEd")) 2536 expect = set() if not os_helper.fs_is_case_insensitive(BASE) else given 2537 self.assertEqual(given, expect) 2538 self.assertEqual(set(p.rglob("FILEd*")), set()) 2539 2540 @unittest.skipUnless(hasattr(pwd, 'getpwall'), 2541 'pwd module does not expose getpwall()') 2542 @unittest.skipIf(sys.platform == "vxworks", 2543 "no home directory on VxWorks") 2544 def test_expanduser(self): 2545 P = self.cls 2546 import_helper.import_module('pwd') 2547 import pwd 2548 pwdent = pwd.getpwuid(os.getuid()) 2549 username = pwdent.pw_name 2550 userhome = pwdent.pw_dir.rstrip('/') or '/' 2551 # Find arbitrary different user (if exists). 2552 for pwdent in pwd.getpwall(): 2553 othername = pwdent.pw_name 2554 otherhome = pwdent.pw_dir.rstrip('/') 2555 if othername != username and otherhome: 2556 break 2557 else: 2558 othername = username 2559 otherhome = userhome 2560 2561 fakename = 'fakeuser' 2562 # This user can theoretically exist on a test runner. Create unique name: 2563 try: 2564 while pwd.getpwnam(fakename): 2565 fakename += '1' 2566 except KeyError: 2567 pass # Non-existent name found 2568 2569 p1 = P('~/Documents') 2570 p2 = P(f'~{username}/Documents') 2571 p3 = P(f'~{othername}/Documents') 2572 p4 = P(f'../~{username}/Documents') 2573 p5 = P(f'/~{username}/Documents') 2574 p6 = P('') 2575 p7 = P(f'~{fakename}/Documents') 2576 2577 with os_helper.EnvironmentVarGuard() as env: 2578 env.pop('HOME', None) 2579 2580 self.assertEqual(p1.expanduser(), P(userhome) / 'Documents') 2581 self.assertEqual(p2.expanduser(), P(userhome) / 'Documents') 2582 self.assertEqual(p3.expanduser(), P(otherhome) / 'Documents') 2583 self.assertEqual(p4.expanduser(), p4) 2584 self.assertEqual(p5.expanduser(), p5) 2585 self.assertEqual(p6.expanduser(), p6) 2586 self.assertRaises(RuntimeError, p7.expanduser) 2587 2588 env['HOME'] = '/tmp' 2589 self.assertEqual(p1.expanduser(), P('/tmp/Documents')) 2590 self.assertEqual(p2.expanduser(), P(userhome) / 'Documents') 2591 self.assertEqual(p3.expanduser(), P(otherhome) / 'Documents') 2592 self.assertEqual(p4.expanduser(), p4) 2593 self.assertEqual(p5.expanduser(), p5) 2594 self.assertEqual(p6.expanduser(), p6) 2595 self.assertRaises(RuntimeError, p7.expanduser) 2596 2597 @unittest.skipIf(sys.platform != "darwin", 2598 "Bad file descriptor in /dev/fd affects only macOS") 2599 def test_handling_bad_descriptor(self): 2600 try: 2601 file_descriptors = list(pathlib.Path('/dev/fd').rglob("*"))[3:] 2602 if not file_descriptors: 2603 self.skipTest("no file descriptors - issue was not reproduced") 2604 # Checking all file descriptors because there is no guarantee 2605 # which one will fail. 2606 for f in file_descriptors: 2607 f.exists() 2608 f.is_dir() 2609 f.is_file() 2610 f.is_symlink() 2611 f.is_block_device() 2612 f.is_char_device() 2613 f.is_fifo() 2614 f.is_socket() 2615 except OSError as e: 2616 if e.errno == errno.EBADF: 2617 self.fail("Bad file descriptor not handled.") 2618 raise 2619 2620 2621@only_nt 2622class WindowsPathTest(_BasePathTest, unittest.TestCase): 2623 cls = pathlib.WindowsPath 2624 2625 def test_glob(self): 2626 P = self.cls 2627 p = P(BASE) 2628 self.assertEqual(set(p.glob("FILEa")), { P(BASE, "fileA") }) 2629 self.assertEqual(set(p.glob("F*a")), { P(BASE, "fileA") }) 2630 self.assertEqual(set(map(str, p.glob("FILEa"))), {f"{p}\\FILEa"}) 2631 self.assertEqual(set(map(str, p.glob("F*a"))), {f"{p}\\fileA"}) 2632 2633 def test_rglob(self): 2634 P = self.cls 2635 p = P(BASE, "dirC") 2636 self.assertEqual(set(p.rglob("FILEd")), { P(BASE, "dirC/dirD/fileD") }) 2637 self.assertEqual(set(map(str, p.rglob("FILEd"))), {f"{p}\\dirD\\FILEd"}) 2638 2639 def test_expanduser(self): 2640 P = self.cls 2641 with os_helper.EnvironmentVarGuard() as env: 2642 env.pop('HOME', None) 2643 env.pop('USERPROFILE', None) 2644 env.pop('HOMEPATH', None) 2645 env.pop('HOMEDRIVE', None) 2646 env['USERNAME'] = 'alice' 2647 2648 # test that the path returns unchanged 2649 p1 = P('~/My Documents') 2650 p2 = P('~alice/My Documents') 2651 p3 = P('~bob/My Documents') 2652 p4 = P('/~/My Documents') 2653 p5 = P('d:~/My Documents') 2654 p6 = P('') 2655 self.assertRaises(RuntimeError, p1.expanduser) 2656 self.assertRaises(RuntimeError, p2.expanduser) 2657 self.assertRaises(RuntimeError, p3.expanduser) 2658 self.assertEqual(p4.expanduser(), p4) 2659 self.assertEqual(p5.expanduser(), p5) 2660 self.assertEqual(p6.expanduser(), p6) 2661 2662 def check(): 2663 env.pop('USERNAME', None) 2664 self.assertEqual(p1.expanduser(), 2665 P('C:/Users/alice/My Documents')) 2666 self.assertRaises(RuntimeError, p2.expanduser) 2667 env['USERNAME'] = 'alice' 2668 self.assertEqual(p2.expanduser(), 2669 P('C:/Users/alice/My Documents')) 2670 self.assertEqual(p3.expanduser(), 2671 P('C:/Users/bob/My Documents')) 2672 self.assertEqual(p4.expanduser(), p4) 2673 self.assertEqual(p5.expanduser(), p5) 2674 self.assertEqual(p6.expanduser(), p6) 2675 2676 env['HOMEPATH'] = 'C:\\Users\\alice' 2677 check() 2678 2679 env['HOMEDRIVE'] = 'C:\\' 2680 env['HOMEPATH'] = 'Users\\alice' 2681 check() 2682 2683 env.pop('HOMEDRIVE', None) 2684 env.pop('HOMEPATH', None) 2685 env['USERPROFILE'] = 'C:\\Users\\alice' 2686 check() 2687 2688 # bpo-38883: ignore `HOME` when set on windows 2689 env['HOME'] = 'C:\\Users\\eve' 2690 check() 2691 2692 2693class CompatiblePathTest(unittest.TestCase): 2694 """ 2695 Test that a type can be made compatible with PurePath 2696 derivatives by implementing division operator overloads. 2697 """ 2698 2699 class CompatPath: 2700 """ 2701 Minimum viable class to test PurePath compatibility. 2702 Simply uses the division operator to join a given 2703 string and the string value of another object with 2704 a forward slash. 2705 """ 2706 def __init__(self, string): 2707 self.string = string 2708 2709 def __truediv__(self, other): 2710 return type(self)(f"{self.string}/{other}") 2711 2712 def __rtruediv__(self, other): 2713 return type(self)(f"{other}/{self.string}") 2714 2715 def test_truediv(self): 2716 result = pathlib.PurePath("test") / self.CompatPath("right") 2717 self.assertIsInstance(result, self.CompatPath) 2718 self.assertEqual(result.string, "test/right") 2719 2720 with self.assertRaises(TypeError): 2721 # Verify improper operations still raise a TypeError 2722 pathlib.PurePath("test") / 10 2723 2724 def test_rtruediv(self): 2725 result = self.CompatPath("left") / pathlib.PurePath("test") 2726 self.assertIsInstance(result, self.CompatPath) 2727 self.assertEqual(result.string, "left/test") 2728 2729 with self.assertRaises(TypeError): 2730 # Verify improper operations still raise a TypeError 2731 10 / pathlib.PurePath("test") 2732 2733 2734if __name__ == "__main__": 2735 unittest.main() 2736