@@ -2139,6 +2139,94 @@ def test_mode(self):
21392139 self .assertEqual (os .stat (path ).st_mode & 0o777 , 0o555 )
21402140 self .assertEqual (os .stat (parent ).st_mode & 0o777 , 0o775 )
21412141
2142+ @unittest .skipIf (
2143+ support .is_emscripten or support .is_wasi ,
2144+ "umask is not implemented on Emscripten/WASI."
2145+ )
2146+ @unittest .skipIf (
2147+ sys .platform == "android" ,
2148+ "Android filesystem may not honor requested permissions."
2149+ )
2150+ def test_mode_with_parent_mode (self ):
2151+ # Test the parent_mode parameter
2152+ parent = os .path .join (os_helper .TESTFN , 'dir1' )
2153+ path = os .path .join (parent , 'dir2' )
2154+ with os_helper .temp_umask (0o002 ):
2155+ # Specify mode for both leaf and parent directories
2156+ os .makedirs (path , 0o770 , parent_mode = 0o750 )
2157+ self .assertTrue (os .path .exists (path ))
2158+ self .assertTrue (os .path .isdir (path ))
2159+ if os .name != 'nt' :
2160+ # Leaf directory gets the mode parameter
2161+ self .assertEqual (os .stat (path ).st_mode & 0o777 , 0o770 )
2162+ # Parent directory gets the parent_mode parameter
2163+ self .assertEqual (os .stat (parent ).st_mode & 0o777 , 0o750 )
2164+
2165+ @unittest .skipIf (
2166+ support .is_emscripten or support .is_wasi ,
2167+ "umask is not implemented on Emscripten/WASI."
2168+ )
2169+ @unittest .skipIf (
2170+ sys .platform == "android" ,
2171+ "Android filesystem may not honor requested permissions."
2172+ )
2173+ def test_parent_mode_deep_hierarchy (self ):
2174+ # Test parent_mode with deep directory hierarchy
2175+ base = os .path .join (os_helper .TESTFN , 'dir1' , 'dir2' , 'dir3' )
2176+ with os_helper .temp_umask (0o002 ):
2177+ os .makedirs (base , 0o755 , parent_mode = 0o700 )
2178+ self .assertTrue (os .path .exists (base ))
2179+ if os .name != 'nt' :
2180+ # Check that all parent directories have parent_mode
2181+ level1 = os .path .join (os_helper .TESTFN , 'dir1' )
2182+ level2 = os .path .join (level1 , 'dir2' )
2183+ self .assertEqual (os .stat (level1 ).st_mode & 0o777 , 0o700 )
2184+ self .assertEqual (os .stat (level2 ).st_mode & 0o777 , 0o700 )
2185+ # Leaf directory has the regular mode
2186+ self .assertEqual (os .stat (base ).st_mode & 0o777 , 0o755 )
2187+
2188+ @unittest .skipIf (
2189+ support .is_emscripten or support .is_wasi ,
2190+ "umask is not implemented on Emscripten/WASI."
2191+ )
2192+ @unittest .skipIf (
2193+ sys .platform == "android" ,
2194+ "Android filesystem may not honor requested permissions."
2195+ )
2196+ def test_parent_mode_same_as_mode (self ):
2197+ # Test emulating Python 3.6 behavior by setting parent_mode=mode
2198+ parent = os .path .join (os_helper .TESTFN , 'dir1' )
2199+ path = os .path .join (parent , 'dir2' )
2200+ with os_helper .temp_umask (0o002 ):
2201+ os .makedirs (path , 0o705 , parent_mode = 0o705 )
2202+ self .assertTrue (os .path .exists (path ))
2203+ if os .name != 'nt' :
2204+ # Both directories should have the same mode
2205+ self .assertEqual (os .stat (path ).st_mode & 0o777 , 0o705 )
2206+ self .assertEqual (os .stat (parent ).st_mode & 0o777 , 0o705 )
2207+
2208+ @unittest .skipIf (
2209+ support .is_emscripten or support .is_wasi ,
2210+ "umask is not implemented on Emscripten/WASI."
2211+ )
2212+ @unittest .skipIf (
2213+ sys .platform == "android" ,
2214+ "Android filesystem may not honor requested permissions."
2215+ )
2216+ def test_parent_mode_combined_with_umask (self ):
2217+ # parent_mode, like mode, is combined with the process umask; it does
2218+ # not bypass it.
2219+ parent = os .path .join (os_helper .TESTFN , 'dir1' )
2220+ path = os .path .join (parent , 'dir2' )
2221+ with os_helper .temp_umask (0o022 ):
2222+ os .makedirs (path , 0o777 , parent_mode = 0o777 )
2223+ self .assertTrue (os .path .isdir (path ))
2224+ if os .name != 'nt' :
2225+ # 0o777 is masked down to 0o755 by the 0o022 umask, for both
2226+ # the leaf (mode) and the parent (parent_mode).
2227+ self .assertEqual (os .stat (path ).st_mode & 0o777 , 0o755 )
2228+ self .assertEqual (os .stat (parent ).st_mode & 0o777 , 0o755 )
2229+
21422230 @unittest .skipIf (
21432231 support .is_wasi ,
21442232 "WASI's umask is a stub."
@@ -2212,15 +2300,9 @@ def test_win32_mkdir_700(self):
22122300 )
22132301
22142302 def tearDown (self ):
2215- path = os .path .join (os_helper .TESTFN , 'dir1' , 'dir2' , 'dir3' ,
2216- 'dir4' , 'dir5' , 'dir6' )
2217- # If the tests failed, the bottom-most directory ('../dir6')
2218- # may not have been created, so we look for the outermost directory
2219- # that exists.
2220- while not os .path .exists (path ) and path != os_helper .TESTFN :
2221- path = os .path .dirname (path )
2222-
2223- os .removedirs (path )
2303+ # Remove the whole tree regardless of which sub-directories a test
2304+ # created and regardless of their permission bits.
2305+ os_helper .rmtree (os_helper .TESTFN )
22242306
22252307
22262308@unittest .skipUnless (hasattr (os , "chown" ), "requires os.chown()" )
0 commit comments