summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBarney Gale <barney.gale@gmail.com>2023-06-26 16:58:17 (GMT)
committerGitHub <noreply@github.com>2023-06-26 16:58:17 (GMT)
commit219effa876785408a87bd6acb37c07ee0d25f3f9 (patch)
treee23207f21e6fb329be9ba41c4a3321aab1600894
parent5d4dbf0e309255e5bce9e31d805a8f950ebf9161 (diff)
downloadcpython-219effa876785408a87bd6acb37c07ee0d25f3f9.zip
cpython-219effa876785408a87bd6acb37c07ee0d25f3f9.tar.gz
cpython-219effa876785408a87bd6acb37c07ee0d25f3f9.tar.bz2
GH-105793: Add follow_symlinks argument to `pathlib.Path.is_dir()` and `is_file()` (GH-105794)
Brings `pathlib.Path.is_dir()` and `in line with `os.DirEntry.is_dir()`, which will be important for implementing generic path walking and globbing. Likewise `is_file()`.
-rw-r--r--Doc/library/pathlib.rst24
-rw-r--r--Doc/whatsnew/3.13.rst7
-rw-r--r--Lib/pathlib.py8
-rw-r--r--Lib/test/test_pathlib.py36
-rw-r--r--Misc/NEWS.d/next/Library/2023-06-14-18-41-18.gh-issue-105793.YSoykM.rst2
5 files changed, 59 insertions, 18 deletions
diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst
index 9bbfe38..3385754 100644
--- a/Doc/library/pathlib.rst
+++ b/Doc/library/pathlib.rst
@@ -978,23 +978,35 @@ call fails (for example because the path doesn't exist).
available. In previous versions, :exc:`NotImplementedError` was raised.
-.. method:: Path.is_dir()
+.. method:: Path.is_dir(*, follow_symlinks=True)
- Return ``True`` if the path points to a directory (or a symbolic link
- pointing to a directory), ``False`` if it points to another kind of file.
+ Return ``True`` if the path points to a directory, ``False`` if it points
+ to another kind of file.
``False`` is also returned if the path doesn't exist or is a broken symlink;
other errors (such as permission errors) are propagated.
+ This method normally follows symlinks; to exclude symlinks to directories,
+ add the argument ``follow_symlinks=False``.
-.. method:: Path.is_file()
+ .. versionchanged:: 3.13
+ The *follow_symlinks* parameter was added.
+
+
+.. method:: Path.is_file(*, follow_symlinks=True)
- Return ``True`` if the path points to a regular file (or a symbolic link
- pointing to a regular file), ``False`` if it points to another kind of file.
+ Return ``True`` if the path points to a regular file, ``False`` if it
+ points to another kind of file.
``False`` is also returned if the path doesn't exist or is a broken symlink;
other errors (such as permission errors) are propagated.
+ This method normally follows symlinks; to exclude symlinks, add the
+ argument ``follow_symlinks=False``.
+
+ .. versionchanged:: 3.13
+ The *follow_symlinks* parameter was added.
+
.. method:: Path.is_junction()
diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst
index b6d13a8..f3460be 100644
--- a/Doc/whatsnew/3.13.rst
+++ b/Doc/whatsnew/3.13.rst
@@ -113,9 +113,10 @@ pathlib
* Add support for recursive wildcards in :meth:`pathlib.PurePath.match`.
(Contributed by Barney Gale in :gh:`73435`.)
-* Add *follow_symlinks* keyword-only argument to :meth:`pathlib.Path.glob` and
- :meth:`~pathlib.Path.rglob`.
- (Contributed by Barney Gale in :gh:`77609`.)
+* Add *follow_symlinks* keyword-only argument to :meth:`pathlib.Path.glob`,
+ :meth:`~pathlib.Path.rglob`, :meth:`~pathlib.Path.is_file`, and
+ :meth:`~pathlib.Path.is_dir`.
+ (Contributed by Barney Gale in :gh:`77609` and :gh:`105793`.)
traceback
---------
diff --git a/Lib/pathlib.py b/Lib/pathlib.py
index a36ffdd..e15718d 100644
--- a/Lib/pathlib.py
+++ b/Lib/pathlib.py
@@ -817,12 +817,12 @@ class Path(PurePath):
return False
return True
- def is_dir(self):
+ def is_dir(self, *, follow_symlinks=True):
"""
Whether this path is a directory.
"""
try:
- return S_ISDIR(self.stat().st_mode)
+ return S_ISDIR(self.stat(follow_symlinks=follow_symlinks).st_mode)
except OSError as e:
if not _ignore_error(e):
raise
@@ -833,13 +833,13 @@ class Path(PurePath):
# Non-encodable path
return False
- def is_file(self):
+ def is_file(self, *, follow_symlinks=True):
"""
Whether this path is a regular file (also True for symlinks pointing
to regular files).
"""
try:
- return S_ISREG(self.stat().st_mode)
+ return S_ISREG(self.stat(follow_symlinks=follow_symlinks).st_mode)
except OSError as e:
if not _ignore_error(e):
raise
diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py
index f935690..eeb522b 100644
--- a/Lib/test/test_pathlib.py
+++ b/Lib/test/test_pathlib.py
@@ -2191,9 +2191,22 @@ class PathTest(unittest.TestCase):
if os_helper.can_symlink():
self.assertFalse((P / 'linkA').is_dir())
self.assertTrue((P / 'linkB').is_dir())
- self.assertFalse((P/ 'brokenLink').is_dir(), False)
- self.assertIs((P / 'dirA\udfff').is_dir(), False)
- self.assertIs((P / 'dirA\x00').is_dir(), False)
+ self.assertFalse((P/ 'brokenLink').is_dir())
+ self.assertFalse((P / 'dirA\udfff').is_dir())
+ self.assertFalse((P / 'dirA\x00').is_dir())
+
+ def test_is_dir_no_follow_symlinks(self):
+ P = self.cls(BASE)
+ self.assertTrue((P / 'dirA').is_dir(follow_symlinks=False))
+ self.assertFalse((P / 'fileA').is_dir(follow_symlinks=False))
+ self.assertFalse((P / 'non-existing').is_dir(follow_symlinks=False))
+ self.assertFalse((P / 'fileA' / 'bah').is_dir(follow_symlinks=False))
+ if os_helper.can_symlink():
+ self.assertFalse((P / 'linkA').is_dir(follow_symlinks=False))
+ self.assertFalse((P / 'linkB').is_dir(follow_symlinks=False))
+ self.assertFalse((P/ 'brokenLink').is_dir(follow_symlinks=False))
+ self.assertFalse((P / 'dirA\udfff').is_dir(follow_symlinks=False))
+ self.assertFalse((P / 'dirA\x00').is_dir(follow_symlinks=False))
def test_is_file(self):
P = self.cls(BASE)
@@ -2205,8 +2218,21 @@ class PathTest(unittest.TestCase):
self.assertTrue((P / 'linkA').is_file())
self.assertFalse((P / 'linkB').is_file())
self.assertFalse((P/ 'brokenLink').is_file())
- self.assertIs((P / 'fileA\udfff').is_file(), False)
- self.assertIs((P / 'fileA\x00').is_file(), False)
+ self.assertFalse((P / 'fileA\udfff').is_file())
+ self.assertFalse((P / 'fileA\x00').is_file())
+
+ def test_is_file_no_follow_symlinks(self):
+ P = self.cls(BASE)
+ self.assertTrue((P / 'fileA').is_file(follow_symlinks=False))
+ self.assertFalse((P / 'dirA').is_file(follow_symlinks=False))
+ self.assertFalse((P / 'non-existing').is_file(follow_symlinks=False))
+ self.assertFalse((P / 'fileA' / 'bah').is_file(follow_symlinks=False))
+ if os_helper.can_symlink():
+ self.assertFalse((P / 'linkA').is_file(follow_symlinks=False))
+ self.assertFalse((P / 'linkB').is_file(follow_symlinks=False))
+ self.assertFalse((P/ 'brokenLink').is_file(follow_symlinks=False))
+ self.assertFalse((P / 'fileA\udfff').is_file(follow_symlinks=False))
+ self.assertFalse((P / 'fileA\x00').is_file(follow_symlinks=False))
def test_is_mount(self):
P = self.cls(BASE)
diff --git a/Misc/NEWS.d/next/Library/2023-06-14-18-41-18.gh-issue-105793.YSoykM.rst b/Misc/NEWS.d/next/Library/2023-06-14-18-41-18.gh-issue-105793.YSoykM.rst
new file mode 100644
index 0000000..0e4090e
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-06-14-18-41-18.gh-issue-105793.YSoykM.rst
@@ -0,0 +1,2 @@
+Add *follow_symlinks* keyword-only argument to :meth:`pathlib.Path.is_dir` and
+:meth:`~pathlib.Path.is_file`, defaulting to ``True``.