Skip to content

Commit 8b3cccf

Browse files
barneygalezooba
andauthored
pythonGH-125413: Revert addition of pathlib.Path.scandir() method (python#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 <[email protected]>
1 parent f4f5308 commit 8b3cccf

File tree

7 files changed

+22
-85
lines changed

7 files changed

+22
-85
lines changed

Doc/library/pathlib.rst

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,35 +1289,6 @@ Reading directories
12891289
raised.
12901290

12911291

1292-
.. method:: Path.scandir()
1293-
1294-
When the path points to a directory, return an iterator of
1295-
:class:`os.DirEntry` objects corresponding to entries in the directory. The
1296-
returned iterator supports the :term:`context manager` protocol. It is
1297-
implemented using :func:`os.scandir` and gives the same guarantees.
1298-
1299-
Using :meth:`~Path.scandir` instead of :meth:`~Path.iterdir` can
1300-
significantly increase the performance of code that also needs file type or
1301-
file attribute information, because :class:`os.DirEntry` objects expose
1302-
this information if the operating system provides it when scanning a
1303-
directory.
1304-
1305-
The following example displays the names of subdirectories. The
1306-
``entry.is_dir()`` check will generally not make an additional system call::
1307-
1308-
>>> p = Path('docs')
1309-
>>> with p.scandir() as entries:
1310-
... for entry in entries:
1311-
... if entry.is_dir():
1312-
... entry.name
1313-
...
1314-
'_templates'
1315-
'_build'
1316-
'_static'
1317-
1318-
.. versionadded:: 3.14
1319-
1320-
13211292
.. method:: Path.glob(pattern, *, case_sensitive=None, recurse_symlinks=False)
13221293

13231294
Glob the given relative *pattern* in the directory represented by this path,

Doc/whatsnew/3.14.rst

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -532,12 +532,6 @@ pathlib
532532

533533
(Contributed by Barney Gale in :gh:`73991`.)
534534

535-
* Add :meth:`pathlib.Path.scandir` to scan a directory and return an iterator
536-
of :class:`os.DirEntry` objects. This is exactly equivalent to calling
537-
:func:`os.scandir` on a path object.
538-
539-
(Contributed by Barney Gale in :gh:`125413`.)
540-
541535

542536
pdb
543537
---

Lib/pathlib/_abc.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ class PathGlobber(_GlobberBase):
9494

9595
lexists = operator.methodcaller('exists', follow_symlinks=False)
9696
add_slash = operator.methodcaller('joinpath', '')
97-
scandir = operator.methodcaller('scandir')
97+
scandir = operator.methodcaller('_scandir')
9898

9999
@staticmethod
100100
def concat_path(path, text):
@@ -632,23 +632,22 @@ def write_text(self, data, encoding=None, errors=None, newline=None):
632632
with self.open(mode='w', encoding=encoding, errors=errors, newline=newline) as f:
633633
return f.write(data)
634634

635-
def scandir(self):
636-
"""Yield os.DirEntry objects of the directory contents.
635+
def _scandir(self):
636+
"""Yield os.DirEntry-like objects of the directory contents.
637637
638638
The children are yielded in arbitrary order, and the
639639
special entries '.' and '..' are not included.
640640
"""
641-
raise UnsupportedOperation(self._unsupported_msg('scandir()'))
641+
import contextlib
642+
return contextlib.nullcontext(self.iterdir())
642643

643644
def iterdir(self):
644645
"""Yield path objects of the directory contents.
645646
646647
The children are yielded in arbitrary order, and the
647648
special entries '.' and '..' are not included.
648649
"""
649-
with self.scandir() as entries:
650-
names = [entry.name for entry in entries]
651-
return map(self.joinpath, names)
650+
raise UnsupportedOperation(self._unsupported_msg('iterdir()'))
652651

653652
def _glob_selector(self, parts, case_sensitive, recurse_symlinks):
654653
if case_sensitive is None:
@@ -698,7 +697,7 @@ def walk(self, top_down=True, on_error=None, follow_symlinks=False):
698697
if not top_down:
699698
paths.append((path, dirnames, filenames))
700699
try:
701-
with path.scandir() as entries:
700+
with path._scandir() as entries:
702701
for entry in entries:
703702
name = entry.name
704703
try:

Lib/pathlib/_local.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -634,8 +634,8 @@ def _filter_trailing_slash(self, paths):
634634
path_str = path_str[:-1]
635635
yield path_str
636636

637-
def scandir(self):
638-
"""Yield os.DirEntry objects of the directory contents.
637+
def _scandir(self):
638+
"""Yield os.DirEntry-like objects of the directory contents.
639639
640640
The children are yielded in arbitrary order, and the
641641
special entries '.' and '..' are not included.

Lib/test/test_pathlib/test_pathlib_abc.py

Lines changed: 9 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import collections
2-
import contextlib
32
import io
43
import os
54
import errno
@@ -1418,24 +1417,6 @@ def close(self):
14181417
'st_mode st_ino st_dev st_nlink st_uid st_gid st_size st_atime st_mtime st_ctime')
14191418

14201419

1421-
class DummyDirEntry:
1422-
"""
1423-
Minimal os.DirEntry-like object. Returned from DummyPath.scandir().
1424-
"""
1425-
__slots__ = ('name', '_is_symlink', '_is_dir')
1426-
1427-
def __init__(self, name, is_symlink, is_dir):
1428-
self.name = name
1429-
self._is_symlink = is_symlink
1430-
self._is_dir = is_dir
1431-
1432-
def is_symlink(self):
1433-
return self._is_symlink
1434-
1435-
def is_dir(self, *, follow_symlinks=True):
1436-
return self._is_dir and (follow_symlinks or not self._is_symlink)
1437-
1438-
14391420
class DummyPath(PathBase):
14401421
"""
14411422
Simple implementation of PathBase that keeps files and directories in
@@ -1503,25 +1484,14 @@ def open(self, mode='r', buffering=-1, encoding=None,
15031484
stream = io.TextIOWrapper(stream, encoding=encoding, errors=errors, newline=newline)
15041485
return stream
15051486

1506-
@contextlib.contextmanager
1507-
def scandir(self):
1508-
path = self.resolve()
1509-
path_str = str(path)
1510-
if path_str in self._files:
1511-
raise NotADirectoryError(errno.ENOTDIR, "Not a directory", path_str)
1512-
elif path_str in self._directories:
1513-
yield iter([path.joinpath(name)._dir_entry for name in self._directories[path_str]])
1487+
def iterdir(self):
1488+
path = str(self.resolve())
1489+
if path in self._files:
1490+
raise NotADirectoryError(errno.ENOTDIR, "Not a directory", path)
1491+
elif path in self._directories:
1492+
return iter([self / name for name in self._directories[path]])
15141493
else:
1515-
raise FileNotFoundError(errno.ENOENT, "File not found", path_str)
1516-
1517-
@property
1518-
def _dir_entry(self):
1519-
path_str = str(self)
1520-
is_symlink = path_str in self._symlinks
1521-
is_directory = (path_str in self._directories
1522-
if not is_symlink
1523-
else self._symlinks[path_str][1])
1524-
return DummyDirEntry(self.name, is_symlink, is_directory)
1494+
raise FileNotFoundError(errno.ENOENT, "File not found", path)
15251495

15261496
def mkdir(self, mode=0o777, parents=False, exist_ok=False):
15271497
path = str(self.parent.resolve() / self.name)
@@ -2214,9 +2184,9 @@ def test_iterdir_nodir(self):
22142184

22152185
def test_scandir(self):
22162186
p = self.cls(self.base)
2217-
with p.scandir() as entries:
2187+
with p._scandir() as entries:
22182188
self.assertTrue(list(entries))
2219-
with p.scandir() as entries:
2189+
with p._scandir() as entries:
22202190
for entry in entries:
22212191
child = p / entry.name
22222192
self.assertIsNotNone(entry)

Misc/NEWS.d/3.14.0a2.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -597,7 +597,7 @@ TypeError is now raised instead of ValueError for some logical errors.
597597
.. nonce: Jat5kq
598598
.. section: Library
599599
600-
Add :meth:`pathlib.Path.scandir` method to efficiently fetch directory
600+
Add :meth:`!pathlib.Path.scandir` method to efficiently fetch directory
601601
children and their file attributes. This is a trivial wrapper of
602602
:func:`os.scandir`.
603603

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Revert addition of :meth:`!pathlib.Path.scandir`. This method was added in
2+
3.14.0a2. The optimizations remain for file system paths, but other
3+
subclasses should only have to implement :meth:`pathlib.Path.iterdir`.

0 commit comments

Comments
 (0)