Issue #22759: Query methods on pathlib.Path() (exists(), is_dir(), etc.) now return False when the underlying stat call raises NotADirectoryError.

This commit is contained in:
Antoine Pitrou 2014-10-30 23:15:25 +01:00
commit c689101a5e
3 changed files with 22 additions and 9 deletions

View File

@ -8,7 +8,7 @@ import re
import sys import sys
from collections import Sequence from collections import Sequence
from contextlib import contextmanager from contextlib import contextmanager
from errno import EINVAL, ENOENT from errno import EINVAL, ENOENT, ENOTDIR
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
@ -1238,7 +1238,7 @@ class Path(PurePath):
try: try:
self.stat() self.stat()
except OSError as e: except OSError as e:
if e.errno != ENOENT: if e.errno not in (ENOENT, ENOTDIR):
raise raise
return False return False
return True return True
@ -1250,7 +1250,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 != ENOENT: if e.errno not in (ENOENT, ENOTDIR):
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/)
@ -1264,7 +1264,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 != ENOENT: if e.errno not in (ENOENT, ENOTDIR):
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/)
@ -1277,7 +1277,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 != ENOENT: if e.errno not in (ENOENT, ENOTDIR):
raise raise
# Path doesn't exist # Path doesn't exist
return False return False
@ -1289,7 +1289,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 != ENOENT: if e.errno not in (ENOENT, ENOTDIR):
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/)
@ -1302,7 +1302,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 != ENOENT: if e.errno not in (ENOENT, ENOTDIR):
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/)
@ -1315,7 +1315,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 != ENOENT: if e.errno not in (ENOENT, ENOTDIR):
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/)
@ -1328,7 +1328,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 != ENOENT: if e.errno not in (ENOENT, ENOTDIR):
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/)

View File

