From 613cdb2f2b29f737acb11f4c2347f0d63d42bb6b Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Thu, 4 Jul 2024 11:55:34 +0000 Subject: [PATCH] ignore NotADirectoryError on missing_ok = True Semantically equivalent to FileNotFoundError - the file to be unlinked does not exist --- Doc/library/pathlib.rst | 9 +++++++-- Lib/pathlib/_local.py | 2 +- Lib/test/test_pathlib/test_pathlib.py | 7 +++++++ Lib/test/test_pathlib/test_pathlib_abc.py | 5 +++++ .../2024-07-04-11-55-15.gh-issue-119993._IXsOb.rst | 2 ++ 5 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-07-04-11-55-15.gh-issue-119993._IXsOb.rst diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index f414da597562139..199ddf315006157 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -1627,10 +1627,15 @@ example because the path doesn't exist). If *missing_ok* is false (the default), this method propagates any :exc:`OSError` from the operating system, including :exc:`FileNotFoundError`. - If *missing_ok* is true, :exc:`FileNotFoundError` exceptions will be - ignored (same behavior as the POSIX ``rm -f`` command), any other + If *missing_ok* is true, this shows similar behavior to the POSIX ``rm -f`` + command and any :exc:`FileNotFoundError` or :exc:`NotADirectoryError` + exceptions will be ignored. This means that the file does not exist after + execution, but cannot guarantee that the file did exist before. Any other :exc:`OSError` which is encountered will continue to be propogated. + .. versionchanged:: 3.?? + The *missing_ok* parameter will also ignore :exc:`NotADirectoryError` + .. versionchanged:: 3.8 The *missing_ok* parameter was added. diff --git a/Lib/pathlib/_local.py b/Lib/pathlib/_local.py index 473fd525768b509..515c9166927323f 100644 --- a/Lib/pathlib/_local.py +++ b/Lib/pathlib/_local.py @@ -793,7 +793,7 @@ def unlink(self, missing_ok=False): """ try: os.unlink(self) - except FileNotFoundError: + except (FileNotFoundError, NotADirectoryError): if not missing_ok: raise diff --git a/Lib/test/test_pathlib/test_pathlib.py b/Lib/test/test_pathlib/test_pathlib.py index 3df354eb25a58c4..51f1ac9441f35e0 100644 --- a/Lib/test/test_pathlib/test_pathlib.py +++ b/Lib/test/test_pathlib/test_pathlib.py @@ -775,6 +775,13 @@ def test_unlink_missing_ok(self): self.assertFileNotFound(p.unlink) p.unlink(missing_ok=True) + def test_unlink_missing_ok_intermediate_file(self): + p = self.cls(self.base) / 'fileAAA' + p.touch() + p = p / 'fileBBB' + self.assertNotADirectory(p.unlink) + p.unlink(missing_ok=True) + def test_rmdir(self): p = self.cls(self.base) / 'dirA' for q in p.iterdir(): diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py index 57cc1612c03468a..4abc0425cf86d4f 100644 --- a/Lib/test/test_pathlib/test_pathlib_abc.py +++ b/Lib/test/test_pathlib/test_pathlib_abc.py @@ -1591,6 +1591,11 @@ def assertFileNotFound(self, func, *args, **kwargs): func(*args, **kwargs) self.assertEqual(cm.exception.errno, errno.ENOENT) + def assertNotADirectory(self, func, *args, **kwargs): + with self.assertRaises(NotADirectoryError) as cm: + func(*args, **kwargs) + self.assertEqual(cm.exception.errno, errno.ENOTDIR) + def assertEqualNormCase(self, path_a, path_b): normcase = self.parser.normcase self.assertEqual(normcase(path_a), normcase(path_b)) diff --git a/Misc/NEWS.d/next/Library/2024-07-04-11-55-15.gh-issue-119993._IXsOb.rst b/Misc/NEWS.d/next/Library/2024-07-04-11-55-15.gh-issue-119993._IXsOb.rst new file mode 100644 index 000000000000000..4199422ccabca89 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-07-04-11-55-15.gh-issue-119993._IXsOb.rst @@ -0,0 +1,2 @@ +:meth:`pathlib.Path.unlink` will also ignore any :exc:`NotADirectoryError` +if *missing_ok* is true.