mirror of https://github.com/python/cpython.git
bpo-29694: race condition in pathlib mkdir with flags parents=True (GH-1089)
This commit is contained in:
parent
5908300e4b
commit
22a594a004
|
@ -1217,8 +1217,8 @@ def mkdir(self, mode=0o777, parents=False, exist_ok=False):
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
if not parents or self.parent == self:
|
if not parents or self.parent == self:
|
||||||
raise
|
raise
|
||||||
self.parent.mkdir(parents=True)
|
self.parent.mkdir(parents=True, exist_ok=True)
|
||||||
self._accessor.mkdir(self, mode)
|
self.mkdir(mode, parents=False, exist_ok=exist_ok)
|
||||||
except OSError:
|
except OSError:
|
||||||
# Cannot rely on checking for EEXIST, since the operating system
|
# Cannot rely on checking for EEXIST, since the operating system
|
||||||
# could give priority to other errors like EACCES or EROFS
|
# could give priority to other errors like EACCES or EROFS
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
import stat
|
import stat
|
||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
from test import support
|
from test import support
|
||||||
android_not_root = support.android_not_root
|
android_not_root = support.android_not_root
|
||||||
|
@ -1801,6 +1802,35 @@ def test_mkdir_no_parents_file(self):
|
||||||
p.mkdir(exist_ok=True)
|
p.mkdir(exist_ok=True)
|
||||||
self.assertEqual(cm.exception.errno, errno.EEXIST)
|
self.assertEqual(cm.exception.errno, errno.EEXIST)
|
||||||
|
|
||||||
|
def test_mkdir_concurrent_parent_creation(self):
|
||||||
|
for pattern_num in range(32):
|
||||||
|
p = self.cls(BASE, 'dirCPC%d' % pattern_num)
|
||||||
|
self.assertFalse(p.exists())
|
||||||
|
|
||||||
|
def my_mkdir(path, mode=0o777):
|
||||||
|
path = str(path)
|
||||||
|
# Emulate another process that would create the directory
|
||||||
|
# just before we try to create it ourselves. We do it
|
||||||
|
# in all possible pattern combinations, assuming that this
|
||||||
|
# function is called at most 5 times (dirCPC/dir1/dir2,
|
||||||
|
# dirCPC/dir1, dirCPC, dirCPC/dir1, dirCPC/dir1/dir2).
|
||||||
|
if pattern.pop():
|
||||||
|
os.mkdir(path, mode) # from another process
|
||||||
|
concurrently_created.add(path)
|
||||||
|
os.mkdir(path, mode) # our real call
|
||||||
|
|
||||||
|
pattern = [bool(pattern_num & (1 << n)) for n in range(5)]
|
||||||
|
concurrently_created = set()
|
||||||
|
p12 = p / 'dir1' / 'dir2'
|
||||||
|
try:
|
||||||
|
with mock.patch("pathlib._normal_accessor.mkdir", my_mkdir):
|
||||||
|
p12.mkdir(parents=True, exist_ok=False)
|
||||||
|
except FileExistsError:
|
||||||
|
self.assertIn(str(p12), concurrently_created)
|
||||||
|
else:
|
||||||
|
self.assertNotIn(str(p12), concurrently_created)
|
||||||
|
self.assertTrue(p.exists())
|
||||||
|
|
||||||
@support.skip_unless_symlink
|
@support.skip_unless_symlink
|
||||||
def test_symlink_to(self):
|
def test_symlink_to(self):
|
||||||
P = self.cls(BASE)
|
P = self.cls(BASE)
|
||||||
|
|
|
@ -306,6 +306,10 @@ Extension Modules
|
||||||
|
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- bpo-29694: Fixed race condition in pathlib mkdir with flags
|
||||||
|
parents=True. Patch by Armin Rigo.
|
||||||
|
|
||||||
- bpo-29692: Fixed arbitrary unchaining of RuntimeError exceptions in
|
- bpo-29692: Fixed arbitrary unchaining of RuntimeError exceptions in
|
||||||
contextlib.contextmanager.
|
contextlib.contextmanager.
|
||||||
Patch by Siddharth Velankar.
|
Patch by Siddharth Velankar.
|
||||||
|
|
Loading…
Reference in New Issue