8000 [3.6] bpo-29694: race condition in pathlib mkdir with flags parents=T… · python/cpython@cbc46af · GitHub
[go: up one dir, main page]

Skip to content
8000

Commit cbc46af

Browse files
authored
[3.6] bpo-29694: race condition in pathlib mkdir with flags parents=True (GH-1089). (GH-1126)
(cherry picked from commit 22a594a)
1 parent 2cdf087 commit cbc46af

File tree

3 files changed

+36
-2
lines changed
  • test
  • Misc
  • 3 files changed

    +36
    -2
    lines changed

    Lib/pathlib.py

    Lines changed: 2 additions & 2 deletions
    Original file line numberDiff line numberDiff line change
    @@ -1230,8 +1230,8 @@ def mkdir(self, mode=0o777, parents=False, exist_ok=False):
    12301230
    except FileNotFoundError:
    12311231
    if not parents or self.parent == self:
    12321232
    raise
    1233-
    self.parent.mkdir(parents=True)
    1234-
    self._accessor.mkdir(self, mode)
    1233+
    self.parent.mkdir(parents=True, exist_ok=True)
    1234+
    self.mkdir(mode, parents=False, exist_ok=exist_ok)
    12351235
    except OSError:
    12361236
    # Cannot rely on checking for EEXIST, since the operating system
    12371237
    # could give priority to other errors like EACCES or EROFS

    Lib/test/test_pathlib.py

    Lines changed: 30 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -8,6 +8,7 @@
    88
    import stat
    99
    import tempfile
    1010
    import unittest
    11+
    from unittest import mock
    1112

    1213
    from test import support
    1314
    android_not_root = support.android_not_root
    @@ -1816,6 +1817,35 @@ def test_mkdir_no_parents_file(self):
    18161817
    p.mkdir(exist_ok=True)
    18171818
    self.assertEqual(cm.exception.errno, errno.EEXIST)
    18181819

    1820+
    def test_mkdir_concurrent_parent_creation(self):
    1821+
    for pattern_num in range(32):
    1822+
    p = self.cls(BASE, 'dirCPC%d' % pattern_num)
    1823+
    self.assertFalse(p.exists())
    1824+
    1825+
    def my_mkdir(path, mode=0o777):
    1826+
    path = str(path)
    1827+
    # Emulate another process that would create the directory
    1828+
    # just before we try to create it ourselves. We do it
    1829+
    # in all possible pattern combinations, assuming that this
    1830+
    # function is called at most 5 times (dirCPC/dir1/dir2,
    1831+
    # dirCPC/dir1, dirCPC, dirCPC/dir1, dirCPC/dir1/dir2).
    1832+
    if pattern.pop():
    1833+
    os.mkdir(path, mode) # from another process
    1834+
    concurrently_created.add(path)
    1835+
    os.mkdir(path, mode) # our real call
    1836+
    1837+
    pattern = [bool(pattern_num & (1 << n)) for n in range(5)]
    1838+
    concurrently_created = set()
    1839+
    p12 = p / 'dir1' / 'dir2'
    1840+
    try:
    1841+
    with mock.patch("pathlib._normal_accessor.mkdir", my_mkdir):
    1842+
    p12.mkdir(parents=True, exist_ok=False)
    1843+
    except FileExistsError:
    1844+
    self.assertIn(str(p12), concurrently_created)
    1845+
    else:
    1846+
    self.assertNotIn(str(p12), concurrently_created)
    1847+
    self.assertTrue(p.exists())
    1848+
    18191849
    @with_symlinks
    18201850
    def test_symlink_to(self):
    18211851
    P = self.cls(BASE)

    Misc/NEWS

    Lines changed: 4 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -31,6 +31,10 @@ Core and Builtins
    3131

    3232
    Library
    3333
    -------
    34+
    35+
    - bpo-29694: Fixed race condition in pathlib mkdir with flags
    36+
    parents=True. Patch by Armin Rigo.
    37+
    3438
    - bpo-29692: Fixed arbitrary unchaining of RuntimeError exceptions in
    3539
    contextlib.contextmanager.
    3640
    Patch by Siddharth Velankar.

    0 commit comments

    Comments
     (0)
    0