bpo-42367: Restore os.makedirs() ability to apply *mode* recursively

Allow os.makedirs() to apply the *mode* argument to any intermediate
directories that are created.
This commit is contained in:
Zackery Spytz 2020-12-22 16:56:30 -07:00
parent 46b5c6be29
commit 128ff8b466
5 changed files with 34 additions and 14 deletions

View File

@ -2055,7 +2055,8 @@ features:
Accepts a :term:`path-like object`.
.. function:: makedirs(name, mode=0o777, exist_ok=False)
.. function:: makedirs(name, mode=0o777, exist_ok=False, *, \
recursive_mode=False)
.. index::
single: directory; creating
@ -2073,6 +2074,9 @@ features:
If *exist_ok* is ``False`` (the default), an :exc:`FileExistsError` is
raised if the target directory already exists.
If *recursive_mode* is ``True``, the *mode* argument will affect the file
permission bits of any newly-created, intermediate-level directories.
.. note::
:func:`makedirs` will become confused if the path elements to create
@ -2099,6 +2103,9 @@ features:
The *mode* argument no longer affects the file permission bits of
newly-created intermediate-level directories.
.. versionadded:: 3.10
The *recursive_mode* parameter.
.. function:: mkfifo(path, mode=0o666, *, dir_fd=None)

View File

@ -259,6 +259,9 @@ descriptors without copying between kernel address space and user
address space, where one of the file descriptors must refer to a
pipe. (Contributed by Pablo Galindo in :issue:`41625`.)
The :func:`os.makedirs` function now has a *recursive_mode* parameter.
(Contributed by Zackery Spytz in :issue:`42367`.)
pathlib
-------

View File

@ -197,14 +197,16 @@ SEEK_END = 2
# Super directory utilities.
# (Inspired by Eric Raymond; the doc strings are mostly his)
def makedirs(name, mode=0o777, exist_ok=False):
"""makedirs(name [, mode=0o777][, exist_ok=False])
def makedirs(name, mode=0o777, exist_ok=False, *, recursive_mode=False):
"""makedirs(name [, mode=0o777][, exist_ok=False][, recursive_mode=False])
Super-mkdir; create a leaf directory and all intermediate ones. Works like
mkdir, except that any intermediate path segment (not just the rightmost)
will be created if it does not exist. If the target directory already
exists, raise an OSError if exist_ok is False. Otherwise no exception is
raised. This is recursive.
raised. If recursive_mode is True, the mode argument will affect the file
permission bits of any newly-created, intermediate-level directories. This
is recursive.
"""
head, tail = path.split(name)
@ -212,7 +214,11 @@ def makedirs(name, mode=0o777, exist_ok=False):
head, tail = path.split(head)
if head and tail and not path.exists(head):
try:
makedirs(head, exist_ok=exist_ok)
if recursive_mode:
makedirs(head, mode=mode, exist_ok=exist_ok,
recursive_mode=True)
else:
makedirs(head, exist_ok=exist_ok)
except FileExistsError:
# Defeats race condition when another thread created the path
pass

View File

@ -1572,16 +1572,19 @@ class MakedirTests(unittest.TestCase):
os.makedirs(path)
def test_mode(self):
parent = os.path.join(os_helper.TESTFN, 'dir1')
path = os.path.join(parent, 'dir2')
with os_helper.temp_umask(0o002):
base = os_helper.TESTFN
parent = os.path.join(base, 'dir1')
path = os.path.join(parent, 'dir2')
os.makedirs(path, 0o555)
self.assertTrue(os.path.exists(path))
self.assertTrue(os.path.isdir(path))
if os.name != 'nt':
self.assertEqual(os.stat(path).st_mode & 0o777, 0o555)
self.assertEqual(os.stat(parent).st_mode & 0o777, 0o775)
for mode, recursive_mode, path_mode, parent_mode in \
(0o555, False, 0o555, 0o775), (0o770, True, 0o770, 0o770):
os.makedirs(path, mode, recursive_mode=recursive_mode)
self.assertTrue(os.path.exists(path))
self.assertTrue(os.path.isdir(path))
if os.name != 'nt':
self.assertEqual(os.stat(path).st_mode & 0o777, path_mode)
self.assertEqual(os.stat(parent).st_mode & 0o777,
parent_mode)
shutil.rmtree(parent)
def test_exist_ok_existing_directory(self):
path = os.path.join(os_helper.TESTFN, 'dir1')

View File

@ -0,0 +1 @@
The :func:`os.makedirs` function now has a *recursive_mode* parameter.