From af7b9ec5c855366feef4c67dc492d64b3baf84ca Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 24 Mar 2017 20:51:53 +0200 Subject: [PATCH] bpo-25803: Avoid incorrect errors raised by Path.mkdir(exist_ok=True) (#805) when the OS gives priority to errors such as EACCES over EEXIST. --- Lib/pathlib.py | 32 +++++++++++++++----------------- Lib/test/test_pathlib.py | 5 +++++ Misc/NEWS | 3 +++ 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 9f347216b18..8c1cb96bad3 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -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): """ diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index 88a93e5802c..3ff9726d435 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -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': diff --git a/Misc/NEWS b/Misc/NEWS index bd43227804d..9b0414a8e57 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -287,6 +287,9 @@ Extension Modules 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.