summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBarney Gale <barney.gale@gmail.com>2024-12-05 21:39:43 (GMT)
committerGitHub <noreply@github.com>2024-12-05 21:39:43 (GMT)
commit8b3cccf3f9508572d85b0044519f2bd5715dacad (patch)
tree9c5e6a23fa26ef59fa0db986ccf9bf4a1bd3a9a2
parentf4f530804b9d8f089eba0f157ec2144c03b13651 (diff)
downloadcpython-8b3cccf3f9508572d85b0044519f2bd5715dacad.zip
cpython-8b3cccf3f9508572d85b0044519f2bd5715dacad.tar.gz
cpython-8b3cccf3f9508572d85b0044519f2bd5715dacad.tar.bz2
GH-125413: Revert addition of `pathlib.Path.scandir()` method (#127377)
Remove documentation for `pathlib.Path.scandir()`, and rename the method to `_scandir()`. In the private pathlib ABCs, make `iterdir()` abstract and call it from `_scandir()`. It's not worthwhile to add this method at the moment - see discussion: https://discuss.python.org/t/ergonomics-of-new-pathlib-path-scandir/71721 Co-authored-by: Steve Dower <steve.dower@microsoft.com>
-rw-r--r--Doc/library/pathlib.rst29
-rw-r--r--Doc/whatsnew/3.14.rst6
-rw-r--r--Lib/pathlib/_abc.py15
-rw-r--r--Lib/pathlib/_local.py4
-rw-r--r--Lib/test/test_pathlib/test_pathlib_abc.py48
-rw-r--r--Misc/NEWS.d/3.14.0a2.rst2
-rw-r--r--Misc/NEWS.d/next/Library/2024-11-29-00-15-59.gh-issue-125413.WCN0vv.rst3
7 files changed, 22 insertions, 85 deletions
diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst
index a42ac1f..4b48880 100644
--- a/Doc/library/pathlib.rst
+++ b/Doc/library/pathlib.rst
@@ -1289,35 +1289,6 @@ Reading directories
raised.
-.. method:: Path.scandir()
-
- When the path points to a directory, return an iterator of
- :class:`os.DirEntry` objects corresponding to entries in the directory. The
- returned iterator supports the :term:`context manager` protocol. It is
- implemented using :func:`os.scandir` and gives the same guarantees.
-
- Using :meth:`~Path.scandir` instead of :meth:`~Path.iterdir` can
- significantly increase the performance of code that also needs file type or
- file attribute information, because :class:`os.DirEntry` objects expose
- this information if the operating system provides it when scanning a
- directory.
-
- The following example displays the names of subdirectories. The
- ``entry.is_dir()`` check will generally not make an additional system call::
-
- >>> p = Path('docs')
- >>> with p.scandir() as entries:
- ... for entry in entries:
- ... if entry.is_dir():
- ... entry.name
- ...
- '_templates'
- '_build'
- '_static'
-
- .. versionadded:: 3.14
-
-
.. method:: Path.glob(pattern, *, case_sensitive=None, recurse_symlinks=False)
Glob the given relative *pattern* in the directory represented by this path,
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index db25c03..b300e34 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -532,12 +532,6 @@ pathlib
(Contributed by Barney Gale in :gh:`73991`.)
-* Add :meth:`pathlib.Path.scandir` to scan a directory and return an iterator
- of :class:`os.DirEntry` objects. This is exactly equivalent to calling
- :func:`os.scandir` on a path object.
-
- (Contributed by Barney Gale in :gh:`125413`.)
-
pdb
---
diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py
index 2b314b6..86617ff 100644
--- a/Lib/pathlib/_abc.py
+++ b/Lib/pathlib/_abc.py
@@ -94,7 +94,7 @@ class PathGlobber(_GlobberBase):
lexists = operator.methodcaller('exists', follow_symlinks=False)
add_slash = operator.methodcaller('joinpath', '')
- scandir = operator.methodcaller('scandir')
+ scandir = operator.methodcaller('_scandir')
@staticmethod
def concat_path(path, text):
@@ -632,13 +632,14 @@ class PathBase(PurePathBase):
with self.open(mode='w', encoding=encoding, errors=errors, newline=newline) as f:
return f.write(data)
- def scandir(self):
- """Yield os.DirEntry objects of the directory contents.
+ def _scandir(self):
+ """Yield os.DirEntry-like objects of the directory contents.
The children are yielded in arbitrary order, and the
special entries '.' and '..' are not included.
"""
- raise UnsupportedOperation(self._unsupported_msg('scandir()'))
+ import contextlib
+ return contextlib.nullcontext(self.iterdir())
def iterdir(self):
"""Yield path objects of the directory contents.
@@ -646,9 +647,7 @@ class PathBase(PurePathBase):
The children are yielded in arbitrary order, and the
special entries '.' and '..' are not included.
"""
- with self.scandir() as entries:
- names = [entry.name for entry in entries]
- return map(self.joinpath, names)
+ raise UnsupportedOperation(self._unsupported_msg('iterdir()'))
def _glob_selector(self, parts, case_sensitive, recurse_symlinks):
if case_sensitive is None:
@@ -698,7 +697,7 @@ class PathBase(PurePathBase):
if not top_down:
paths.append((path, dirnames, filenames))
try:
- with path.scandir() as entries:
+ with path._scandir() as entries:
for entry in entries:
name = entry.name
try:
diff --git a/Lib/pathlib/_local.py b/Lib/pathlib/_local.py
index b5d9dc4..bb8a252 100644
--- a/Lib/pathlib/_local.py
+++ b/Lib/pathlib/_local.py
@@ -634,8 +634,8 @@ class Path(PathBase, PurePath):
path_str = path_str[:-1]
yield path_str
- def scandir(self):
- """Yield os.DirEntry objects of the directory contents.
+ def _scandir(self):
+ """Yield os.DirEntry-like objects of the directory contents.
The children are yielded in arbitrary order, and the
special entries '.' and '..' are not included.
diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py
index 5fa2f55..7ba3fa8 100644
--- a/Lib/test/test_pathlib/test_pathlib_abc.py
+++ b/Lib/test/test_pathlib/test_pathlib_abc.py
@@ -1,5 +1,4 @@
import collections
-import contextlib
import io
import os
import errno
@@ -1418,24 +1417,6 @@ DummyPathStatResult = collections.namedtuple(
'st_mode st_ino st_dev st_nlink st_uid st_gid st_size st_atime st_mtime st_ctime')
-class DummyDirEntry:
- """
- Minimal os.DirEntry-like object. Returned from DummyPath.scandir().
- """
- __slots__ = ('name', '_is_symlink', '_is_dir')
-
- def __init__(self, name, is_symlink, is_dir):
- self.name = name
- self._is_symlink = is_symlink
- self._is_dir = is_dir
-
- def is_symlink(self):
- return self._is_symlink
-
- def is_dir(self, *, follow_symlinks=True):
- return self._is_dir and (follow_symlinks or not self._is_symlink)
-
-
class DummyPath(PathBase):
"""
Simple implementation of PathBase that keeps files and directories in
@@ -1503,25 +1484,14 @@ class DummyPath(PathBase):
stream = io.TextIOWrapper(stream, encoding=encoding, errors=errors, newline=newline)
return stream
- @contextlib.contextmanager
- def scandir(self):
- path = self.resolve()
- path_str = str(path)
- if path_str in self._files:
- raise NotADirectoryError(errno.ENOTDIR, "Not a directory", path_str)
- elif path_str in self._directories:
- yield iter([path.joinpath(name)._dir_entry for name in self._directories[path_str]])
+ def iterdir(self):
+ path = str(self.resolve())
+ if path in self._files:
+ raise NotADirectoryError(errno.ENOTDIR, "Not a directory", path)
+ elif path in self._directories:
+ return iter([self / name for name in self._directories[path]])
else:
- raise FileNotFoundError(errno.ENOENT, "File not found", path_str)
-
- @property
- def _dir_entry(self):
- path_str = str(self)
- is_symlink = path_str in self._symlinks
- is_directory = (path_str in self._directories
- if not is_symlink
- else self._symlinks[path_str][1])
- return DummyDirEntry(self.name, is_symlink, is_directory)
+ raise FileNotFoundError(errno.ENOENT, "File not found", path)
def mkdir(self, mode=0o777, parents=False, exist_ok=False):
path = str(self.parent.resolve() / self.name)
@@ -2214,9 +2184,9 @@ class DummyPathTest(DummyPurePathTest):
def test_scandir(self):
p = self.cls(self.base)
- with p.scandir() as entries:
+ with p._scandir() as entries:
self.assertTrue(list(entries))
- with p.scandir() as entries:
+ with p._scandir() as entries:
for entry in entries:
child = p / entry.name
self.assertIsNotNone(entry)
diff --git a/Misc/NEWS.d/3.14.0a2.rst b/Misc/NEWS.d/3.14.0a2.rst
index 7384ce5..d82ec98 100644
--- a/Misc/NEWS.d/3.14.0a2.rst
+++ b/Misc/NEWS.d/3.14.0a2.rst
@@ -597,7 +597,7 @@ TypeError is now raised instead of ValueError for some logical errors.
.. nonce: Jat5kq
.. section: Library
-Add :meth:`pathlib.Path.scandir` method to efficiently fetch directory
+Add :meth:`!pathlib.Path.scandir` method to efficiently fetch directory
children and their file attributes. This is a trivial wrapper of
:func:`os.scandir`.
diff --git a/Misc/NEWS.d/next/Library/2024-11-29-00-15-59.gh-issue-125413.WCN0vv.rst b/Misc/NEWS.d/next/Library/2024-11-29-00-15-59.gh-issue-125413.WCN0vv.rst
new file mode 100644
index 0000000..b56a77b
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-11-29-00-15-59.gh-issue-125413.WCN0vv.rst
@@ -0,0 +1,3 @@
+Revert addition of :meth:`!pathlib.Path.scandir`. This method was added in
+3.14.0a2. The optimizations remain for file system paths, but other
+subclasses should only have to implement :meth:`pathlib.Path.iterdir`.