Author: Armin Rigo <[email protected]>
Branch: py3.5
Changeset: r91042:d944ef8c8aa0
Date: 2017-04-12 10:15 +0200
http://bitbucket.org/pypy/pypy/changeset/d944ef8c8aa0/
Log: pathlib.mkdir(): apply the fixes done in CPython > 3.5, and apply on
top of that the fix for CPython issue #29694
diff --git a/lib-python/3/pathlib.py b/lib-python/3/pathlib.py
--- a/lib-python/3/pathlib.py
+++ b/lib-python/3/pathlib.py
@@ -1209,23 +1209,19 @@
def mkdir(self, mode=0o777, parents=False, exist_ok=False):
if self._closed:
self._raise_closed()
- if not parents:
- try:
- self._accessor.mkdir(self, mode)
- except FileExistsError:
- if not exist_ok or not self.is_dir():
- raise
- else:
- try:
- self._accessor.mkdir(self, mode)
- except FileExistsError:
- if not exist_ok or not self.is_dir():
- raise
- except OSError as e:
- if e.errno != ENOENT:
- raise
- self.parent.mkdir(parents=True)
- self._accessor.mkdir(self, mode)
+ # NOTE: version from CPython 3.7 plus fix at issue #29694
+ try:
+ self._accessor.mkdir(self, mode)
+ except FileNotFoundError:
+ if not parents or self.parent == self:
+ raise
+ self.parent.mkdir(parents=True, exist_ok=True)
+ self.mkdir(mode, parents=False, exist_ok=exist_ok)
+ except OSError:
+ # Cannot rely on checking for EEXIST, since the operating system
+ # could give priority to other errors like EACCES or EROFS
+ if not exist_ok or not self.is_dir():
+ raise
def chmod(self, mode):
"""
diff --git a/lib-python/3/test/test_pathlib.py
b/lib-python/3/test/test_pathlib.py
--- a/lib-python/3/test/test_pathlib.py
+++ b/lib-python/3/test/test_pathlib.py
@@ -8,6 +8,7 @@
import stat
import tempfile
import unittest
+from unittest import mock
from test import support
TESTFN = support.TESTFN
@@ -1751,6 +1752,36 @@
p.mkdir(exist_ok=True)
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, cirCPC/dir1/dir2).
+ if pattern.pop():
+ os.mkdir(path, mode) # from another process
+ concurrently_created.add(path)
+ os.mkdir(path, mode) # our real call
+ org_mkdir = pathlib._normal_accessor.mkdir
+
+ 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)
+ got_exception = False
+ except FileExistsError:
+ got_exception = True
+ self.assertEqual(str(p12) in concurrently_created, got_exception)
+ self.assertTrue(p.exists())
+
@with_symlinks
def test_symlink_to(self):
P = self.cls(BASE)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit