Thanks to visit codestin.com
Credit goes to github.com

Skip to content

GH-127381: pathlib ABCs: remove PathBase.samefile() and rarer is_*() #127709

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 2 additions & 87 deletions Lib/pathlib/_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import operator
from errno import EINVAL
from glob import _GlobberBase, _no_recurse_symlinks
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
from pathlib._os import copyfileobj


Expand Down Expand Up @@ -460,26 +460,6 @@ def is_file(self, *, follow_symlinks=True):
except (OSError, ValueError):
return False

def is_mount(self):
"""
Check if this path is a mount point
"""
# Need to exist and be a dir
if not self.exists() or not self.is_dir():
return False

try:
parent_dev = self.parent.stat().st_dev
except OSError:
return False

dev = self.stat().st_dev
if dev != parent_dev:
return True
ino = self.stat().st_ino
parent_ino = self.parent.stat().st_ino
return ino == parent_ino

def is_symlink(self):
"""
Whether this path is a symbolic link.
Expand All @@ -489,76 +469,11 @@ def is_symlink(self):
except (OSError, ValueError):
return False

def is_junction(self):
"""
Whether this path is a junction.
"""
# Junctions are a Windows-only feature, not present in POSIX nor the
# majority of virtual filesystems. There is no cross-platform idiom
# to check for junctions (using stat().st_mode).
return False

def is_block_device(self):
"""
Whether this path is a block device.
"""
try:
return S_ISBLK(self.stat().st_mode)
except (OSError, ValueError):
return False

def is_char_device(self):
"""
Whether this path is a character device.
"""
try:
return S_ISCHR(self.stat().st_mode)
except (OSError, ValueError):
return False

def is_fifo(self):
"""
Whether this path is a FIFO.
"""
try:
return S_ISFIFO(self.stat().st_mode)
except (OSError, ValueError):
return False

def is_socket(self):
"""
Whether this path is a socket.
"""
try:
return S_ISSOCK(self.stat().st_mode)
except (OSError, ValueError):
return False

def samefile(self, other_path):
"""Return whether other_path is the same or not as this file
(as returned by os.path.samefile()).
"""
st = self.stat()
try:
other_st = other_path.stat()
except AttributeError:
other_st = self.with_segments(other_path).stat()
return (st.st_ino == other_st.st_ino and
st.st_dev == other_st.st_dev)

def _ensure_different_file(self, other_path):
"""
Raise OSError(EINVAL) if both paths refer to the same file.
"""
try:
if not self.samefile(other_path):
return
except (OSError, ValueError):
return
err = OSError(EINVAL, "Source and target are the same file")
err.filename = str(self)
err.filename2 = str(other_path)
raise err
pass

def _ensure_distinct_path(self, other_path):
"""
Expand Down
65 changes: 64 additions & 1 deletion Lib/pathlib/_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
import os
import posixpath
import sys
from errno import EXDEV
from errno import EINVAL, EXDEV
from glob import _StringGlobber
from itertools import chain
from stat import S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO
from _collections_abc import Sequence

try:
Expand Down Expand Up @@ -596,6 +597,68 @@ def is_junction(self):
"""
return os.path.isjunction(self)

def is_block_device(self):
"""
Whether this path is a block device.
"""
try:
return S_ISBLK(self.stat().st_mode)
except (OSError, ValueError):
return False

def is_char_device(self):
"""
Whether this path is a character device.
"""
try:
return S_ISCHR(self.stat().st_mode)
except (OSError, ValueError):
return False

def is_fifo(self):
"""
Whether this path is a FIFO.
"""
try:
return S_ISFIFO(self.stat().st_mode)
except (OSError, ValueError):
return False

def is_socket(self):
"""
Whether this path is a socket.
"""
try:
return S_ISSOCK(self.stat().st_mode)
except (OSError, ValueError):
return False

def samefile(self, other_path):
"""Return whether other_path is the same or not as this file
(as returned by os.path.samefile()).
"""
st = self.stat()
try:
other_st = other_path.stat()
except AttributeError:
other_st = self.with_segments(other_path).stat()
return (st.st_ino == other_st.st_ino and
st.st_dev == other_st.st_dev)

def _ensure_different_file(self, other_path):
"""
Raise OSError(EINVAL) if both paths refer to the same file.
"""
try:
if not self.samefile(other_path):
return
except (OSError, ValueError):
return
err = OSError(EINVAL, "Source and target are the same file")
err.filename = str(self)
err.filename2 = str(other_path)
raise err

def open(self, mode='r', buffering=-1, encoding=None,
errors=None, newline=None):
"""
Expand Down
77 changes: 75 additions & 2 deletions Lib/test/test_pathlib/test_pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -1786,13 +1786,31 @@ def test_lstat_nosymlink(self):
st = p.stat()
self.assertEqual(st, p.lstat())

def test_is_junction(self):
def test_is_junction_false(self):
P = self.cls(self.base)
self.assertFalse((P / 'fileA').is_junction())
self.assertFalse((P / 'dirA').is_junction())
self.assertFalse((P / 'non-existing').is_junction())
self.assertFalse((P / 'fileA' / 'bah').is_junction())
self.assertFalse((P / 'fileA\udfff').is_junction())
self.assertFalse((P / 'fileA\x00').is_junction())

def test_is_junction_true(self):
P = self.cls(self.base)

with mock.patch.object(P.parser, 'isjunction'):
self.assertEqual(P.is_junction(), P.parser.isjunction.return_value)
P.parser.isjunction.assert_called_once_with(P)

def test_is_fifo_false(self):
P = self.cls(self.base)
self.assertFalse((P / 'fileA').is_fifo())
self.assertFalse((P / 'dirA').is_fifo())
self.assertFalse((P / 'non-existing').is_fifo())
self.assertFalse((P / 'fileA' / 'bah').is_fifo())
self.assertIs((P / 'fileA\udfff').is_fifo(), False)
self.assertIs((P / 'fileA\x00').is_fifo(), False)

@unittest.skipUnless(hasattr(os, "mkfifo"), "os.mkfifo() required")
@unittest.skipIf(sys.platform == "vxworks",
"fifo requires special path on VxWorks")
Expand All @@ -1808,6 +1826,15 @@ def test_is_fifo_true(self):
self.assertIs(self.cls(self.base, 'myfifo\udfff').is_fifo(), False)
self.assertIs(self.cls(self.base, 'myfifo\x00').is_fifo(), False)

def test_is_socket_false(self):
P = self.cls(self.base)
self.assertFalse((P / 'fileA').is_socket())
self.assertFalse((P / 'dirA').is_socket())
self.assertFalse((P / 'non-existing').is_socket())
self.assertFalse((P / 'fileA' / 'bah').is_socket())
self.assertIs((P / 'fileA\udfff').is_socket(), False)
self.assertIs((P / 'fileA\x00').is_socket(), False)

@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
@unittest.skipIf(
is_emscripten, "Unix sockets are not implemented on Emscripten."
Expand All @@ -1831,6 +1858,24 @@ def test_is_socket_true(self):
self.assertIs(self.cls(self.base, 'mysock\udfff').is_socket(), False)
self.assertIs(self.cls(self.base, 'mysock\x00').is_socket(), False)

def test_is_block_device_false(self):
P = self.cls(self.base)
self.assertFalse((P / 'fileA').is_block_device())
self.assertFalse((P / 'dirA').is_block_device())
self.assertFalse((P / 'non-existing').is_block_device())
self.assertFalse((P / 'fileA' / 'bah').is_block_device())
self.assertIs((P / 'fileA\udfff').is_block_device(), False)
self.assertIs((P / 'fileA\x00').is_block_device(), False)

def test_is_char_device_false(self):
P = self.cls(self.base)
self.assertFalse((P / 'fileA').is_char_device())
self.assertFalse((P / 'dirA').is_char_device())
self.assertFalse((P / 'non-existing').is_char_device())
self.assertFalse((P / 'fileA' / 'bah').is_char_device())
self.assertIs((P / 'fileA\udfff').is_char_device(), False)
self.assertIs((P / 'fileA\x00').is_char_device(), False)

def test_is_char_device_true(self):
# os.devnull should generally be a char device.
P = self.cls(os.devnull)
Expand All @@ -1842,14 +1887,42 @@ def test_is_char_device_true(self):
self.assertIs(self.cls(f'{os.devnull}\udfff').is_char_device(), False)
self.assertIs(self.cls(f'{os.devnull}\x00').is_char_device(), False)

def test_is_mount_root(self):
def test_is_mount(self):
P = self.cls(self.base)
self.assertFalse((P / 'fileA').is_mount())
self.assertFalse((P / 'dirA').is_mount())
self.assertFalse((P / 'non-existing').is_mount())
self.assertFalse((P / 'fileA' / 'bah').is_mount())
if self.can_symlink:
self.assertFalse((P / 'linkA').is_mount())
if os.name == 'nt':
R = self.cls('c:\\')
else:
R = self.cls('/')
self.assertTrue(R.is_mount())
self.assertFalse((R / '\udfff').is_mount())

def test_samefile(self):
parser = self.parser
fileA_path = parser.join(self.base, 'fileA')
fileB_path = parser.join(self.base, 'dirB', 'fileB')
p = self.cls(fileA_path)
pp = self.cls(fileA_path)
q = self.cls(fileB_path)
self.assertTrue(p.samefile(fileA_path))
self.assertTrue(p.samefile(pp))
self.assertFalse(p.samefile(fileB_path))
self.assertFalse(p.samefile(q))
# Test the non-existent file case
non_existent = parser.join(self.base, 'foo')
r = self.cls(non_existent)
self.assertRaises(FileNotFoundError, p.samefile, r)
self.assertRaises(FileNotFoundError, p.samefile, non_existent)
self.assertRaises(FileNotFoundError, r.samefile, p)
self.assertRaises(FileNotFoundError, r.samefile, non_existent)
self.assertRaises(FileNotFoundError, r.samefile, r)
self.assertRaises(FileNotFoundError, r.samefile, non_existent)

def test_passing_kwargs_errors(self):
with self.assertRaises(TypeError):
self.cls(foo="bar")
Expand Down
Loading
Loading