mirror of
https://github.com/python/cpython.git
synced 2025-09-26 10:19:53 +00:00
bpo-33635: Handling Bad file descriptor in Path.is_file and related. (GH-8542)
This commit is contained in:
parent
7ef1697be5
commit
216b745eaf
3 changed files with 48 additions and 10 deletions
|
@ -7,7 +7,7 @@ import posixpath
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
from _collections_abc import Sequence
|
from _collections_abc import Sequence
|
||||||
from errno import EINVAL, ENOENT, ENOTDIR
|
from errno import EINVAL, ENOENT, ENOTDIR, EBADF
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO
|
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
|
from urllib.parse import quote_from_bytes as urlquote_from_bytes
|
||||||
|
@ -34,6 +34,9 @@ __all__ = [
|
||||||
# Internals
|
# Internals
|
||||||
#
|
#
|
||||||
|
|
||||||
|
# EBADF - guard agains macOS `stat` throwing EBADF
|
||||||
|
_IGNORED_ERROS = (ENOENT, ENOTDIR, EBADF)
|
||||||
|
|
||||||
def _is_wildcard_pattern(pat):
|
def _is_wildcard_pattern(pat):
|
||||||
# Whether this pattern needs actual matching using fnmatch, or can
|
# Whether this pattern needs actual matching using fnmatch, or can
|
||||||
# be looked up directly as a file.
|
# be looked up directly as a file.
|
||||||
|
@ -528,7 +531,13 @@ class _RecursiveWildcardSelector(_Selector):
|
||||||
try:
|
try:
|
||||||
entries = list(scandir(parent_path))
|
entries = list(scandir(parent_path))
|
||||||
for entry in entries:
|
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)
|
path = parent_path._make_child_relpath(entry.name)
|
||||||
for p in self._iterate_directories(path, is_dir, scandir):
|
for p in self._iterate_directories(path, is_dir, scandir):
|
||||||
yield p
|
yield p
|
||||||
|
@ -1319,7 +1328,7 @@ class Path(PurePath):
|
||||||
try:
|
try:
|
||||||
self.stat()
|
self.stat()
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno not in (ENOENT, ENOTDIR):
|
if e.errno not in _IGNORED_ERROS:
|
||||||
raise
|
raise
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
@ -1331,7 +1340,7 @@ class Path(PurePath):
|
||||||
try:
|
try:
|
||||||
return S_ISDIR(self.stat().st_mode)
|
return S_ISDIR(self.stat().st_mode)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno not in (ENOENT, ENOTDIR):
|
if e.errno not in _IGNORED_ERROS:
|
||||||
raise
|
raise
|
||||||
# Path doesn't exist or is a broken symlink
|
# Path doesn't exist or is a broken symlink
|
||||||
# (see https://bitbucket.org/pitrou/pathlib/issue/12/)
|
# (see https://bitbucket.org/pitrou/pathlib/issue/12/)
|
||||||
|
@ -1345,7 +1354,7 @@ class Path(PurePath):
|
||||||
try:
|
try:
|
||||||
return S_ISREG(self.stat().st_mode)
|
return S_ISREG(self.stat().st_mode)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno not in (ENOENT, ENOTDIR):
|
if e.errno not in _IGNORED_ERROS:
|
||||||
raise
|
raise
|
||||||
# Path doesn't exist or is a broken symlink
|
# Path doesn't exist or is a broken symlink
|
||||||
# (see https://bitbucket.org/pitrou/pathlib/issue/12/)
|
# (see https://bitbucket.org/pitrou/pathlib/issue/12/)
|
||||||
|
@ -1379,7 +1388,7 @@ class Path(PurePath):
|
||||||
try:
|
try:
|
||||||
return S_ISLNK(self.lstat().st_mode)
|
return S_ISLNK(self.lstat().st_mode)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno not in (ENOENT, ENOTDIR):
|
if e.errno not in _IGNORED_ERROS:
|
||||||
raise
|
raise
|
||||||
# Path doesn't exist
|
# Path doesn't exist
|
||||||
return False
|
return False
|
||||||
|
@ -1391,7 +1400,7 @@ class Path(PurePath):
|
||||||
try:
|
try:
|
||||||
return S_ISBLK(self.stat().st_mode)
|
return S_ISBLK(self.stat().st_mode)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno not in (ENOENT, ENOTDIR):
|
if e.errno not in _IGNORED_ERROS:
|
||||||
raise
|
raise
|
||||||
# Path doesn't exist or is a broken symlink
|
# Path doesn't exist or is a broken symlink
|
||||||
# (see https://bitbucket.org/pitrou/pathlib/issue/12/)
|
# (see https://bitbucket.org/pitrou/pathlib/issue/12/)
|
||||||
|
@ -1404,7 +1413,7 @@ class Path(PurePath):
|
||||||
try:
|
try:
|
||||||
return S_ISCHR(self.stat().st_mode)
|
return S_ISCHR(self.stat().st_mode)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno not in (ENOENT, ENOTDIR):
|
if e.errno not in _IGNORED_ERROS:
|
||||||
raise
|
raise
|
||||||
# Path doesn't exist or is a broken symlink
|
# Path doesn't exist or is a broken symlink
|
||||||
# (see https://bitbucket.org/pitrou/pathlib/issue/12/)
|
# (see https://bitbucket.org/pitrou/pathlib/issue/12/)
|
||||||
|
@ -1417,7 +1426,7 @@ class Path(PurePath):
|
||||||
try:
|
try:
|
||||||
return S_ISFIFO(self.stat().st_mode)
|
return S_ISFIFO(self.stat().st_mode)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno not in (ENOENT, ENOTDIR):
|
if e.errno not in _IGNORED_ERROS:
|
||||||
raise
|
raise
|
||||||
# Path doesn't exist or is a broken symlink
|
# Path doesn't exist or is a broken symlink
|
||||||
# (see https://bitbucket.org/pitrou/pathlib/issue/12/)
|
# (see https://bitbucket.org/pitrou/pathlib/issue/12/)
|
||||||
|
@ -1430,7 +1439,7 @@ class Path(PurePath):
|
||||||
try:
|
try:
|
||||||
return S_ISSOCK(self.stat().st_mode)
|
return S_ISSOCK(self.stat().st_mode)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno not in (ENOENT, ENOTDIR):
|
if e.errno not in _IGNORED_ERROS:
|
||||||
raise
|
raise
|
||||||
# Path doesn't exist or is a broken symlink
|
# Path doesn't exist or is a broken symlink
|
||||||
# (see https://bitbucket.org/pitrou/pathlib/issue/12/)
|
# (see https://bitbucket.org/pitrou/pathlib/issue/12/)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import collections.abc
|
import collections.abc
|
||||||
import io
|
import io
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
import errno
|
import errno
|
||||||
import pathlib
|
import pathlib
|
||||||
import pickle
|
import pickle
|
||||||
|
@ -2176,6 +2177,29 @@ class PosixPathTest(_BasePathTest, unittest.TestCase):
|
||||||
self.assertEqual(p6.expanduser(), p6)
|
self.assertEqual(p6.expanduser(), p6)
|
||||||
self.assertRaises(RuntimeError, p7.expanduser)
|
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
|
@only_nt
|
||||||
class WindowsPathTest(_BasePathTest, unittest.TestCase):
|
class WindowsPathTest(_BasePathTest, unittest.TestCase):
|
||||||
|
|
|
@ -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.
|
Loading…
Add table
Add a link
Reference in a new issue