[3.6] bpo-29694: race condition in pathlib mkdir with flags parents=T… · python/cpython@cbc46af · GitHub
Skip to content

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

3 files changed

Lines changed: 36 additions & 2 deletions

File tree

Lib/pathlib.py

Lines changed: 2 additions & 2 deletions

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

0 commit comments

Comments
 (0)