Skip to content

Commit 128ff8b

Browse files
committed
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.
1 parent 46b5c6b commit 128ff8b

File tree

5 files changed

+34
-14
lines changed

5 files changed

+34
-14
lines changed

Doc/library/os.rst

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2055,7 +2055,8 @@ features:
20552055
Accepts a :term:`path-like object`.
20562056

20572057

2058-
.. function:: makedirs(name, mode=0o777, exist_ok=False)
2058+
.. function:: makedirs(name, mode=0o777, exist_ok=False, *, \
2059+
recursive_mode=False)
20592060

20602061
.. index::
20612062
single: directory; creating
@@ -2073,6 +2074,9 @@ features:
20732074
If *exist_ok* is ``False`` (the default), an :exc:`FileExistsError` is
20742075
raised if the target directory already exists.
20752076

2077+
If *recursive_mode* is ``True``, the *mode* argument will affect the file
2078+
permission bits of any newly-created, intermediate-level directories.
2079+
20762080
.. note::
20772081

20782082
:func:`makedirs` will become confused if the path elements to create
@@ -2099,6 +2103,9 @@ features:
20992103
The *mode* argument no longer affects the file permission bits of
21002104
newly-created intermediate-level directories.
21012105

2106+
.. versionadded:: 3.10
2107+
The *recursive_mode* parameter.
2108+
21022109

21032110
.. function:: mkfifo(path, mode=0o666, *, dir_fd=None)
21042111

Doc/whatsnew/3.10.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,9 @@ descriptors without copying between kernel address space and user
259259
address space, where one of the file descriptors must refer to a
260260
pipe. (Contributed by Pablo Galindo in :issue:`41625`.)
261261

262+
The :func:`os.makedirs` function now has a *recursive_mode* parameter.
263+
(Contributed by Zackery Spytz in :issue:`42367`.)
264+
262265
pathlib
263266
-------
264267

Lib/os.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -197,22 +197,28 @@ def _add(str, fn):
197197
# Super directory utilities.
198198
# (Inspired by Eric Raymond; the doc strings are mostly his)
199199

200-
def makedirs(name, mode=0o777, exist_ok=False):
201-
"""makedirs(name [, mode=0o777][, exist_ok=False])
200+
def makedirs(name, mode=0o777, exist_ok=False, *, recursive_mode=False):
201+
"""makedirs(name [, mode=0o777][, exist_ok=False][, recursive_mode=False])
202202
203203
Super-mkdir; create a leaf directory and all intermediate ones. Works like
204204
mkdir, except that any intermediate path segment (not just the rightmost)
205205
will be created if it does not exist. If the target directory already
206206
exists, raise an OSError if exist_ok is False. Otherwise no exception is
207-
raised. This is recursive.
207+
raised. If recursive_mode is True, the mode argument will affect the file
208+
permission bits of any newly-created, intermediate-level directories. This
209+
is recursive.
208210
209211
"""
210212
head, tail = path.split(name)
211213
if not tail:
212214
head, tail = path.split(head)
213215
if head and tail and not path.exists(head):
214216
try:
215-
makedirs(head, exist_ok=exist_ok)
217+
if recursive_mode:
218+
makedirs(head, mode=mode, exist_ok=exist_ok,
219+
recursive_mode=True)
220+
else:
221+
makedirs(head, exist_ok=exist_ok)
216222
except FileExistsError:
217223
# Defeats race condition when another thread created the path
218224
pass

Lib/test/test_os.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1572,16 +1572,19 @@ def test_makedir(self):
15721572
os.makedirs(path)
15731573

15741574
def test_mode(self):
1575+
parent = os.path.join(os_helper.TESTFN, 'dir1')
1576+
path = os.path.join(parent, 'dir2')
15751577
with os_helper.temp_umask(0o002):
1576-
base = os_helper.TESTFN
1577-
parent = os.path.join(base, 'dir1')
1578-
path = os.path.join(parent, 'dir2')
1579-
os.makedirs(path, 0o555)
1580-
self.assertTrue(os.path.exists(path))
1581-
self.assertTrue(os.path.isdir(path))
1582-
if os.name != 'nt':
1583-
self.assertEqual(os.stat(path).st_mode & 0o777, 0o555)
1584-
self.assertEqual(os.stat(parent).st_mode & 0o777, 0o775)
1578+
for mode, recursive_mode, path_mode, parent_mode in \
1579+
(0o555, False, 0o555, 0o775), (0o770, True, 0o770, 0o770):
1580+
os.makedirs(path, mode, recursive_mode=recursive_mode)
1581+
self.assertTrue(os.path.exists(path))
1582+
self.assertTrue(os.path.isdir(path))
1583+
if os.name != 'nt':
1584+
self.assertEqual(os.stat(path).st_mode & 0o777, path_mode)
1585+
self.assertEqual(os.stat(parent).st_mode & 0o777,
1586+
parent_mode)
1587+
shutil.rmtree(parent)
15851588

15861589
def test_exist_ok_existing_directory(self):
15871590
path = os.path.join(os_helper.TESTFN, 'dir1')
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
The :func:`os.makedirs` function now has a *recursive_mode* parameter.

0 commit comments

Comments
 (0)