@ -1292,9 +1292,12 @@ class _BasePathTest(object):
self.assertIs(True, p.exists()) self.assertIs(True, p.exists())
self.assertIs(True, (p / 'dirA').exists()) self.assertIs(True, (p / 'dirA').exists())
self.assertIs(True, (p / 'fileA').exists()) self.assertIs(True, (p / 'fileA').exists())
self.assertIs(False, (p / 'fileA' / 'bah').exists())
if not symlink_skip_reason: if not symlink_skip_reason:
self.assertIs(True, (p / 'linkA').exists()) self.assertIs(True, (p / 'linkA').exists())
self.assertIs(True, (p / 'linkB').exists()) self.assertIs(True, (p / 'linkB').exists())
self.assertIs(True, (p / 'linkB' / 'fileB').exists())
self.assertIs(False, (p / 'linkA' / 'bah').exists())
self.assertIs(False, (p / 'foo').exists()) self.assertIs(False, (p / 'foo').exists())
self.assertIs(False, P('/xyzzy').exists()) self.assertIs(False, P('/xyzzy').exists())
@ -1712,6 +1715,7 @@ class _BasePathTest(object):
self.assertTrue((P / 'dirA').is_dir()) self.assertTrue((P / 'dirA').is_dir())
self.assertFalse((P / 'fileA').is_dir()) self.assertFalse((P / 'fileA').is_dir())
self.assertFalse((P / 'non-existing').is_dir()) self.assertFalse((P / 'non-existing').is_dir())
self.assertFalse((P / 'fileA' / 'bah').is_dir())
if not symlink_skip_reason: if not symlink_skip_reason:
self.assertFalse((P / 'linkA').is_dir()) self.assertFalse((P / 'linkA').is_dir())
self.assertTrue((P / 'linkB').is_dir()) self.assertTrue((P / 'linkB').is_dir())
@ -1722,6 +1726,7 @@ class _BasePathTest(object):
self.assertTrue((P / 'fileA').is_file()) self.assertTrue((P / 'fileA').is_file())
self.assertFalse((P / 'dirA').is_file()) self.assertFalse((P / 'dirA').is_file())
self.assertFalse((P / 'non-existing').is_file()) self.assertFalse((P / 'non-existing').is_file())
self.assertFalse((P / 'fileA' / 'bah').is_file())
if not symlink_skip_reason: if not symlink_skip_reason:
self.assertTrue((P / 'linkA').is_file()) self.assertTrue((P / 'linkA').is_file())
self.assertFalse((P / 'linkB').is_file()) self.assertFalse((P / 'linkB').is_file())
@ -1732,6 +1737,7 @@ class _BasePathTest(object):
self.assertFalse((P / 'fileA').is_symlink()) self.assertFalse((P / 'fileA').is_symlink())
self.assertFalse((P / 'dirA').is_symlink()) self.assertFalse((P / 'dirA').is_symlink())
self.assertFalse((P / 'non-existing').is_symlink()) self.assertFalse((P / 'non-existing').is_symlink())
self.assertFalse((P / 'fileA' / 'bah').is_symlink())
if not symlink_skip_reason: if not symlink_skip_reason:
self.assertTrue((P / 'linkA').is_symlink()) self.assertTrue((P / 'linkA').is_symlink())
self.assertTrue((P / 'linkB').is_symlink()) self.assertTrue((P / 'linkB').is_symlink())
@ -1742,6 +1748,7 @@ class _BasePathTest(object):
self.assertFalse((P / 'fileA').is_fifo()) self.assertFalse((P / 'fileA').is_fifo())
self.assertFalse((P / 'dirA').is_fifo()) self.assertFalse((P / 'dirA').is_fifo())
self.assertFalse((P / 'non-existing').is_fifo()) self.assertFalse((P / 'non-existing').is_fifo())
self.assertFalse((P / 'fileA' / 'bah').is_fifo())
@unittest.skipUnless(hasattr(os, "mkfifo"), "os.mkfifo() required") @unittest.skipUnless(hasattr(os, "mkfifo"), "os.mkfifo() required")
def test_is_fifo_true(self): def test_is_fifo_true(self):
@ -1756,6 +1763,7 @@ class _BasePathTest(object):
self.assertFalse((P / 'fileA').is_socket()) self.assertFalse((P / 'fileA').is_socket())
self.assertFalse((P / 'dirA').is_socket()) self.assertFalse((P / 'dirA').is_socket())
self.assertFalse((P / 'non-existing').is_socket()) self.assertFalse((P / 'non-existing').is_socket())
self.assertFalse((P / 'fileA' / 'bah').is_socket())
@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
def test_is_socket_true(self): def test_is_socket_true(self):
@ -1776,12 +1784,14 @@ class _BasePathTest(object):
self.assertFalse((P / 'fileA').is_block_device()) self.assertFalse((P / 'fileA').is_block_device())
self.assertFalse((P / 'dirA').is_block_device()) self.assertFalse((P / 'dirA').is_block_device())
self.assertFalse((P / 'non-existing').is_block_device()) self.assertFalse((P / 'non-existing').is_block_device())
self.assertFalse((P / 'fileA' / 'bah').is_block_device())
def test_is_char_device_false(self): def test_is_char_device_false(self):
P = self.cls(BASE) P = self.cls(BASE)
self.assertFalse((P / 'fileA').is_char_device()) self.assertFalse((P / 'fileA').is_char_device())
self.assertFalse((P / 'dirA').is_char_device()) self.assertFalse((P / 'dirA').is_char_device())
self.assertFalse((P / 'non-existing').is_char_device()) self.assertFalse((P / 'non-existing').is_char_device())
self.assertFalse((P / 'fileA' / 'bah').is_char_device())
def test_is_char_device_true(self): def test_is_char_device_true(self):
# Under Unix, /dev/null should generally be a char device # Under Unix, /dev/null should generally be a char device

View File

@ -180,6 +180,9 @@ Core and Builtins
Library Library
------- -------
- Issue #22759: Query methods on pathlib.Path() (exists(), is_dir(), etc.)
now return False when the underlying stat call raises NotADirectoryError.
- Issue #8876: distutils now falls back to copying files when hard linking - Issue #8876: distutils now falls back to copying files when hard linking
doesn't work. This allows use with special filesystems such as VirtualBox doesn't work. This allows use with special filesystems such as VirtualBox
shared folders. shared folders.