diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 7d2ec607348..69dd5c45677 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -1741,8 +1741,11 @@ features: Recursive directory creation function. Like :func:`mkdir`, but makes all intermediate-level directories needed to contain the leaf directory. - The *mode* parameter is passed to :func:`mkdir`; see :ref:`the mkdir() - description ` for how it is interpreted. + The *mode* parameter is passed to :func:`mkdir` for creating the leaf + directory; see :ref:`the mkdir() description ` for how it + is interpreted. To set the file permission bits of any newly-created parent + directories you can set the umask before invoking :func:`makedirs`. The + file permission bits of existing parent directories are not changed. If *exist_ok* is ``False`` (the default), an :exc:`OSError` is raised if the target directory already exists. @@ -1767,6 +1770,10 @@ features: .. versionchanged:: 3.6 Accepts a :term:`path-like object`. + .. versionchanged:: 3.7 + The *mode* argument no longer affects the file permission bits of + newly-created intermediate-level directories. + .. function:: mkfifo(path, mode=0o666, *, dir_fd=None) diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index 882d6a27f7e..b69f452b133 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -247,6 +247,13 @@ Changes in the Python API and module are affected by this change. (Contributed by INADA Naoki and Eugene Toder in :issue:`29463`.) +* The *mode* argument of :func:`os.makedirs` no longer affects the file + permission bits of newly-created intermediate-level directories. + To set their file permission bits you can set the umask before invoking + ``makedirs()``. + (Contributed by Serhiy Storchaka in :issue:`19930`.) + + CPython bytecode changes ------------------------ diff --git a/Lib/os.py b/Lib/os.py index 18ec124b29a..70857c7e785 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -207,7 +207,7 @@ 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, mode, exist_ok) + makedirs(head, exist_ok=exist_ok) except FileExistsError: # Defeats race condition when another thread created the path pass diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 5f302f6d0ee..83932e64a0b 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -1118,6 +1118,18 @@ class MakedirTests(unittest.TestCase): 'dir5', 'dir6') os.makedirs(path) + def test_mode(self): + with support.temp_umask(0o002): + base = support.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(stat.S_IMODE(os.stat(path).st_mode), 0o555) + self.assertEqual(stat.S_IMODE(os.stat(parent).st_mode), 0o775) + def test_exist_ok_existing_directory(self): path = os.path.join(support.TESTFN, 'dir1') mode = 0o777 diff --git a/Misc/NEWS b/Misc/NEWS index 8cc8b9f971f..8003facf122 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -287,6 +287,9 @@ Extension Modules Library ------- +- bpo-19930: The mode argument of os.makedirs() no longer affects the file + permission bits of newly-created intermediate-level directories. + - bpo-29884: faulthandler: Restore the old sigaltstack during teardown. Patch by Christophe Zeitouny.