[Python-checkins] bpo-33635: Handling Bad file descriptor in Path.is_file and related. (GH-8542)
Miss Islington (bot)
webhook-mailer at python.org
Mon Aug 27 18:37:22 EDT 2018
https://github.com/python/cpython/commit/2cf33161f7cb4ca726c74fcfa2d6db27073953d8
commit: 2cf33161f7cb4ca726c74fcfa2d6db27073953d8
branch: 3.7
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: GitHub <noreply at github.com>
date: 2018-08-27T18:37:18-04:00
summary:
bpo-33635: Handling Bad file descriptor in Path.is_file and related. (GH-8542)
(cherry picked from commit 216b745eafa7cd4a683a8405dcfbd7f5567f504c)
Co-authored-by: Przemysław Spodymek <przemyslaw at spodymek.com>
files:
A Misc/NEWS.d/next/macOS/2018-07-31-09-51-01.bpo-33635.KiscE-.rst
M Lib/pathlib.py
M Lib/test/test_pathlib.py
diff --git a/Lib/pathlib.py b/Lib/pathlib.py
index 6be372ed320a..c2986bd022d0 100644
--- a/Lib/pathlib.py
+++ b/Lib/pathlib.py
@@ -7,7 +7,7 @@
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 @@
# 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 @@ def _iterate_directories(self, parent_path, is_dir, scandir):
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 @@ def exists(self):
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 @@ def is_dir(self):
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 @@ def is_file(self):
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 @@ def is_symlink(self):
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 @@ def is_block_device(self):
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 @@ def is_char_device(self):
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 @@ def is_fifo(self):
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 @@ def is_socket(self):
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/)
diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py
index ae7c75deb0e6..e436db995ce4 100644
--- a/Lib/test/test_pathlib.py
+++ b/Lib/test/test_pathlib.py
@@ -1,6 +1,7 @@
import collections.abc
import io
import os
+import sys
import errno
import pathlib
import pickle
@@ -2176,6 +2177,29 @@ def test_expanduser(self):
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
index 000000000000..90d9ab6d4202
--- /dev/null
+++ b/Misc/NEWS.d/next/macOS/2018-07-31-09-51-01.bpo-33635.KiscE-.rst
@@ -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.
More information about the Python-checkins
mailing list