1""" 2Tests common to genericpath, ntpath and posixpath 3""" 4 5import genericpath 6import os 7import sys 8import unittest 9import warnings 10from test.support import is_emscripten 11from test.support import os_helper 12from test.support import warnings_helper 13from test.support.script_helper import assert_python_ok 14from test.support.os_helper import FakePath 15 16 17def create_file(filename, data=b'foo'): 18 with open(filename, 'xb', 0) as fp: 19 fp.write(data) 20 21 22class GenericTest: 23 common_attributes = ['commonprefix', 'getsize', 'getatime', 'getctime', 24 'getmtime', 'exists', 'isdir', 'isfile'] 25 attributes = [] 26 27 def test_no_argument(self): 28 for attr in self.common_attributes + self.attributes: 29 with self.assertRaises(TypeError): 30 getattr(self.pathmodule, attr)() 31 raise self.fail("{}.{}() did not raise a TypeError" 32 .format(self.pathmodule.__name__, attr)) 33 34 def test_commonprefix(self): 35 commonprefix = self.pathmodule.commonprefix 36 self.assertEqual( 37 commonprefix([]), 38 "" 39 ) 40 self.assertEqual( 41 commonprefix(["/home/swenson/spam", "/home/swen/spam"]), 42 "/home/swen" 43 ) 44 self.assertEqual( 45 commonprefix(["/home/swen/spam", "/home/swen/eggs"]), 46 "/home/swen/" 47 ) 48 self.assertEqual( 49 commonprefix(["/home/swen/spam", "/home/swen/spam"]), 50 "/home/swen/spam" 51 ) 52 self.assertEqual( 53 commonprefix(["home:swenson:spam", "home:swen:spam"]), 54 "home:swen" 55 ) 56 self.assertEqual( 57 commonprefix([":home:swen:spam", ":home:swen:eggs"]), 58 ":home:swen:" 59 ) 60 self.assertEqual( 61 commonprefix([":home:swen:spam", ":home:swen:spam"]), 62 ":home:swen:spam" 63 ) 64 65 self.assertEqual( 66 commonprefix([b"/home/swenson/spam", b"/home/swen/spam"]), 67 b"/home/swen" 68 ) 69 self.assertEqual( 70 commonprefix([b"/home/swen/spam", b"/home/swen/eggs"]), 71 b"/home/swen/" 72 ) 73 self.assertEqual( 74 commonprefix([b"/home/swen/spam", b"/home/swen/spam"]), 75 b"/home/swen/spam" 76 ) 77 self.assertEqual( 78 commonprefix([b"home:swenson:spam", b"home:swen:spam"]), 79 b"home:swen" 80 ) 81 self.assertEqual( 82 commonprefix([b":home:swen:spam", b":home:swen:eggs"]), 83 b":home:swen:" 84 ) 85 self.assertEqual( 86 commonprefix([b":home:swen:spam", b":home:swen:spam"]), 87 b":home:swen:spam" 88 ) 89 90 testlist = ['', 'abc', 'Xbcd', 'Xb', 'XY', 'abcd', 91 'aXc', 'abd', 'ab', 'aX', 'abcX'] 92 for s1 in testlist: 93 for s2 in testlist: 94 p = commonprefix([s1, s2]) 95 self.assertTrue(s1.startswith(p)) 96 self.assertTrue(s2.startswith(p)) 97 if s1 != s2: 98 n = len(p) 99 self.assertNotEqual(s1[n:n+1], s2[n:n+1]) 100 101 def test_getsize(self): 102 filename = os_helper.TESTFN 103 self.addCleanup(os_helper.unlink, filename) 104 105 create_file(filename, b'Hello') 106 self.assertEqual(self.pathmodule.getsize(filename), 5) 107 os.remove(filename) 108 109 create_file(filename, b'Hello World!') 110 self.assertEqual(self.pathmodule.getsize(filename), 12) 111 112 def test_filetime(self): 113 filename = os_helper.TESTFN 114 self.addCleanup(os_helper.unlink, filename) 115 116 create_file(filename, b'foo') 117 118 with open(filename, "ab", 0) as f: 119 f.write(b"bar") 120 121 with open(filename, "rb", 0) as f: 122 data = f.read() 123 self.assertEqual(data, b"foobar") 124 125 self.assertLessEqual( 126 self.pathmodule.getctime(filename), 127 self.pathmodule.getmtime(filename) 128 ) 129 130 def test_exists(self): 131 filename = os_helper.TESTFN 132 bfilename = os.fsencode(filename) 133 self.addCleanup(os_helper.unlink, filename) 134 135 self.assertIs(self.pathmodule.exists(filename), False) 136 self.assertIs(self.pathmodule.exists(bfilename), False) 137 138 create_file(filename) 139 140 self.assertIs(self.pathmodule.exists(filename), True) 141 self.assertIs(self.pathmodule.exists(bfilename), True) 142 143 self.assertIs(self.pathmodule.exists(filename + '\udfff'), False) 144 self.assertIs(self.pathmodule.exists(bfilename + b'\xff'), False) 145 self.assertIs(self.pathmodule.exists(filename + '\x00'), False) 146 self.assertIs(self.pathmodule.exists(bfilename + b'\x00'), False) 147 148 if self.pathmodule is not genericpath: 149 self.assertIs(self.pathmodule.lexists(filename), True) 150 self.assertIs(self.pathmodule.lexists(bfilename), True) 151 152 self.assertIs(self.pathmodule.lexists(filename + '\udfff'), False) 153 self.assertIs(self.pathmodule.lexists(bfilename + b'\xff'), False) 154 self.assertIs(self.pathmodule.lexists(filename + '\x00'), False) 155 self.assertIs(self.pathmodule.lexists(bfilename + b'\x00'), False) 156 157 @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") 158 @unittest.skipIf(is_emscripten, "Emscripten pipe fds have no stat") 159 def test_exists_fd(self): 160 r, w = os.pipe() 161 try: 162 self.assertTrue(self.pathmodule.exists(r)) 163 finally: 164 os.close(r) 165 os.close(w) 166 self.assertFalse(self.pathmodule.exists(r)) 167 168 def test_isdir(self): 169 filename = os_helper.TESTFN 170 bfilename = os.fsencode(filename) 171 self.assertIs(self.pathmodule.isdir(filename), False) 172 self.assertIs(self.pathmodule.isdir(bfilename), False) 173 174 self.assertIs(self.pathmodule.isdir(filename + '\udfff'), False) 175 self.assertIs(self.pathmodule.isdir(bfilename + b'\xff'), False) 176 self.assertIs(self.pathmodule.isdir(filename + '\x00'), False) 177 self.assertIs(self.pathmodule.isdir(bfilename + b'\x00'), False) 178 179 try: 180 create_file(filename) 181 self.assertIs(self.pathmodule.isdir(filename), False) 182 self.assertIs(self.pathmodule.isdir(bfilename), False) 183 finally: 184 os_helper.unlink(filename) 185 186 try: 187 os.mkdir(filename) 188 self.assertIs(self.pathmodule.isdir(filename), True) 189 self.assertIs(self.pathmodule.isdir(bfilename), True) 190 finally: 191 os_helper.rmdir(filename) 192 193 def test_isfile(self): 194 filename = os_helper.TESTFN 195 bfilename = os.fsencode(filename) 196 self.assertIs(self.pathmodule.isfile(filename), False) 197 self.assertIs(self.pathmodule.isfile(bfilename), False) 198 199 self.assertIs(self.pathmodule.isfile(filename + '\udfff'), False) 200 self.assertIs(self.pathmodule.isfile(bfilename + b'\xff'), False) 201 self.assertIs(self.pathmodule.isfile(filename + '\x00'), False) 202 self.assertIs(self.pathmodule.isfile(bfilename + b'\x00'), False) 203 204 try: 205 create_file(filename) 206 self.assertIs(self.pathmodule.isfile(filename), True) 207 self.assertIs(self.pathmodule.isfile(bfilename), True) 208 finally: 209 os_helper.unlink(filename) 210 211 try: 212 os.mkdir(filename) 213 self.assertIs(self.pathmodule.isfile(filename), False) 214 self.assertIs(self.pathmodule.isfile(bfilename), False) 215 finally: 216 os_helper.rmdir(filename) 217 218 def test_samefile(self): 219 file1 = os_helper.TESTFN 220 file2 = os_helper.TESTFN + "2" 221 self.addCleanup(os_helper.unlink, file1) 222 self.addCleanup(os_helper.unlink, file2) 223 224 create_file(file1) 225 self.assertTrue(self.pathmodule.samefile(file1, file1)) 226 227 create_file(file2) 228 self.assertFalse(self.pathmodule.samefile(file1, file2)) 229 230 self.assertRaises(TypeError, self.pathmodule.samefile) 231 232 def _test_samefile_on_link_func(self, func): 233 test_fn1 = os_helper.TESTFN 234 test_fn2 = os_helper.TESTFN + "2" 235 self.addCleanup(os_helper.unlink, test_fn1) 236 self.addCleanup(os_helper.unlink, test_fn2) 237 238 create_file(test_fn1) 239 240 func(test_fn1, test_fn2) 241 self.assertTrue(self.pathmodule.samefile(test_fn1, test_fn2)) 242 os.remove(test_fn2) 243 244 create_file(test_fn2) 245 self.assertFalse(self.pathmodule.samefile(test_fn1, test_fn2)) 246 247 @os_helper.skip_unless_symlink 248 def test_samefile_on_symlink(self): 249 self._test_samefile_on_link_func(os.symlink) 250 251 @unittest.skipUnless(hasattr(os, 'link'), 'requires os.link') 252 def test_samefile_on_link(self): 253 try: 254 self._test_samefile_on_link_func(os.link) 255 except PermissionError as e: 256 self.skipTest('os.link(): %s' % e) 257 258 def test_samestat(self): 259 test_fn1 = os_helper.TESTFN 260 test_fn2 = os_helper.TESTFN + "2" 261 self.addCleanup(os_helper.unlink, test_fn1) 262 self.addCleanup(os_helper.unlink, test_fn2) 263 264 create_file(test_fn1) 265 stat1 = os.stat(test_fn1) 266 self.assertTrue(self.pathmodule.samestat(stat1, os.stat(test_fn1))) 267 268 create_file(test_fn2) 269 stat2 = os.stat(test_fn2) 270 self.assertFalse(self.pathmodule.samestat(stat1, stat2)) 271 272 self.assertRaises(TypeError, self.pathmodule.samestat) 273 274 def _test_samestat_on_link_func(self, func): 275 test_fn1 = os_helper.TESTFN + "1" 276 test_fn2 = os_helper.TESTFN + "2" 277 self.addCleanup(os_helper.unlink, test_fn1) 278 self.addCleanup(os_helper.unlink, test_fn2) 279 280 create_file(test_fn1) 281 func(test_fn1, test_fn2) 282 self.assertTrue(self.pathmodule.samestat(os.stat(test_fn1), 283 os.stat(test_fn2))) 284 os.remove(test_fn2) 285 286 create_file(test_fn2) 287 self.assertFalse(self.pathmodule.samestat(os.stat(test_fn1), 288 os.stat(test_fn2))) 289 290 @os_helper.skip_unless_symlink 291 def test_samestat_on_symlink(self): 292 self._test_samestat_on_link_func(os.symlink) 293 294 @unittest.skipUnless(hasattr(os, 'link'), 'requires os.link') 295 def test_samestat_on_link(self): 296 try: 297 self._test_samestat_on_link_func(os.link) 298 except PermissionError as e: 299 self.skipTest('os.link(): %s' % e) 300 301 def test_sameopenfile(self): 302 filename = os_helper.TESTFN 303 self.addCleanup(os_helper.unlink, filename) 304 create_file(filename) 305 306 with open(filename, "rb", 0) as fp1: 307 fd1 = fp1.fileno() 308 with open(filename, "rb", 0) as fp2: 309 fd2 = fp2.fileno() 310 self.assertTrue(self.pathmodule.sameopenfile(fd1, fd2)) 311 312 313class TestGenericTest(GenericTest, unittest.TestCase): 314 # Issue 16852: GenericTest can't inherit from unittest.TestCase 315 # for test discovery purposes; CommonTest inherits from GenericTest 316 # and is only meant to be inherited by others. 317 pathmodule = genericpath 318 319 def test_invalid_paths(self): 320 for attr in GenericTest.common_attributes: 321 # os.path.commonprefix doesn't raise ValueError 322 if attr == 'commonprefix': 323 continue 324 func = getattr(self.pathmodule, attr) 325 with self.subTest(attr=attr): 326 if attr in ('exists', 'isdir', 'isfile'): 327 func('/tmp\udfffabcds') 328 func(b'/tmp\xffabcds') 329 func('/tmp\x00abcds') 330 func(b'/tmp\x00abcds') 331 else: 332 with self.assertRaises((OSError, UnicodeEncodeError)): 333 func('/tmp\udfffabcds') 334 with self.assertRaises((OSError, UnicodeDecodeError)): 335 func(b'/tmp\xffabcds') 336 with self.assertRaisesRegex(ValueError, 'embedded null'): 337 func('/tmp\x00abcds') 338 with self.assertRaisesRegex(ValueError, 'embedded null'): 339 func(b'/tmp\x00abcds') 340 341# Following TestCase is not supposed to be run from test_genericpath. 342# It is inherited by other test modules (ntpath, posixpath). 343 344class CommonTest(GenericTest): 345 common_attributes = GenericTest.common_attributes + [ 346 # Properties 347 'curdir', 'pardir', 'extsep', 'sep', 348 'pathsep', 'defpath', 'altsep', 'devnull', 349 # Methods 350 'normcase', 'splitdrive', 'expandvars', 'normpath', 'abspath', 351 'join', 'split', 'splitext', 'isabs', 'basename', 'dirname', 352 'lexists', 'islink', 'ismount', 'expanduser', 'normpath', 'realpath', 353 ] 354 355 def test_normcase(self): 356 normcase = self.pathmodule.normcase 357 # check that normcase() is idempotent 358 for p in ["FoO/./BaR", b"FoO/./BaR"]: 359 p = normcase(p) 360 self.assertEqual(p, normcase(p)) 361 362 self.assertEqual(normcase(''), '') 363 self.assertEqual(normcase(b''), b'') 364 365 # check that normcase raises a TypeError for invalid types 366 for path in (None, True, 0, 2.5, [], bytearray(b''), {'o','o'}): 367 self.assertRaises(TypeError, normcase, path) 368 369 def test_splitdrive(self): 370 # splitdrive for non-NT paths 371 splitdrive = self.pathmodule.splitdrive 372 self.assertEqual(splitdrive("/foo/bar"), ("", "/foo/bar")) 373 self.assertEqual(splitdrive("foo:bar"), ("", "foo:bar")) 374 self.assertEqual(splitdrive(":foo:bar"), ("", ":foo:bar")) 375 376 self.assertEqual(splitdrive(b"/foo/bar"), (b"", b"/foo/bar")) 377 self.assertEqual(splitdrive(b"foo:bar"), (b"", b"foo:bar")) 378 self.assertEqual(splitdrive(b":foo:bar"), (b"", b":foo:bar")) 379 380 def test_expandvars(self): 381 expandvars = self.pathmodule.expandvars 382 with os_helper.EnvironmentVarGuard() as env: 383 env.clear() 384 env["foo"] = "bar" 385 env["{foo"] = "baz1" 386 env["{foo}"] = "baz2" 387 self.assertEqual(expandvars("foo"), "foo") 388 self.assertEqual(expandvars("$foo bar"), "bar bar") 389 self.assertEqual(expandvars("${foo}bar"), "barbar") 390 self.assertEqual(expandvars("$[foo]bar"), "$[foo]bar") 391 self.assertEqual(expandvars("$bar bar"), "$bar bar") 392 self.assertEqual(expandvars("$?bar"), "$?bar") 393 self.assertEqual(expandvars("$foo}bar"), "bar}bar") 394 self.assertEqual(expandvars("${foo"), "${foo") 395 self.assertEqual(expandvars("${{foo}}"), "baz1}") 396 self.assertEqual(expandvars("$foo$foo"), "barbar") 397 self.assertEqual(expandvars("$bar$bar"), "$bar$bar") 398 399 self.assertEqual(expandvars(b"foo"), b"foo") 400 self.assertEqual(expandvars(b"$foo bar"), b"bar bar") 401 self.assertEqual(expandvars(b"${foo}bar"), b"barbar") 402 self.assertEqual(expandvars(b"$[foo]bar"), b"$[foo]bar") 403 self.assertEqual(expandvars(b"$bar bar"), b"$bar bar") 404 self.assertEqual(expandvars(b"$?bar"), b"$?bar") 405 self.assertEqual(expandvars(b"$foo}bar"), b"bar}bar") 406 self.assertEqual(expandvars(b"${foo"), b"${foo") 407 self.assertEqual(expandvars(b"${{foo}}"), b"baz1}") 408 self.assertEqual(expandvars(b"$foo$foo"), b"barbar") 409 self.assertEqual(expandvars(b"$bar$bar"), b"$bar$bar") 410 411 @unittest.skipUnless(os_helper.FS_NONASCII, 'need os_helper.FS_NONASCII') 412 def test_expandvars_nonascii(self): 413 expandvars = self.pathmodule.expandvars 414 def check(value, expected): 415 self.assertEqual(expandvars(value), expected) 416 with os_helper.EnvironmentVarGuard() as env: 417 env.clear() 418 nonascii = os_helper.FS_NONASCII 419 env['spam'] = nonascii 420 env[nonascii] = 'ham' + nonascii 421 check(nonascii, nonascii) 422 check('$spam bar', '%s bar' % nonascii) 423 check('${spam}bar', '%sbar' % nonascii) 424 check('${%s}bar' % nonascii, 'ham%sbar' % nonascii) 425 check('$bar%s bar' % nonascii, '$bar%s bar' % nonascii) 426 check('$spam}bar', '%s}bar' % nonascii) 427 428 check(os.fsencode(nonascii), os.fsencode(nonascii)) 429 check(b'$spam bar', os.fsencode('%s bar' % nonascii)) 430 check(b'${spam}bar', os.fsencode('%sbar' % nonascii)) 431 check(os.fsencode('${%s}bar' % nonascii), 432 os.fsencode('ham%sbar' % nonascii)) 433 check(os.fsencode('$bar%s bar' % nonascii), 434 os.fsencode('$bar%s bar' % nonascii)) 435 check(b'$spam}bar', os.fsencode('%s}bar' % nonascii)) 436 437 def test_abspath(self): 438 self.assertIn("foo", self.pathmodule.abspath("foo")) 439 with warnings.catch_warnings(): 440 warnings.simplefilter("ignore", DeprecationWarning) 441 self.assertIn(b"foo", self.pathmodule.abspath(b"foo")) 442 443 # avoid UnicodeDecodeError on Windows 444 undecodable_path = b'' if sys.platform == 'win32' else b'f\xf2\xf2' 445 446 # Abspath returns bytes when the arg is bytes 447 with warnings.catch_warnings(): 448 warnings.simplefilter("ignore", DeprecationWarning) 449 for path in (b'', b'foo', undecodable_path, b'/foo', b'C:\\'): 450 self.assertIsInstance(self.pathmodule.abspath(path), bytes) 451 452 def test_realpath(self): 453 self.assertIn("foo", self.pathmodule.realpath("foo")) 454 with warnings.catch_warnings(): 455 warnings.simplefilter("ignore", DeprecationWarning) 456 self.assertIn(b"foo", self.pathmodule.realpath(b"foo")) 457 458 def test_normpath_issue5827(self): 459 # Make sure normpath preserves unicode 460 for path in ('', '.', '/', '\\', '///foo/.//bar//'): 461 self.assertIsInstance(self.pathmodule.normpath(path), str) 462 463 def test_abspath_issue3426(self): 464 # Check that abspath returns unicode when the arg is unicode 465 # with both ASCII and non-ASCII cwds. 466 abspath = self.pathmodule.abspath 467 for path in ('', 'fuu', 'f\xf9\xf9', '/fuu', 'U:\\'): 468 self.assertIsInstance(abspath(path), str) 469 470 unicwd = '\xe7w\xf0' 471 try: 472 os.fsencode(unicwd) 473 except (AttributeError, UnicodeEncodeError): 474 # FS encoding is probably ASCII 475 pass 476 else: 477 with os_helper.temp_cwd(unicwd): 478 for path in ('', 'fuu', 'f\xf9\xf9', '/fuu', 'U:\\'): 479 self.assertIsInstance(abspath(path), str) 480 481 def test_nonascii_abspath(self): 482 if (os_helper.TESTFN_UNDECODABLE 483 # macOS and Emscripten deny the creation of a directory with an 484 # invalid UTF-8 name. Windows allows creating a directory with an 485 # arbitrary bytes name, but fails to enter this directory 486 # (when the bytes name is used). 487 and sys.platform not in ('win32', 'darwin', 'emscripten', 'wasi')): 488 name = os_helper.TESTFN_UNDECODABLE 489 elif os_helper.TESTFN_NONASCII: 490 name = os_helper.TESTFN_NONASCII 491 else: 492 self.skipTest("need os_helper.TESTFN_NONASCII") 493 494 with warnings.catch_warnings(): 495 warnings.simplefilter("ignore", DeprecationWarning) 496 with os_helper.temp_cwd(name): 497 self.test_abspath() 498 499 def test_join_errors(self): 500 # Check join() raises friendly TypeErrors. 501 with warnings_helper.check_warnings(('', BytesWarning), quiet=True): 502 errmsg = "Can't mix strings and bytes in path components" 503 with self.assertRaisesRegex(TypeError, errmsg): 504 self.pathmodule.join(b'bytes', 'str') 505 with self.assertRaisesRegex(TypeError, errmsg): 506 self.pathmodule.join('str', b'bytes') 507 # regression, see #15377 508 with self.assertRaisesRegex(TypeError, 'int'): 509 self.pathmodule.join(42, 'str') 510 with self.assertRaisesRegex(TypeError, 'int'): 511 self.pathmodule.join('str', 42) 512 with self.assertRaisesRegex(TypeError, 'int'): 513 self.pathmodule.join(42) 514 with self.assertRaisesRegex(TypeError, 'list'): 515 self.pathmodule.join([]) 516 with self.assertRaisesRegex(TypeError, 'bytearray'): 517 self.pathmodule.join(bytearray(b'foo'), bytearray(b'bar')) 518 519 def test_relpath_errors(self): 520 # Check relpath() raises friendly TypeErrors. 521 with warnings_helper.check_warnings( 522 ('', (BytesWarning, DeprecationWarning)), quiet=True): 523 errmsg = "Can't mix strings and bytes in path components" 524 with self.assertRaisesRegex(TypeError, errmsg): 525 self.pathmodule.relpath(b'bytes', 'str') 526 with self.assertRaisesRegex(TypeError, errmsg): 527 self.pathmodule.relpath('str', b'bytes') 528 with self.assertRaisesRegex(TypeError, 'int'): 529 self.pathmodule.relpath(42, 'str') 530 with self.assertRaisesRegex(TypeError, 'int'): 531 self.pathmodule.relpath('str', 42) 532 with self.assertRaisesRegex(TypeError, 'bytearray'): 533 self.pathmodule.relpath(bytearray(b'foo'), bytearray(b'bar')) 534 535 def test_import(self): 536 assert_python_ok('-S', '-c', 'import ' + self.pathmodule.__name__) 537 538 539class PathLikeTests(unittest.TestCase): 540 541 def setUp(self): 542 self.file_name = os_helper.TESTFN 543 self.file_path = FakePath(os_helper.TESTFN) 544 self.addCleanup(os_helper.unlink, self.file_name) 545 create_file(self.file_name, b"test_genericpath.PathLikeTests") 546 547 def assertPathEqual(self, func): 548 self.assertEqual(func(self.file_path), func(self.file_name)) 549 550 def test_path_exists(self): 551 self.assertPathEqual(os.path.exists) 552 553 def test_path_isfile(self): 554 self.assertPathEqual(os.path.isfile) 555 556 def test_path_isdir(self): 557 self.assertPathEqual(os.path.isdir) 558 559 def test_path_commonprefix(self): 560 self.assertEqual(os.path.commonprefix([self.file_path, self.file_name]), 561 self.file_name) 562 563 def test_path_getsize(self): 564 self.assertPathEqual(os.path.getsize) 565 566 def test_path_getmtime(self): 567 self.assertPathEqual(os.path.getatime) 568 569 def test_path_getctime(self): 570 self.assertPathEqual(os.path.getctime) 571 572 def test_path_samefile(self): 573 self.assertTrue(os.path.samefile(self.file_path, self.file_name)) 574 575 576if __name__ == "__main__": 577 unittest.main() 578