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

Reply via email to