[3.6] bpo-29694: race condition in pathlib mkdir with flags parents=T… · python/cpython@cbc46af (original) (raw)
`@@ -8,6 +8,7 @@
`
8
8
`import stat
`
9
9
`import tempfile
`
10
10
`import unittest
`
``
11
`+
from unittest import mock
`
11
12
``
12
13
`from test import support
`
13
14
`android_not_root = support.android_not_root
`
`@@ -1816,6 +1817,35 @@ def test_mkdir_no_parents_file(self):
`
1816
1817
`p.mkdir(exist_ok=True)
`
1817
1818
`self.assertEqual(cm.exception.errno, errno.EEXIST)
`
1818
1819
``
``
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
+
1819
1849
`@with_symlinks
`
1820
1850
`def test_symlink_to(self):
`
1821
1851
`P = self.cls(BASE)
`