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