@@ -269,6 +269,17 @@ def test_realpath_basic(self):
269269 self .assertPathEqual (ntpath .realpath (os .fsencode (ABSTFN + "1" )),
270270 os .fsencode (ABSTFN ))
271271
272+ @os_helper .skip_unless_symlink
273+ @unittest .skipUnless (HAVE_GETFINALPATHNAME , 'need _getfinalpathname' )
274+ def test_realpath_strict (self ):
275+ # Bug #43757: raise FileNotFoundError in strict mode if we encounter
276+ # a path that does not exist.
277+ ABSTFN = ntpath .abspath (os_helper .TESTFN )
278+ os .symlink (ABSTFN + "1" , ABSTFN )
279+ self .addCleanup (os_helper .unlink , ABSTFN )
280+ self .assertRaises (FileNotFoundError , ntpath .realpath , ABSTFN , strict = True )
281+ self .assertRaises (FileNotFoundError , ntpath .realpath , ABSTFN + "2" , strict = True )
282+
272283 @os_helper .skip_unless_symlink
273284 @unittest .skipUnless (HAVE_GETFINALPATHNAME , 'need _getfinalpathname' )
274285 def test_realpath_relative (self ):
@@ -343,8 +354,9 @@ def test_realpath_broken_symlinks(self):
343354 @os_helper .skip_unless_symlink
344355 @unittest .skipUnless (HAVE_GETFINALPATHNAME , 'need _getfinalpathname' )
345356 def test_realpath_symlink_loops (self ):
346- # Symlink loops are non-deterministic as to which path is returned, but
347- # it will always be the fully resolved path of one member of the cycle
357+ # Symlink loops in non-strict mode are non-deterministic as to which
358+ # path is returned, but it will always be the fully resolved path of
359+ # one member of the cycle
348360 ABSTFN = ntpath .abspath (os_helper .TESTFN )
349361 self .addCleanup (os_helper .unlink , ABSTFN )
350362 self .addCleanup (os_helper .unlink , ABSTFN + "1" )
@@ -386,6 +398,50 @@ def test_realpath_symlink_loops(self):
386398 # Test using relative path as well.
387399 self .assertPathEqual (ntpath .realpath (ntpath .basename (ABSTFN )), ABSTFN )
388400
401+ @os_helper .skip_unless_symlink
402+ @unittest .skipUnless (HAVE_GETFINALPATHNAME , 'need _getfinalpathname' )
403+ def test_realpath_symlink_loops_strict (self ):
404+ # Symlink loops raise OSError in strict mode
405+ ABSTFN = ntpath .abspath (os_helper .TESTFN )
406+ self .addCleanup (os_helper .unlink , ABSTFN )
407+ self .addCleanup (os_helper .unlink , ABSTFN + "1" )
408+ self .addCleanup (os_helper .unlink , ABSTFN + "2" )
409+ self .addCleanup (os_helper .unlink , ABSTFN + "y" )
410+ self .addCleanup (os_helper .unlink , ABSTFN + "c" )
411+ self .addCleanup (os_helper .unlink , ABSTFN + "a" )
412+
413+ os .symlink (ABSTFN , ABSTFN )
414+ self .assertRaises (OSError , ntpath .realpath , ABSTFN , strict = True )
415+
416+ os .symlink (ABSTFN + "1" , ABSTFN + "2" )
417+ os .symlink (ABSTFN + "2" , ABSTFN + "1" )
418+ self .assertRaises (OSError , ntpath .realpath , ABSTFN + "1" , strict = True )
419+ self .assertRaises (OSError , ntpath .realpath , ABSTFN + "2" , strict = True )
420+ self .assertRaises (OSError , ntpath .realpath , ABSTFN + "1\\ x" , strict = True )
421+ # Windows eliminates '..' components before resolving links, so the
422+ # following call is not expected to raise.
423+ self .assertPathEqual (ntpath .realpath (ABSTFN + "1\\ .." , strict = True ),
424+ ntpath .dirname (ABSTFN ))
425+ self .assertRaises (OSError , ntpath .realpath , ABSTFN + "1\\ ..\\ x" , strict = True )
426+ os .symlink (ABSTFN + "x" , ABSTFN + "y" )
427+ self .assertRaises (OSError , ntpath .realpath , ABSTFN + "1\\ ..\\ "
428+ + ntpath .basename (ABSTFN ) + "y" ,
429+ strict = True )
430+ self .assertRaises (OSError , ntpath .realpath ,
431+ ABSTFN + "1\\ ..\\ " + ntpath .basename (ABSTFN ) + "1" ,
432+ strict = True )
433+
434+ os .symlink (ntpath .basename (ABSTFN ) + "a\\ b" , ABSTFN + "a" )
435+ self .assertRaises (OSError , ntpath .realpath , ABSTFN + "a" , strict = True )
436+
437+ os .symlink ("..\\ " + ntpath .basename (ntpath .dirname (ABSTFN ))
438+ + "\\ " + ntpath .basename (ABSTFN ) + "c" , ABSTFN + "c" )
439+ self .assertRaises (OSError , ntpath .realpath , ABSTFN + "c" , strict = True )
440+
441+ # Test using relative path as well.
442+ self .assertRaises (OSError , ntpath .realpath , ntpath .basename (ABSTFN ),
443+ strict = True )
444+
389445 @os_helper .skip_unless_symlink
390446 @unittest .skipUnless (HAVE_GETFINALPATHNAME , 'need _getfinalpathname' )
391447 def test_realpath_symlink_prefix (self ):
@@ -506,34 +562,47 @@ def test_expanduser(self):
506562 env .clear ()
507563 tester ('ntpath.expanduser("~test")' , '~test' )
508564
509- env ['HOMEPATH' ] = 'eric\\ idle'
510565 env ['HOMEDRIVE' ] = 'C:\\ '
511- tester ('ntpath.expanduser("~test")' , 'C:\\ eric\\ test' )
512- tester ('ntpath.expanduser("~")' , 'C:\\ eric\\ idle' )
566+ env ['HOMEPATH' ] = 'Users\\ eric'
567+ env ['USERNAME' ] = 'eric'
568+ tester ('ntpath.expanduser("~test")' , 'C:\\ Users\\ test' )
569+ tester ('ntpath.expanduser("~")' , 'C:\\ Users\\ eric' )
513570
514571 del env ['HOMEDRIVE' ]
515- tester ('ntpath.expanduser("~test")' , 'eric \\ test' )
516- tester ('ntpath.expanduser("~")' , 'eric \\ idle ' )
572+ tester ('ntpath.expanduser("~test")' , 'Users \\ test' )
573+ tester ('ntpath.expanduser("~")' , 'Users \\ eric ' )
517574
518575 env .clear ()
519- env ['USERPROFILE' ] = 'C:\\ eric\\ idle'
520- tester ('ntpath.expanduser("~test")' , 'C:\\ eric\\ test' )
521- tester ('ntpath.expanduser("~")' , 'C:\\ eric\\ idle' )
576+ env ['USERPROFILE' ] = 'C:\\ Users\\ eric'
577+ env ['USERNAME' ] = 'eric'
578+ tester ('ntpath.expanduser("~test")' , 'C:\\ Users\\ test' )
579+ tester ('ntpath.expanduser("~")' , 'C:\\ Users\\ eric' )
522580 tester ('ntpath.expanduser("~test\\ foo\\ bar")' ,
523- 'C:\\ eric \\ test\\ foo\\ bar' )
581+ 'C:\\ Users \\ test\\ foo\\ bar' )
524582 tester ('ntpath.expanduser("~test/foo/bar")' ,
525- 'C:\\ eric \\ test/foo/bar' )
583+ 'C:\\ Users \\ test/foo/bar' )
526584 tester ('ntpath.expanduser("~\\ foo\\ bar")' ,
527- 'C:\\ eric \\ idle \\ foo\\ bar' )
585+ 'C:\\ Users \\ eric \\ foo\\ bar' )
528586 tester ('ntpath.expanduser("~/foo/bar")' ,
529- 'C:\\ eric \\ idle /foo/bar' )
587+ 'C:\\ Users \\ eric /foo/bar' )
530588
531589 # bpo-36264: ignore `HOME` when set on windows
532590 env .clear ()
533591 env ['HOME' ] = 'F:\\ '
534- env ['USERPROFILE' ] = 'C:\\ eric\\ idle'
535- tester ('ntpath.expanduser("~test")' , 'C:\\ eric\\ test' )
536- tester ('ntpath.expanduser("~")' , 'C:\\ eric\\ idle' )
592+ env ['USERPROFILE' ] = 'C:\\ Users\\ eric'
593+ env ['USERNAME' ] = 'eric'
594+ tester ('ntpath.expanduser("~test")' , 'C:\\ Users\\ test' )
595+ tester ('ntpath.expanduser("~")' , 'C:\\ Users\\ eric' )
596+
597+ # bpo-39899: don't guess another user's home directory if
598+ # `%USERNAME% != basename(%USERPROFILE%)`
599+ env .clear ()
600+ env ['USERPROFILE' ] = 'C:\\ Users\\ eric'
601+ env ['USERNAME' ] = 'idle'
602+ tester ('ntpath.expanduser("~test")' , '~test' )
603+ tester ('ntpath.expanduser("~")' , 'C:\\ Users\\ eric' )
604+
605+
537606
538607 @unittest .skipUnless (nt , "abspath requires 'nt' module" )
539608 def test_abspath (self ):
@@ -728,9 +797,6 @@ class NtCommonTest(test_genericpath.CommonTest, unittest.TestCase):
728797 pathmodule = ntpath
729798 attributes = ['relpath' ]
730799
731- def test_expandvars_nonascii (self ):
732- super ().test_expandvars_nonascii ()
733-
734800 # TODO: RUSTPYTHON
735801 if sys .platform == "linux" :
736802 @unittest .expectedFailure
@@ -767,7 +833,7 @@ class PathLikeTests(NtpathTestCase):
767833 path = ntpath
768834
769835 def setUp (self ):
770- self .file_name = os_helper .TESTFN . lower ()
836+ self .file_name = os_helper .TESTFN
771837 self .file_path = FakePath (os_helper .TESTFN )
772838 self .addCleanup (os_helper .unlink , self .file_name )
773839 with open (self .file_name , 'xb' , 0 ) as file :
@@ -778,6 +844,8 @@ def _check_function(self, func):
778844
779845 def test_path_normcase (self ):
780846 self ._check_function (self .path .normcase )
847+ if sys .platform == 'win32' :
848+ self .assertEqual (ntpath .normcase ('\u03a9 \u2126 ' ), 'ωΩ' )
781849
782850 def test_path_isabs (self ):
783851 self ._check_function (self .path .isabs )
0 commit comments