]> granicus.if.org Git - python/commitdiff
bpo-33635: Handling Bad file descriptor in Path.is_file and related. (GH-8542)
authorPrzemysław Spodymek <przemyslaw@spodymek.com>
Mon, 27 Aug 2018 21:33:45 +0000 (23:33 +0200)
committerSteve Dower <steve.dower@microsoft.com>
Mon, 27 Aug 2018 21:33:45 +0000 (14:33 -0700)
Lib/pathlib.py
Lib/test/test_pathlib.py
Misc/NEWS.d/next/macOS/2018-07-31-09-51-01.bpo-33635.KiscE-.rst [new file with mode: 0644]

index 6be372ed320a2c4e1c2d98875260822304ba2623..c2986bd022d09457fdd3ef5e9badcc6376890f62 100644 (file)
@@ -7,7 +7,7 @@ import posixpath
 import re
 import sys
 from _collections_abc import Sequence
-from errno import EINVAL, ENOENT, ENOTDIR
+from errno import EINVAL, ENOENT, ENOTDIR, EBADF
 from operator import attrgetter
 from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO
 from urllib.parse import quote_from_bytes as urlquote_from_bytes
@@ -34,6 +34,9 @@ __all__ = [
 # Internals
 #
 
+# EBADF - guard agains macOS `stat` throwing EBADF
+_IGNORED_ERROS = (ENOENT, ENOTDIR, EBADF)
+
 def _is_wildcard_pattern(pat):
     # Whether this pattern needs actual matching using fnmatch, or can
     # be looked up directly as a file.
@@ -528,7 +531,13 @@ class _RecursiveWildcardSelector(_Selector):
         try:
             entries = list(scandir(parent_path))
             for entry in entries:
-                if entry.is_dir() and not entry.is_symlink():
+                entry_is_dir = False
+                try:
+                    entry_is_dir = entry.is_dir()
+                except OSError as e:
+                    if e.errno not in _IGNORED_ERROS:
+                        raise
+                if entry_is_dir and not entry.is_symlink():
                     path = parent_path._make_child_relpath(entry.name)
                     for p in self._iterate_directories(path, is_dir, scandir):
                         yield p
@@ -1319,7 +1328,7 @@ class Path(PurePath):
         try:
             self.stat()
         except OSError as e:
-            if e.errno not in (ENOENT, ENOTDIR):
+            if e.errno not in _IGNORED_ERROS:
                 raise
             return False
         return True
@@ -1331,7 +1340,7 @@ class Path(PurePath):
         try:
             return S_ISDIR(self.stat().st_mode)
         except OSError as e:
-            if e.errno not in (ENOENT, ENOTDIR):
+            if e.errno not in _IGNORED_ERROS:
                 raise
             # Path doesn't exist or is a broken symlink
             # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
@@ -1345,7 +1354,7 @@ class Path(PurePath):
         try:
             return S_ISREG(self.stat().st_mode)
         except OSError as e:
-            if e.errno not in (ENOENT, ENOTDIR):
+            if e.errno not in _IGNORED_ERROS:
                 raise
             # Path doesn't exist or is a broken symlink
             # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
@@ -1379,7 +1388,7 @@ class Path(PurePath):
         try:
             return S_ISLNK(self.lstat().st_mode)
         except OSError as e:
-            if e.errno not in (ENOENT, ENOTDIR):
+            if e.errno not in _IGNORED_ERROS:
                 raise
             # Path doesn't exist
             return False
@@ -1391,7 +1400,7 @@ class Path(PurePath):
         try:
             return S_ISBLK(self.stat().st_mode)
         except OSError as e:
-            if e.errno not in (ENOENT, ENOTDIR):
+            if e.errno not in _IGNORED_ERROS:
                 raise
             # Path doesn't exist or is a broken symlink
             # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
@@ -1404,7 +1413,7 @@ class Path(PurePath):
         try:
             return S_ISCHR(self.stat().st_mode)
         except OSError as e:
-            if e.errno not in (ENOENT, ENOTDIR):
+            if e.errno not in _IGNORED_ERROS:
                 raise
             # Path doesn't exist or is a broken symlink
             # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
@@ -1417,7 +1426,7 @@ class Path(PurePath):
         try:
             return S_ISFIFO(self.stat().st_mode)
         except OSError as e:
-            if e.errno not in (ENOENT, ENOTDIR):
+            if e.errno not in _IGNORED_ERROS:
                 raise
             # Path doesn't exist or is a broken symlink
             # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
@@ -1430,7 +1439,7 @@ class Path(PurePath):
         try:
             return S_ISSOCK(self.stat().st_mode)
         except OSError as e:
-            if e.errno not in (ENOENT, ENOTDIR):
+            if e.errno not in _IGNORED_ERROS:
                 raise
             # Path doesn't exist or is a broken symlink
             # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
index ae7c75deb0e6b7ed5f779199e854a696ea494a93..e436db995ce49c13fc5c37d637d3778a9291c487 100644 (file)
@@ -1,6 +1,7 @@
 import collections.abc
 import io
 import os
+import sys
 import errno
 import pathlib
 import pickle
@@ -2176,6 +2177,29 @@ class PosixPathTest(_BasePathTest, unittest.TestCase):
             self.assertEqual(p6.expanduser(), p6)
             self.assertRaises(RuntimeError, p7.expanduser)
 
+    @unittest.skipIf(sys.platform != "darwin",
+                     "Bad file descriptor in /dev/fd affects only macOS")
+    def test_handling_bad_descriptor(self):
+        try:
+            file_descriptors = list(pathlib.Path('/dev/fd').rglob("*"))[3:]
+            if not file_descriptors:
+                self.skipTest("no file descriptors - issue was not reproduced")
+            # Checking all file descriptors because there is no guarantee
+            # which one will fail.
+            for f in file_descriptors:
+                f.exists()
+                f.is_dir()
+                f.is_file()
+                f.is_symlink()
+                f.is_block_device()
+                f.is_char_device()
+                f.is_fifo()
+                f.is_socket()
+        except OSError as e:
+            if e.errno == errno.EBADF:
+                self.fail("Bad file descriptor not handled.")
+            raise
+
 
 @only_nt
 class WindowsPathTest(_BasePathTest, unittest.TestCase):
diff --git a/Misc/NEWS.d/next/macOS/2018-07-31-09-51-01.bpo-33635.KiscE-.rst b/Misc/NEWS.d/next/macOS/2018-07-31-09-51-01.bpo-33635.KiscE-.rst
new file mode 100644 (file)
index 0000000..90d9ab6
--- /dev/null
@@ -0,0 +1,5 @@
+In macOS stat on some file descriptors (/dev/fd/3 f.e) will result in bad
+file descriptor OSError. Guard against this exception was added in is_dir,
+is_file and similar methods. DirEntry.is_dir can also throw this exception
+so _RecursiveWildcardSelector._iterate_directories was also extended with
+the same error ignoring pattern.