1import os 2import posixpath 3import sys 4import unittest 5from functools import partial 6from posixpath import realpath, abspath, dirname, basename, ALLOW_MISSING 7from test import support 8from test import test_genericpath 9from test.support import import_helper 10from test.support import os_helper 11from test.support.os_helper import FakePath 12from unittest import mock 13 14try: 15 import posix 16except ImportError: 17 posix = None 18 19 20# An absolute path to a temporary filename for testing. We can't rely on TESTFN 21# being an absolute path, so we need this. 22 23ABSTFN = abspath(os_helper.TESTFN) 24 25def skip_if_ABSTFN_contains_backslash(test): 26 """ 27 On Windows, posixpath.abspath still returns paths with backslashes 28 instead of posix forward slashes. If this is the case, several tests 29 fail, so skip them. 30 """ 31 found_backslash = '\\' in ABSTFN 32 msg = "ABSTFN is not a posix path - tests fail" 33 return [test, unittest.skip(msg)(test)][found_backslash] 34 35def safe_rmdir(dirname): 36 try: 37 os.rmdir(dirname) 38 except OSError: 39 pass 40 41def _parameterize(*parameters): 42 """Simplistic decorator to parametrize a test 43 44 Runs the decorated test multiple times in subTest, with a value from 45 'parameters' passed as an extra positional argument. 46 Does *not* call doCleanups() after each run. 47 48 Not for general use. Intended to avoid indenting for easier backports. 49 50 See https://discuss.python.org/t/91827 for discussing generalizations. 51 """ 52 def _parametrize_decorator(func): 53 def _parameterized(self, *args, **kwargs): 54 for parameter in parameters: 55 with self.subTest(parameter): 56 func(self, *args, parameter, **kwargs) 57 return _parameterized 58 return _parametrize_decorator 59 60 61class PosixPathTest(unittest.TestCase): 62 63 def setUp(self): 64 self.tearDown() 65 66 def tearDown(self): 67 for suffix in ["", "1", "2"]: 68 os_helper.unlink(os_helper.TESTFN + suffix) 69 safe_rmdir(os_helper.TESTFN + suffix) 70 71 def test_join(self): 72 self.assertEqual(posixpath.join("/foo", "bar", "/bar", "baz"), 73 "/bar/baz") 74 self.assertEqual(posixpath.join("/foo", "bar", "baz"), "/foo/bar/baz") 75 self.assertEqual(posixpath.join("/foo/", "bar/", "baz/"), 76 "/foo/bar/baz/") 77 78 self.assertEqual(posixpath.join(b"/foo", b"bar", b"/bar", b"baz"), 79 b"/bar/baz") 80 self.assertEqual(posixpath.join(b"/foo", b"bar", b"baz"), 81 b"/foo/bar/baz") 82 self.assertEqual(posixpath.join(b"/foo/", b"bar/", b"baz/"), 83 b"/foo/bar/baz/") 84 85 def test_split(self): 86 self.assertEqual(posixpath.split("/foo/bar"), ("/foo", "bar")) 87 self.assertEqual(posixpath.split("/"), ("/", "")) 88 self.assertEqual(posixpath.split("foo"), ("", "foo")) 89 self.assertEqual(posixpath.split("////foo"), ("////", "foo")) 90 self.assertEqual(posixpath.split("//foo//bar"), ("//foo", "bar")) 91 92 self.assertEqual(posixpath.split(b"/foo/bar"), (b"/foo", b"bar")) 93 self.assertEqual(posixpath.split(b"/"), (b"/", b"")) 94 self.assertEqual(posixpath.split(b"foo"), (b"", b"foo")) 95 self.assertEqual(posixpath.split(b"////foo"), (b"////", b"foo")) 96 self.assertEqual(posixpath.split(b"//foo//bar"), (b"//foo", b"bar")) 97 98 def splitextTest(self, path, filename, ext): 99 self.assertEqual(posixpath.splitext(path), (filename, ext)) 100 self.assertEqual(posixpath.splitext("/" + path), ("/" + filename, ext)) 101 self.assertEqual(posixpath.splitext("abc/" + path), 102 ("abc/" + filename, ext)) 103 self.assertEqual(posixpath.splitext("abc.def/" + path), 104 ("abc.def/" + filename, ext)) 105 self.assertEqual(posixpath.splitext("/abc.def/" + path), 106 ("/abc.def/" + filename, ext)) 107 self.assertEqual(posixpath.splitext(path + "/"), 108 (filename + ext + "/", "")) 109 110 path = bytes(path, "ASCII") 111 filename = bytes(filename, "ASCII") 112 ext = bytes(ext, "ASCII") 113 114 self.assertEqual(posixpath.splitext(path), (filename, ext)) 115 self.assertEqual(posixpath.splitext(b"/" + path), 116 (b"/" + filename, ext)) 117 self.assertEqual(posixpath.splitext(b"abc/" + path), 118 (b"abc/" + filename, ext)) 119 self.assertEqual(posixpath.splitext(b"abc.def/" + path), 120 (b"abc.def/" + filename, ext)) 121 self.assertEqual(posixpath.splitext(b"/abc.def/" + path), 122 (b"/abc.def/" + filename, ext)) 123 self.assertEqual(posixpath.splitext(path + b"/"), 124 (filename + ext + b"/", b"")) 125 126 def test_splitext(self): 127 self.splitextTest("foo.bar", "foo", ".bar") 128 self.splitextTest("foo.boo.bar", "foo.boo", ".bar") 129 self.splitextTest("foo.boo.biff.bar", "foo.boo.biff", ".bar") 130 self.splitextTest(".csh.rc", ".csh", ".rc") 131 self.splitextTest("nodots", "nodots", "") 132 self.splitextTest(".cshrc", ".cshrc", "") 133 self.splitextTest("...manydots", "...manydots", "") 134 self.splitextTest("...manydots.ext", "...manydots", ".ext") 135 self.splitextTest(".", ".", "") 136 self.splitextTest("..", "..", "") 137 self.splitextTest("........", "........", "") 138 self.splitextTest("", "", "") 139 140 def test_isabs(self): 141 self.assertIs(posixpath.isabs(""), False) 142 self.assertIs(posixpath.isabs("/"), True) 143 self.assertIs(posixpath.isabs("/foo"), True) 144 self.assertIs(posixpath.isabs("/foo/bar"), True) 145 self.assertIs(posixpath.isabs("foo/bar"), False) 146 147 self.assertIs(posixpath.isabs(b""), False) 148 self.assertIs(posixpath.isabs(b"/"), True) 149 self.assertIs(posixpath.isabs(b"/foo"), True) 150 self.assertIs(posixpath.isabs(b"/foo/bar"), True) 151 self.assertIs(posixpath.isabs(b"foo/bar"), False) 152 153 def test_basename(self): 154 self.assertEqual(posixpath.basename("/foo/bar"), "bar") 155 self.assertEqual(posixpath.basename("/"), "") 156 self.assertEqual(posixpath.basename("foo"), "foo") 157 self.assertEqual(posixpath.basename("////foo"), "foo") 158 self.assertEqual(posixpath.basename("//foo//bar"), "bar") 159 160 self.assertEqual(posixpath.basename(b"/foo/bar"), b"bar") 161 self.assertEqual(posixpath.basename(b"/"), b"") 162 self.assertEqual(posixpath.basename(b"foo"), b"foo") 163 self.assertEqual(posixpath.basename(b"////foo"), b"foo") 164 self.assertEqual(posixpath.basename(b"//foo//bar"), b"bar") 165 166 def test_dirname(self): 167 self.assertEqual(posixpath.dirname("/foo/bar"), "/foo") 168 self.assertEqual(posixpath.dirname("/"), "/") 169 self.assertEqual(posixpath.dirname("foo"), "") 170 self.assertEqual(posixpath.dirname("////foo"), "////") 171 self.assertEqual(posixpath.dirname("//foo//bar"), "//foo") 172 173 self.assertEqual(posixpath.dirname(b"/foo/bar"), b"/foo") 174 self.assertEqual(posixpath.dirname(b"/"), b"/") 175 self.assertEqual(posixpath.dirname(b"foo"), b"") 176 self.assertEqual(posixpath.dirname(b"////foo"), b"////") 177 self.assertEqual(posixpath.dirname(b"//foo//bar"), b"//foo") 178 179 def test_islink(self): 180 self.assertIs(posixpath.islink(os_helper.TESTFN + "1"), False) 181 self.assertIs(posixpath.lexists(os_helper.TESTFN + "2"), False) 182 183 with open(os_helper.TESTFN + "1", "wb") as f: 184 f.write(b"foo") 185 self.assertIs(posixpath.islink(os_helper.TESTFN + "1"), False) 186 187 if os_helper.can_symlink(): 188 os.symlink(os_helper.TESTFN + "1", os_helper.TESTFN + "2") 189 self.assertIs(posixpath.islink(os_helper.TESTFN + "2"), True) 190 os.remove(os_helper.TESTFN + "1") 191 self.assertIs(posixpath.islink(os_helper.TESTFN + "2"), True) 192 self.assertIs(posixpath.exists(os_helper.TESTFN + "2"), False) 193 self.assertIs(posixpath.lexists(os_helper.TESTFN + "2"), True) 194 195 self.assertIs(posixpath.islink(os_helper.TESTFN + "\udfff"), False) 196 self.assertIs(posixpath.islink(os.fsencode(os_helper.TESTFN) + b"\xff"), False) 197 self.assertIs(posixpath.islink(os_helper.TESTFN + "\x00"), False) 198 self.assertIs(posixpath.islink(os.fsencode(os_helper.TESTFN) + b"\x00"), False) 199 200 def test_ismount(self): 201 self.assertIs(posixpath.ismount("/"), True) 202 self.assertIs(posixpath.ismount(b"/"), True) 203 self.assertIs(posixpath.ismount(FakePath("/")), True) 204 self.assertIs(posixpath.ismount(FakePath(b"/")), True) 205 206 def test_ismount_non_existent(self): 207 # Non-existent mountpoint. 208 self.assertIs(posixpath.ismount(ABSTFN), False) 209 try: 210 os.mkdir(ABSTFN) 211 self.assertIs(posixpath.ismount(ABSTFN), False) 212 finally: 213 safe_rmdir(ABSTFN) 214 215 self.assertIs(posixpath.ismount('/\udfff'), False) 216 self.assertIs(posixpath.ismount(b'/\xff'), False) 217 self.assertIs(posixpath.ismount('/\x00'), False) 218 self.assertIs(posixpath.ismount(b'/\x00'), False) 219 220 @os_helper.skip_unless_symlink 221 def test_ismount_symlinks(self): 222 # Symlinks are never mountpoints. 223 try: 224 os.symlink("/", ABSTFN) 225 self.assertIs(posixpath.ismount(ABSTFN), False) 226 finally: 227 os.unlink(ABSTFN) 228 229 @unittest.skipIf(posix is None, "Test requires posix module") 230 def test_ismount_different_device(self): 231 # Simulate the path being on a different device from its parent by 232 # mocking out st_dev. 233 save_lstat = os.lstat 234 def fake_lstat(path): 235 st_ino = 0 236 st_dev = 0 237 if path == ABSTFN: 238 st_dev = 1 239 st_ino = 1 240 return posix.stat_result((0, st_ino, st_dev, 0, 0, 0, 0, 0, 0, 0)) 241 try: 242 os.lstat = fake_lstat 243 self.assertIs(posixpath.ismount(ABSTFN), True) 244 finally: 245 os.lstat = save_lstat 246 247 @unittest.skipIf(posix is None, "Test requires posix module") 248 def test_ismount_directory_not_readable(self): 249 # issue #2466: Simulate ismount run on a directory that is not 250 # readable, which used to return False. 251 save_lstat = os.lstat 252 def fake_lstat(path): 253 st_ino = 0 254 st_dev = 0 255 if path.startswith(ABSTFN) and path != ABSTFN: 256 # ismount tries to read something inside the ABSTFN directory; 257 # simulate this being forbidden (no read permission). 258 raise OSError("Fake [Errno 13] Permission denied") 259 if path == ABSTFN: 260 st_dev = 1 261 st_ino = 1 262 return posix.stat_result((0, st_ino, st_dev, 0, 0, 0, 0, 0, 0, 0)) 263 try: 264 os.lstat = fake_lstat 265 self.assertIs(posixpath.ismount(ABSTFN), True) 266 finally: 267 os.lstat = save_lstat 268 269 def test_expanduser(self): 270 self.assertEqual(posixpath.expanduser("foo"), "foo") 271 self.assertEqual(posixpath.expanduser(b"foo"), b"foo") 272 273 def test_expanduser_home_envvar(self): 274 with os_helper.EnvironmentVarGuard() as env: 275 env['HOME'] = '/home/victor' 276 self.assertEqual(posixpath.expanduser("~"), "/home/victor") 277 278 # expanduser() strips trailing slash 279 env['HOME'] = '/home/victor/' 280 self.assertEqual(posixpath.expanduser("~"), "/home/victor") 281 282 for home in '/', '', '//', '///': 283 with self.subTest(home=home): 284 env['HOME'] = home 285 self.assertEqual(posixpath.expanduser("~"), "/") 286 self.assertEqual(posixpath.expanduser("~/"), "/") 287 self.assertEqual(posixpath.expanduser("~/foo"), "/foo") 288 289 @unittest.skipIf(sys.platform == "vxworks", 290 "no home directory on VxWorks") 291 def test_expanduser_pwd(self): 292 pwd = import_helper.import_module('pwd') 293 294 self.assertIsInstance(posixpath.expanduser("~/"), str) 295 self.assertIsInstance(posixpath.expanduser(b"~/"), bytes) 296 297 # if home directory == root directory, this test makes no sense 298 if posixpath.expanduser("~") != '/': 299 self.assertEqual( 300 posixpath.expanduser("~") + "/", 301 posixpath.expanduser("~/") 302 ) 303 self.assertEqual( 304 posixpath.expanduser(b"~") + b"/", 305 posixpath.expanduser(b"~/") 306 ) 307 self.assertIsInstance(posixpath.expanduser("~root/"), str) 308 self.assertIsInstance(posixpath.expanduser("~foo/"), str) 309 self.assertIsInstance(posixpath.expanduser(b"~root/"), bytes) 310 self.assertIsInstance(posixpath.expanduser(b"~foo/"), bytes) 311 312 with os_helper.EnvironmentVarGuard() as env: 313 # expanduser should fall back to using the password database 314 del env['HOME'] 315 316 home = pwd.getpwuid(os.getuid()).pw_dir 317 # $HOME can end with a trailing /, so strip it (see #17809) 318 home = home.rstrip("/") or '/' 319 self.assertEqual(posixpath.expanduser("~"), home) 320 321 # bpo-10496: If the HOME environment variable is not set and the 322 # user (current identifier or name in the path) doesn't exist in 323 # the password database (pwd.getuid() or pwd.getpwnam() fail), 324 # expanduser() must return the path unchanged. 325 with mock.patch.object(pwd, 'getpwuid', side_effect=KeyError), \ 326 mock.patch.object(pwd, 'getpwnam', side_effect=KeyError): 327 for path in ('~', '~/.local', '~vstinner/'): 328 self.assertEqual(posixpath.expanduser(path), path) 329 330 NORMPATH_CASES = [ 331 ("", "."), 332 ("/", "/"), 333 ("/.", "/"), 334 ("/./", "/"), 335 ("/.//.", "/"), 336 ("/foo", "/foo"), 337 ("/foo/bar", "/foo/bar"), 338 ("//", "//"), 339 ("///", "/"), 340 ("///foo/.//bar//", "/foo/bar"), 341 ("///foo/.//bar//.//..//.//baz///", "/foo/baz"), 342 ("///..//./foo/.//bar", "/foo/bar"), 343 (".", "."), 344 (".//.", "."), 345 ("..", ".."), 346 ("../", ".."), 347 ("../foo", "../foo"), 348 ("../../foo", "../../foo"), 349 ("../foo/../bar", "../bar"), 350 ("../../foo/../bar/./baz/boom/..", "../../bar/baz"), 351 ("/..", "/"), 352 ("/..", "/"), 353 ("/../", "/"), 354 ("/..//", "/"), 355 ("//.", "//"), 356 ("//..", "//"), 357 ("//...", "//..."), 358 ("//../foo", "//foo"), 359 ("//../../foo", "//foo"), 360 ("/../foo", "/foo"), 361 ("/../../foo", "/foo"), 362 ("/../foo/../", "/"), 363 ("/../foo/../bar", "/bar"), 364 ("/../../foo/../bar/./baz/boom/..", "/bar/baz"), 365 ("/../../foo/../bar/./baz/boom/.", "/bar/baz/boom"), 366 ("foo/../bar/baz", "bar/baz"), 367 ("foo/../../bar/baz", "../bar/baz"), 368 ("foo/../../../bar/baz", "../../bar/baz"), 369 ("foo///../bar/.././../baz/boom", "../baz/boom"), 370 ("foo/bar/../..///../../baz/boom", "../../baz/boom"), 371 ("/foo/..", "/"), 372 ("/foo/../..", "/"), 373 ("//foo/..", "//"), 374 ("//foo/../..", "//"), 375 ("///foo/..", "/"), 376 ("///foo/../..", "/"), 377 ("////foo/..", "/"), 378 ("/////foo/..", "/"), 379 ] 380 381 def test_normpath(self): 382 for path, expected in self.NORMPATH_CASES: 383 with self.subTest(path): 384 result = posixpath.normpath(path) 385 self.assertEqual(result, expected) 386 387 path = path.encode('utf-8') 388 expected = expected.encode('utf-8') 389 with self.subTest(path, type=bytes): 390 result = posixpath.normpath(path) 391 self.assertEqual(result, expected) 392 393 @skip_if_ABSTFN_contains_backslash 394 @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING}) 395 def test_realpath_curdir(self, kwargs): 396 self.assertEqual(realpath('.', **kwargs), os.getcwd()) 397 self.assertEqual(realpath('./.', **kwargs), os.getcwd()) 398 self.assertEqual(realpath('/'.join(['.'] * 100), **kwargs), os.getcwd()) 399 400 self.assertEqual(realpath(b'.', **kwargs), os.getcwdb()) 401 self.assertEqual(realpath(b'./.', **kwargs), os.getcwdb()) 402 self.assertEqual(realpath(b'/'.join([b'.'] * 100), **kwargs), os.getcwdb()) 403 404 @skip_if_ABSTFN_contains_backslash 405 @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING}) 406 def test_realpath_pardir(self, kwargs): 407 self.assertEqual(realpath('..', **kwargs), dirname(os.getcwd())) 408 self.assertEqual(realpath('../..', **kwargs), dirname(dirname(os.getcwd()))) 409 self.assertEqual(realpath('/'.join(['..'] * 100), **kwargs), '/') 410 411 self.assertEqual(realpath(b'..', **kwargs), dirname(os.getcwdb())) 412 self.assertEqual(realpath(b'../..', **kwargs), dirname(dirname(os.getcwdb()))) 413 self.assertEqual(realpath(b'/'.join([b'..'] * 100), **kwargs), b'/') 414 415 @os_helper.skip_unless_symlink 416 @skip_if_ABSTFN_contains_backslash 417 @_parameterize({}, {'strict': ALLOW_MISSING}) 418 def test_realpath_basic(self, kwargs): 419 # Basic operation. 420 try: 421 os.symlink(ABSTFN+"1", ABSTFN) 422 self.assertEqual(realpath(ABSTFN, **kwargs), ABSTFN+"1") 423 finally: 424 os_helper.unlink(ABSTFN) 425 426 @os_helper.skip_unless_symlink 427 @skip_if_ABSTFN_contains_backslash 428 def test_realpath_strict(self): 429 # Bug #43757: raise FileNotFoundError in strict mode if we encounter 430 # a path that does not exist. 431 try: 432 os.symlink(ABSTFN+"1", ABSTFN) 433 self.assertRaises(FileNotFoundError, realpath, ABSTFN, strict=True) 434 self.assertRaises(FileNotFoundError, realpath, ABSTFN + "2", strict=True) 435 finally: 436 os_helper.unlink(ABSTFN) 437 438 def test_realpath_invalid_paths(self): 439 path = '/\x00' 440 self.assertRaises(ValueError, realpath, path, strict=False) 441 self.assertRaises(ValueError, realpath, path, strict=True) 442 self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING) 443 path = b'/\x00' 444 self.assertRaises(ValueError, realpath, path, strict=False) 445 self.assertRaises(ValueError, realpath, path, strict=True) 446 self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING) 447 path = '/nonexistent/x\x00' 448 self.assertRaises(ValueError, realpath, path, strict=False) 449 self.assertRaises(FileNotFoundError, realpath, path, strict=True) 450 self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING) 451 path = b'/nonexistent/x\x00' 452 self.assertRaises(ValueError, realpath, path, strict=False) 453 self.assertRaises(FileNotFoundError, realpath, path, strict=True) 454 self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING) 455 path = '/\x00/..' 456 self.assertRaises(ValueError, realpath, path, strict=False) 457 self.assertRaises(ValueError, realpath, path, strict=True) 458 self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING) 459 path = b'/\x00/..' 460 self.assertRaises(ValueError, realpath, path, strict=False) 461 self.assertRaises(ValueError, realpath, path, strict=True) 462 self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING) 463 464 path = '/nonexistent/x\x00/..' 465 self.assertRaises(ValueError, realpath, path, strict=False) 466 self.assertRaises(FileNotFoundError, realpath, path, strict=True) 467 self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING) 468 path = b'/nonexistent/x\x00/..' 469 self.assertRaises(ValueError, realpath, path, strict=False) 470 self.assertRaises(FileNotFoundError, realpath, path, strict=True) 471 self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING) 472 473 path = '/\udfff' 474 if sys.platform == 'win32': 475 self.assertEqual(realpath(path, strict=False), path) 476 self.assertRaises(FileNotFoundError, realpath, path, strict=True) 477 self.assertEqual(realpath(path, strict=ALLOW_MISSING), path) 478 else: 479 self.assertRaises(UnicodeEncodeError, realpath, path, strict=False) 480 self.assertRaises(UnicodeEncodeError, realpath, path, strict=True) 481 self.assertRaises(UnicodeEncodeError, realpath, path, strict=ALLOW_MISSING) 482 path = '/nonexistent/\udfff' 483 if sys.platform == 'win32': 484 self.assertEqual(realpath(path, strict=False), path) 485 self.assertEqual(realpath(path, strict=ALLOW_MISSING), path) 486 else: 487 self.assertRaises(UnicodeEncodeError, realpath, path, strict=False) 488 self.assertRaises(UnicodeEncodeError, realpath, path, strict=ALLOW_MISSING) 489 self.assertRaises(FileNotFoundError, realpath, path, strict=True) 490 path = '/\udfff/..' 491 if sys.platform == 'win32': 492 self.assertEqual(realpath(path, strict=False), '/') 493 self.assertRaises(FileNotFoundError, realpath, path, strict=True) 494 self.assertEqual(realpath(path, strict=ALLOW_MISSING), '/') 495 else: 496 self.assertRaises(UnicodeEncodeError, realpath, path, strict=False) 497 self.assertRaises(UnicodeEncodeError, realpath, path, strict=True) 498 self.assertRaises(UnicodeEncodeError, realpath, path, strict=ALLOW_MISSING) 499 path = '/nonexistent/\udfff/..' 500 if sys.platform == 'win32': 501 self.assertEqual(realpath(path, strict=False), '/nonexistent') 502 self.assertEqual(realpath(path, strict=ALLOW_MISSING), '/nonexistent') 503 else: 504 self.assertRaises(UnicodeEncodeError, realpath, path, strict=False) 505 self.assertRaises(UnicodeEncodeError, realpath, path, strict=ALLOW_MISSING) 506 self.assertRaises(FileNotFoundError, realpath, path, strict=True) 507 508 path = b'/\xff' 509 if sys.platform == 'win32': 510 self.assertRaises(UnicodeDecodeError, realpath, path, strict=False) 511 self.assertRaises(UnicodeDecodeError, realpath, path, strict=True) 512 self.assertRaises(UnicodeDecodeError, realpath, path, strict=ALLOW_MISSING) 513 else: 514 self.assertEqual(realpath(path, strict=False), path) 515 if support.is_wasi: 516 self.assertRaises(OSError, realpath, path, strict=True) 517 self.assertRaises(OSError, realpath, path, strict=ALLOW_MISSING) 518 else: 519 self.assertRaises(FileNotFoundError, realpath, path, strict=True) 520 self.assertEqual(realpath(path, strict=ALLOW_MISSING), path) 521 path = b'/nonexistent/\xff' 522 if sys.platform == 'win32': 523 self.assertRaises(UnicodeDecodeError, realpath, path, strict=False) 524 self.assertRaises(UnicodeDecodeError, realpath, path, strict=ALLOW_MISSING) 525 else: 526 self.assertEqual(realpath(path, strict=False), path) 527 if support.is_wasi: 528 self.assertRaises(OSError, realpath, path, strict=True) 529 self.assertRaises(OSError, realpath, path, strict=ALLOW_MISSING) 530 else: 531 self.assertRaises(FileNotFoundError, realpath, path, strict=True) 532 533 @os_helper.skip_unless_symlink 534 @skip_if_ABSTFN_contains_backslash 535 @_parameterize({}, {'strict': ALLOW_MISSING}) 536 def test_realpath_relative(self, kwargs): 537 try: 538 os.symlink(posixpath.relpath(ABSTFN+"1"), ABSTFN) 539 self.assertEqual(realpath(ABSTFN, **kwargs), ABSTFN+"1") 540 finally: 541 os_helper.unlink(ABSTFN) 542 543 @os_helper.skip_unless_symlink 544 @skip_if_ABSTFN_contains_backslash 545 @_parameterize({}, {'strict': ALLOW_MISSING}) 546 def test_realpath_missing_pardir(self, kwargs): 547 try: 548 os.symlink(os_helper.TESTFN + "1", os_helper.TESTFN) 549 self.assertEqual( 550 realpath("nonexistent/../" + os_helper.TESTFN, **kwargs), ABSTFN + "1") 551 finally: 552 os_helper.unlink(os_helper.TESTFN) 553 554 @os_helper.skip_unless_symlink 555 @skip_if_ABSTFN_contains_backslash 556 def test_realpath_symlink_loops(self): 557 # Bug #930024, return the path unchanged if we get into an infinite 558 # symlink loop in non-strict mode (default). 559 try: 560 os.symlink(ABSTFN, ABSTFN) 561 self.assertEqual(realpath(ABSTFN), ABSTFN) 562 563 os.symlink(ABSTFN+"1", ABSTFN+"2") 564 os.symlink(ABSTFN+"2", ABSTFN+"1") 565 self.assertEqual(realpath(ABSTFN+"1"), ABSTFN+"1") 566 self.assertEqual(realpath(ABSTFN+"2"), ABSTFN+"2") 567 568 self.assertEqual(realpath(ABSTFN+"1/x"), ABSTFN+"1/x") 569 self.assertEqual(realpath(ABSTFN+"1/.."), dirname(ABSTFN)) 570 self.assertEqual(realpath(ABSTFN+"1/../x"), dirname(ABSTFN) + "/x") 571 os.symlink(ABSTFN+"x", ABSTFN+"y") 572 self.assertEqual(realpath(ABSTFN+"1/../" + basename(ABSTFN) + "y"), 573 ABSTFN + "y") 574 self.assertEqual(realpath(ABSTFN+"1/../" + basename(ABSTFN) + "1"), 575 ABSTFN + "1") 576 577 os.symlink(basename(ABSTFN) + "a/b", ABSTFN+"a") 578 self.assertEqual(realpath(ABSTFN+"a"), ABSTFN+"a/b") 579 580 os.symlink("../" + basename(dirname(ABSTFN)) + "/" + 581 basename(ABSTFN) + "c", ABSTFN+"c") 582 self.assertEqual(realpath(ABSTFN+"c"), ABSTFN+"c") 583 584 # Test using relative path as well. 585 with os_helper.change_cwd(dirname(ABSTFN)): 586 self.assertEqual(realpath(basename(ABSTFN)), ABSTFN) 587 finally: 588 os_helper.unlink(ABSTFN) 589 os_helper.unlink(ABSTFN+"1") 590 os_helper.unlink(ABSTFN+"2") 591 os_helper.unlink(ABSTFN+"y") 592 os_helper.unlink(ABSTFN+"c") 593 os_helper.unlink(ABSTFN+"a") 594 595 @os_helper.skip_unless_symlink 596 @skip_if_ABSTFN_contains_backslash 597 @_parameterize({'strict': True}, {'strict': ALLOW_MISSING}) 598 def test_realpath_symlink_loops_strict(self, kwargs): 599 # Bug #43757, raise OSError if we get into an infinite symlink loop in 600 # the strict modes. 601 try: 602 os.symlink(ABSTFN, ABSTFN) 603 self.assertRaises(OSError, realpath, ABSTFN, **kwargs) 604 605 os.symlink(ABSTFN+"1", ABSTFN+"2") 606 os.symlink(ABSTFN+"2", ABSTFN+"1") 607 self.assertRaises(OSError, realpath, ABSTFN+"1", **kwargs) 608 self.assertRaises(OSError, realpath, ABSTFN+"2", **kwargs) 609 610 self.assertRaises(OSError, realpath, ABSTFN+"1/x", **kwargs) 611 self.assertRaises(OSError, realpath, ABSTFN+"1/..", **kwargs) 612 self.assertRaises(OSError, realpath, ABSTFN+"1/../x", **kwargs) 613 os.symlink(ABSTFN+"x", ABSTFN+"y") 614 self.assertRaises(OSError, realpath, 615 ABSTFN+"1/../" + basename(ABSTFN) + "y", **kwargs) 616 self.assertRaises(OSError, realpath, 617 ABSTFN+"1/../" + basename(ABSTFN) + "1", **kwargs) 618 619 os.symlink(basename(ABSTFN) + "a/b", ABSTFN+"a") 620 self.assertRaises(OSError, realpath, ABSTFN+"a", **kwargs) 621 622 os.symlink("../" + basename(dirname(ABSTFN)) + "/" + 623 basename(ABSTFN) + "c", ABSTFN+"c") 624 self.assertRaises(OSError, realpath, ABSTFN+"c", **kwargs) 625 626 # Test using relative path as well. 627 with os_helper.change_cwd(dirname(ABSTFN)): 628 self.assertRaises(OSError, realpath, basename(ABSTFN), **kwargs) 629 finally: 630 os_helper.unlink(ABSTFN) 631 os_helper.unlink(ABSTFN+"1") 632 os_helper.unlink(ABSTFN+"2") 633 os_helper.unlink(ABSTFN+"y") 634 os_helper.unlink(ABSTFN+"c") 635 os_helper.unlink(ABSTFN+"a") 636 637 @os_helper.skip_unless_symlink 638 @skip_if_ABSTFN_contains_backslash 639 @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING}) 640 def test_realpath_repeated_indirect_symlinks(self, kwargs): 641 # Issue #6975. 642 try: 643 os.mkdir(ABSTFN) 644 os.symlink('../' + basename(ABSTFN), ABSTFN + '/self') 645 os.symlink('self/self/self', ABSTFN + '/link') 646 self.assertEqual(realpath(ABSTFN + '/link', **kwargs), ABSTFN) 647 finally: 648 os_helper.unlink(ABSTFN + '/self') 649 os_helper.unlink(ABSTFN + '/link') 650 safe_rmdir(ABSTFN) 651 652 @os_helper.skip_unless_symlink 653 @skip_if_ABSTFN_contains_backslash 654 @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING}) 655 def test_realpath_deep_recursion(self, kwargs): 656 depth = 10 657 try: 658 os.mkdir(ABSTFN) 659 for i in range(depth): 660 os.symlink('/'.join(['%d' % i] * 10), ABSTFN + '/%d' % (i + 1)) 661 os.symlink('.', ABSTFN + '/0') 662 self.assertEqual(realpath(ABSTFN + '/%d' % depth, **kwargs), ABSTFN) 663 664 # Test using relative path as well. 665 with os_helper.change_cwd(ABSTFN): 666 self.assertEqual(realpath('%d' % depth), ABSTFN) 667 finally: 668 for i in range(depth + 1): 669 os_helper.unlink(ABSTFN + '/%d' % i) 670 safe_rmdir(ABSTFN) 671 672 @os_helper.skip_unless_symlink 673 @skip_if_ABSTFN_contains_backslash 674 @_parameterize({}, {'strict': ALLOW_MISSING}) 675 def test_realpath_resolve_parents(self, kwargs): 676 # We also need to resolve any symlinks in the parents of a relative 677 # path passed to realpath. E.g.: current working directory is 678 # /usr/doc with 'doc' being a symlink to /usr/share/doc. We call 679 # realpath("a"). This should return /usr/share/doc/a/. 680 try: 681 os.mkdir(ABSTFN) 682 os.mkdir(ABSTFN + "/y") 683 os.symlink(ABSTFN + "/y", ABSTFN + "/k") 684 685 with os_helper.change_cwd(ABSTFN + "/k"): 686 self.assertEqual(realpath("a", **kwargs), 687 ABSTFN + "/y/a") 688 finally: 689 os_helper.unlink(ABSTFN + "/k") 690 safe_rmdir(ABSTFN + "/y") 691 safe_rmdir(ABSTFN) 692 693 @os_helper.skip_unless_symlink 694 @skip_if_ABSTFN_contains_backslash 695 @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING}) 696 def test_realpath_resolve_before_normalizing(self, kwargs): 697 # Bug #990669: Symbolic links should be resolved before we 698 # normalize the path. E.g.: if we have directories 'a', 'k' and 'y' 699 # in the following hierarchy: 700 # a/k/y 701 # 702 # and a symbolic link 'link-y' pointing to 'y' in directory 'a', 703 # then realpath("link-y/..") should return 'k', not 'a'. 704 try: 705 os.mkdir(ABSTFN) 706 os.mkdir(ABSTFN + "/k") 707 os.mkdir(ABSTFN + "/k/y") 708 os.symlink(ABSTFN + "/k/y", ABSTFN + "/link-y") 709 710 # Absolute path. 711 self.assertEqual(realpath(ABSTFN + "/link-y/..", **kwargs), ABSTFN + "/k") 712 # Relative path. 713 with os_helper.change_cwd(dirname(ABSTFN)): 714 self.assertEqual(realpath(basename(ABSTFN) + "/link-y/..", **kwargs), 715 ABSTFN + "/k") 716 finally: 717 os_helper.unlink(ABSTFN + "/link-y") 718 safe_rmdir(ABSTFN + "/k/y") 719 safe_rmdir(ABSTFN + "/k") 720 safe_rmdir(ABSTFN) 721 722 @os_helper.skip_unless_symlink 723 @skip_if_ABSTFN_contains_backslash 724 @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING}) 725 def test_realpath_resolve_first(self, kwargs): 726 # Bug #1213894: The first component of the path, if not absolute, 727 # must be resolved too. 728 729 try: 730 os.mkdir(ABSTFN) 731 os.mkdir(ABSTFN + "/k") 732 os.symlink(ABSTFN, ABSTFN + "link") 733 with os_helper.change_cwd(dirname(ABSTFN)): 734 base = basename(ABSTFN) 735 self.assertEqual(realpath(base + "link", **kwargs), ABSTFN) 736 self.assertEqual(realpath(base + "link/k", **kwargs), ABSTFN + "/k") 737 finally: 738 os_helper.unlink(ABSTFN + "link") 739 safe_rmdir(ABSTFN + "/k") 740 safe_rmdir(ABSTFN) 741 742 @os_helper.skip_unless_symlink 743 @skip_if_ABSTFN_contains_backslash 744 @unittest.skipIf(os.chmod not in os.supports_follow_symlinks, "Can't set symlink permissions") 745 @unittest.skipIf(sys.platform != "darwin", "only macOS requires read permission to readlink()") 746 @_parameterize({'strict': True}, {'strict': ALLOW_MISSING}) 747 def test_realpath_unreadable_symlink_strict(self, kwargs): 748 try: 749 os.symlink(ABSTFN+"1", ABSTFN) 750 os.chmod(ABSTFN, 0o000, follow_symlinks=False) 751 with self.assertRaises(PermissionError): 752 realpath(ABSTFN, **kwargs) 753 with self.assertRaises(PermissionError): 754 realpath(ABSTFN + '/foo', **kwargs), 755 with self.assertRaises(PermissionError): 756 realpath(ABSTFN + '/../foo', **kwargs) 757 with self.assertRaises(PermissionError): 758 realpath(ABSTFN + '/foo/..', **kwargs) 759 finally: 760 os.chmod(ABSTFN, 0o755, follow_symlinks=False) 761 os.unlink(ABSTFN) 762 763 @skip_if_ABSTFN_contains_backslash 764 @os_helper.skip_unless_symlink 765 def test_realpath_unreadable_directory(self): 766 try: 767 os.mkdir(ABSTFN) 768 os.mkdir(ABSTFN + '/k') 769 os.chmod(ABSTFN, 0o000) 770 self.assertEqual(realpath(ABSTFN, strict=False), ABSTFN) 771 self.assertEqual(realpath(ABSTFN, strict=True), ABSTFN) 772 self.assertEqual(realpath(ABSTFN, strict=ALLOW_MISSING), ABSTFN) 773 774 try: 775 os.stat(ABSTFN) 776 except PermissionError: 777 pass 778 else: 779 self.skipTest('Cannot block permissions') 780 781 self.assertEqual(realpath(ABSTFN + '/k', strict=False), 782 ABSTFN + '/k') 783 self.assertRaises(PermissionError, realpath, ABSTFN + '/k', 784 strict=True) 785 self.assertRaises(PermissionError, realpath, ABSTFN + '/k', 786 strict=ALLOW_MISSING) 787 788 self.assertEqual(realpath(ABSTFN + '/missing', strict=False), 789 ABSTFN + '/missing') 790 self.assertRaises(PermissionError, realpath, ABSTFN + '/missing', 791 strict=True) 792 self.assertRaises(PermissionError, realpath, ABSTFN + '/missing', 793 strict=ALLOW_MISSING) 794 finally: 795 os.chmod(ABSTFN, 0o755) 796 safe_rmdir(ABSTFN + '/k') 797 safe_rmdir(ABSTFN) 798 799 def test_relpath(self): 800 (real_getcwd, os.getcwd) = (os.getcwd, lambda: r"/home/user/bar") 801 try: 802 curdir = os.path.split(os.getcwd())[-1] 803 self.assertRaises(ValueError, posixpath.relpath, "") 804 self.assertEqual(posixpath.relpath("a"), "a") 805 self.assertEqual(posixpath.relpath(posixpath.abspath("a")), "a") 806 self.assertEqual(posixpath.relpath("a/b"), "a/b") 807 self.assertEqual(posixpath.relpath("../a/b"), "../a/b") 808 self.assertEqual(posixpath.relpath("a", "../b"), "../"+curdir+"/a") 809 self.assertEqual(posixpath.relpath("a/b", "../c"), 810 "../"+curdir+"/a/b") 811 self.assertEqual(posixpath.relpath("a", "b/c"), "../../a") 812 self.assertEqual(posixpath.relpath("a", "a"), ".") 813 self.assertEqual(posixpath.relpath("/foo/bar/bat", "/x/y/z"), '../../../foo/bar/bat') 814 self.assertEqual(posixpath.relpath("/foo/bar/bat", "/foo/bar"), 'bat') 815 self.assertEqual(posixpath.relpath("/foo/bar/bat", "/"), 'foo/bar/bat') 816 self.assertEqual(posixpath.relpath("/", "/foo/bar/bat"), '../../..') 817 self.assertEqual(posixpath.relpath("/foo/bar/bat", "/x"), '../foo/bar/bat') 818 self.assertEqual(posixpath.relpath("/x", "/foo/bar/bat"), '../../../x') 819 self.assertEqual(posixpath.relpath("/", "/"), '.') 820 self.assertEqual(posixpath.relpath("/a", "/a"), '.') 821 self.assertEqual(posixpath.relpath("/a/b", "/a/b"), '.') 822 finally: 823 os.getcwd = real_getcwd 824 825 def test_relpath_bytes(self): 826 (real_getcwdb, os.getcwdb) = (os.getcwdb, lambda: br"/home/user/bar") 827 try: 828 curdir = os.path.split(os.getcwdb())[-1] 829 self.assertRaises(ValueError, posixpath.relpath, b"") 830 self.assertEqual(posixpath.relpath(b"a"), b"a") 831 self.assertEqual(posixpath.relpath(posixpath.abspath(b"a")), b"a") 832 self.assertEqual(posixpath.relpath(b"a/b"), b"a/b") 833 self.assertEqual(posixpath.relpath(b"../a/b"), b"../a/b") 834 self.assertEqual(posixpath.relpath(b"a", b"../b"), 835 b"../"+curdir+b"/a") 836 self.assertEqual(posixpath.relpath(b"a/b", b"../c"), 837 b"../"+curdir+b"/a/b") 838 self.assertEqual(posixpath.relpath(b"a", b"b/c"), b"../../a") 839 self.assertEqual(posixpath.relpath(b"a", b"a"), b".") 840 self.assertEqual(posixpath.relpath(b"/foo/bar/bat", b"/x/y/z"), b'../../../foo/bar/bat') 841 self.assertEqual(posixpath.relpath(b"/foo/bar/bat", b"/foo/bar"), b'bat') 842 self.assertEqual(posixpath.relpath(b"/foo/bar/bat", b"/"), b'foo/bar/bat') 843 self.assertEqual(posixpath.relpath(b"/", b"/foo/bar/bat"), b'../../..') 844 self.assertEqual(posixpath.relpath(b"/foo/bar/bat", b"/x"), b'../foo/bar/bat') 845 self.assertEqual(posixpath.relpath(b"/x", b"/foo/bar/bat"), b'../../../x') 846 self.assertEqual(posixpath.relpath(b"/", b"/"), b'.') 847 self.assertEqual(posixpath.relpath(b"/a", b"/a"), b'.') 848 self.assertEqual(posixpath.relpath(b"/a/b", b"/a/b"), b'.') 849 850 self.assertRaises(TypeError, posixpath.relpath, b"bytes", "str") 851 self.assertRaises(TypeError, posixpath.relpath, "str", b"bytes") 852 finally: 853 os.getcwdb = real_getcwdb 854 855 def test_commonpath(self): 856 def check(paths, expected): 857 self.assertEqual(posixpath.commonpath(paths), expected) 858 self.assertEqual(posixpath.commonpath([os.fsencode(p) for p in paths]), 859 os.fsencode(expected)) 860 def check_error(exc, paths): 861 self.assertRaises(exc, posixpath.commonpath, paths) 862 self.assertRaises(exc, posixpath.commonpath, 863 [os.fsencode(p) for p in paths]) 864 865 self.assertRaises(ValueError, posixpath.commonpath, []) 866 check_error(ValueError, ['/usr', 'usr']) 867 check_error(ValueError, ['usr', '/usr']) 868 869 check(['/usr/local'], '/usr/local') 870 check(['/usr/local', '/usr/local'], '/usr/local') 871 check(['/usr/local/', '/usr/local'], '/usr/local') 872 check(['/usr/local/', '/usr/local/'], '/usr/local') 873 check(['/usr//local', '//usr/local'], '/usr/local') 874 check(['/usr/./local', '/./usr/local'], '/usr/local') 875 check(['/', '/dev'], '/') 876 check(['/usr', '/dev'], '/') 877 check(['/usr/lib/', '/usr/lib/python3'], '/usr/lib') 878 check(['/usr/lib/', '/usr/lib64/'], '/usr') 879 880 check(['/usr/lib', '/usr/lib64'], '/usr') 881 check(['/usr/lib/', '/usr/lib64'], '/usr') 882 883 check(['spam'], 'spam') 884 check(['spam', 'spam'], 'spam') 885 check(['spam', 'alot'], '') 886 check(['and/jam', 'and/spam'], 'and') 887 check(['and//jam', 'and/spam//'], 'and') 888 check(['and/./jam', './and/spam'], 'and') 889 check(['and/jam', 'and/spam', 'alot'], '') 890 check(['and/jam', 'and/spam', 'and'], 'and') 891 892 check([''], '') 893 check(['', 'spam/alot'], '') 894 check_error(ValueError, ['', '/spam/alot']) 895 896 self.assertRaises(TypeError, posixpath.commonpath, 897 [b'/usr/lib/', '/usr/lib/python3']) 898 self.assertRaises(TypeError, posixpath.commonpath, 899 [b'/usr/lib/', 'usr/lib/python3']) 900 self.assertRaises(TypeError, posixpath.commonpath, 901 [b'usr/lib/', '/usr/lib/python3']) 902 self.assertRaises(TypeError, posixpath.commonpath, 903 ['/usr/lib/', b'/usr/lib/python3']) 904 self.assertRaises(TypeError, posixpath.commonpath, 905 ['/usr/lib/', b'usr/lib/python3']) 906 self.assertRaises(TypeError, posixpath.commonpath, 907 ['usr/lib/', b'/usr/lib/python3']) 908 909 910class PosixCommonTest(test_genericpath.CommonTest, unittest.TestCase): 911 pathmodule = posixpath 912 attributes = ['relpath', 'samefile', 'sameopenfile', 'samestat'] 913 914 915class PathLikeTests(unittest.TestCase): 916 917 path = posixpath 918 919 def setUp(self): 920 self.file_name = os_helper.TESTFN 921 self.file_path = FakePath(os_helper.TESTFN) 922 self.addCleanup(os_helper.unlink, self.file_name) 923 with open(self.file_name, 'xb', 0) as file: 924 file.write(b"test_posixpath.PathLikeTests") 925 926 def assertPathEqual(self, func): 927 self.assertEqual(func(self.file_path), func(self.file_name)) 928 929 def test_path_normcase(self): 930 self.assertPathEqual(self.path.normcase) 931 932 def test_path_isabs(self): 933 self.assertPathEqual(self.path.isabs) 934 935 def test_path_join(self): 936 self.assertEqual(self.path.join('a', FakePath('b'), 'c'), 937 self.path.join('a', 'b', 'c')) 938 939 def test_path_split(self): 940 self.assertPathEqual(self.path.split) 941 942 def test_path_splitext(self): 943 self.assertPathEqual(self.path.splitext) 944 945 def test_path_splitdrive(self): 946 self.assertPathEqual(self.path.splitdrive) 947 948 def test_path_basename(self): 949 self.assertPathEqual(self.path.basename) 950 951 def test_path_dirname(self): 952 self.assertPathEqual(self.path.dirname) 953 954 def test_path_islink(self): 955 self.assertPathEqual(self.path.islink) 956 957 def test_path_lexists(self): 958 self.assertPathEqual(self.path.lexists) 959 960 def test_path_ismount(self): 961 self.assertPathEqual(self.path.ismount) 962 963 def test_path_expanduser(self): 964 self.assertPathEqual(self.path.expanduser) 965 966 def test_path_expandvars(self): 967 self.assertPathEqual(self.path.expandvars) 968 969 def test_path_normpath(self): 970 self.assertPathEqual(self.path.normpath) 971 972 def test_path_abspath(self): 973 self.assertPathEqual(self.path.abspath) 974 975 @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING}) 976 def test_path_realpath(self, kwargs): 977 self.assertPathEqual(self.path.realpath) 978 979 self.assertPathEqual(partial(self.path.realpath, **kwargs)) 980 981 def test_path_relpath(self): 982 self.assertPathEqual(self.path.relpath) 983 984 def test_path_commonpath(self): 985 common_path = self.path.commonpath([self.file_path, self.file_name]) 986 self.assertEqual(common_path, self.file_name) 987 988 989if __name__=="__main__": 990 unittest.main() 991