]> granicus.if.org Git - python/commitdiff
bpo-25803: Avoid incorrect errors raised by Path.mkdir(exist_ok=True)… (#806)
authorSerhiy Storchaka <storchaka@gmail.com>
Fri, 24 Mar 2017 19:46:25 +0000 (21:46 +0200)
committerGitHub <noreply@github.com>
Fri, 24 Mar 2017 19:46:25 +0000 (21:46 +0200)
when the OS gives priority to errors such as EACCES over EEXIST.

(cherry picked from commit af7b9ec5c855366feef4c67dc492d64b3baf84ca)

Lib/pathlib.py
Lib/test/test_pathlib.py
Misc/NEWS

index 9f347216b183a957e6202cf2a09b021bf56ead92..8c1cb96bad3075575f3873fe20c608ce129d9eff 100644 (file)
@@ -1220,25 +1220,23 @@ class Path(PurePath):
         os.close(fd)
 
     def mkdir(self, mode=0o777, parents=False, exist_ok=False):
+        """
+        Create a new directory at this given path.
+        """
         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 or self.parent == self:
-                    raise
-                self.parent.mkdir(parents=True)
-                self._accessor.mkdir(self, mode)
+        try:
+            self._accessor.mkdir(self, mode)
+        except FileNotFoundError:
+            if not parents or self.parent == self:
+                raise
+            self.parent.mkdir(parents=True)
+            self._accessor.mkdir(self, mode)
+        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):
         """
index 88a93e5802c1cbe48dfc0e07da0f52cf992b8b93..3ff9726d4359ec5457d9c2efb7d982bb74d84fdf 100644 (file)
@@ -1776,6 +1776,11 @@ class _BasePathTest(object):
         self.assertTrue(p.exists())
         self.assertEqual(p.stat().st_ctime, st_ctime_first)
 
+    def test_mkdir_exist_ok_root(self):
+        # Issue #25803: A drive root could raise PermissionError on Windows
+        self.cls('/').resolve().mkdir(exist_ok=True)
+        self.cls('/').resolve().mkdir(parents=True, exist_ok=True)
+
     @only_nt    # XXX: not sure how to test this on POSIX
     def test_mkdir_with_unknown_drive(self):
         for d in 'ZYXWVUTSRQPONMLKJIHGFEDCBA':
index 0c7eaea5a8b65c495229348707533e58e8590c4c..9798a3a4490cbed309827616de2ce2b4058bc452 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -27,6 +27,9 @@ Core and Builtins
 Library
 -------
 
+- bpo-25803: Avoid incorrect errors raised by Path.mkdir(exist_ok=True)
+  when the OS gives priority to errors such as EACCES over EEXIST.
+
 - bpo-29861: Release references to tasks, their arguments and their results
   as soon as they are finished in multiprocessing.Pool.