mirror of
https://github.com/python/cpython.git
synced 2025-09-27 02:39:58 +00:00
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>
This commit is contained in:
parent
f4f530804b
commit
8b3cccf3f9
7 changed files with 22 additions and 85 deletions
|
@ -1289,35 +1289,6 @@ Reading directories
|
||||||
raised.
|
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)
|
.. method:: Path.glob(pattern, *, case_sensitive=None, recurse_symlinks=False)
|
||||||
|
|
||||||
Glob the given relative *pattern* in the directory represented by this path,
|
Glob the given relative *pattern* in the directory represented by this path,
|
||||||
|
|
|
@ -532,12 +532,6 @@ pathlib
|
||||||
|
|
||||||
(Contributed by Barney Gale in :gh:`73991`.)
|
(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
|
pdb
|
||||||
---
|
---
|
||||||
|
|
|
@ -94,7 +94,7 @@ class PathGlobber(_GlobberBase):
|
||||||
|
|
||||||
lexists = operator.methodcaller('exists', follow_symlinks=False)
|
lexists = operator.methodcaller('exists', follow_symlinks=False)
|
||||||
add_slash = operator.methodcaller('joinpath', '')
|
add_slash = operator.methodcaller('joinpath', '')
|
||||||
scandir = operator.methodcaller('scandir')
|
scandir = operator.methodcaller('_scandir')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def concat_path(path, text):
|
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:
|
with self.open(mode='w', encoding=encoding, errors=errors, newline=newline) as f:
|
||||||
return f.write(data)
|
return f.write(data)
|
||||||
|
|
||||||
def scandir(self):
|
def _scandir(self):
|
||||||
"""Yield os.DirEntry objects of the directory contents.
|
"""Yield os.DirEntry-like objects of the directory contents.
|
||||||
|
|
||||||
The children are yielded in arbitrary order, and the
|
The children are yielded in arbitrary order, and the
|
||||||
special entries '.' and '..' are not included.
|
special entries '.' and '..' are not included.
|
||||||
"""
|
"""
|
||||||
raise UnsupportedOperation(self._unsupported_msg('scandir()'))
|
import contextlib
|
||||||
|
return contextlib.nullcontext(self.iterdir())
|
||||||
|
|
||||||
def iterdir(self):
|
def iterdir(self):
|
||||||
"""Yield path objects of the directory contents.
|
"""Yield path objects of the directory contents.
|
||||||
|
@ -646,9 +647,7 @@ class PathBase(PurePathBase):
|
||||||
The children are yielded in arbitrary order, and the
|
The children are yielded in arbitrary order, and the
|
||||||
special entries '.' and '..' are not included.
|
special entries '.' and '..' are not included.
|
||||||
"""
|
"""
|
||||||
with self.scandir() as entries:
|
raise UnsupportedOperation(self._unsupported_msg('iterdir()'))
|
||||||
names = [entry.name for entry in entries]
|
|
||||||
return map(self.joinpath, names)
|
|
||||||
|
|
||||||
def _glob_selector(self, parts, case_sensitive, recurse_symlinks):
|
def _glob_selector(self, parts, case_sensitive, recurse_symlinks):
|
||||||
if case_sensitive is None:
|
if case_sensitive is None:
|
||||||
|
@ -698,7 +697,7 @@ class PathBase(PurePathBase):
|
||||||
if not top_down:
|
if not top_down:
|
||||||
paths.append((path, dirnames, filenames))
|
paths.append((path, dirnames, filenames))
|
||||||
try:
|
try:
|
||||||
with path.scandir() as entries:
|
with path._scandir() as entries:
|
||||||
for entry in entries:
|
for entry in entries:
|
||||||
name = entry.name
|
name = entry.name
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -634,8 +634,8 @@ class Path(PathBase, PurePath):
|
||||||
path_str = path_str[:-1]
|
path_str = path_str[:-1]
|
||||||
yield path_str
|
yield path_str
|
||||||
|
|
||||||
def scandir(self):
|
def _scandir(self):
|
||||||
"""Yield os.DirEntry objects of the directory contents.
|
"""Yield os.DirEntry-like objects of the directory contents.
|
||||||
|
|
||||||
The children are yielded in arbitrary order, and the
|
The children are yielded in arbitrary order, and the
|
||||||
special entries '.' and '..' are not included.
|
special entries '.' and '..' are not included.
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import collections
|
import collections
|
||||||
import contextlib
|
|
||||||
import io
|
import io
|
||||||
import os
|
import os
|
||||||
import errno
|
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')
|
'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):
|
class DummyPath(PathBase):
|
||||||
"""
|
"""
|
||||||
Simple implementation of PathBase that keeps files and directories in
|
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)
|
stream = io.TextIOWrapper(stream, encoding=encoding, errors=errors, newline=newline)
|
||||||
return stream
|
return stream
|
||||||
|
|
||||||
@contextlib.contextmanager
|
def iterdir(self):
|
||||||
def scandir(self):
|
path = str(self.resolve())
|
||||||
path = self.resolve()
|
if path in self._files:
|
||||||
path_str = str(path)
|
raise NotADirectoryError(errno.ENOTDIR, "Not a directory", path)
|
||||||
if path_str in self._files:
|
elif path in self._directories:
|
||||||
raise NotADirectoryError(errno.ENOTDIR, "Not a directory", path_str)
|
return iter([self / name for name in self._directories[path]])
|
||||||
elif path_str in self._directories:
|
|
||||||
yield iter([path.joinpath(name)._dir_entry for name in self._directories[path_str]])
|
|
||||||
else:
|
else:
|
||||||
raise FileNotFoundError(errno.ENOENT, "File not found", path_str)
|
raise FileNotFoundError(errno.ENOENT, "File not found", path)
|
||||||
|
|
||||||
@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)
|
|
||||||
|
|
||||||
def mkdir(self, mode=0o777, parents=False, exist_ok=False):
|
def mkdir(self, mode=0o777, parents=False, exist_ok=False):
|
||||||
path = str(self.parent.resolve() / self.name)
|
path = str(self.parent.resolve() / self.name)
|
||||||
|
@ -2214,9 +2184,9 @@ class DummyPathTest(DummyPurePathTest):
|
||||||
|
|
||||||
def test_scandir(self):
|
def test_scandir(self):
|
||||||
p = self.cls(self.base)
|
p = self.cls(self.base)
|
||||||
with p.scandir() as entries:
|
with p._scandir() as entries:
|
||||||
self.assertTrue(list(entries))
|
self.assertTrue(list(entries))
|
||||||
with p.scandir() as entries:
|
with p._scandir() as entries:
|
||||||
for entry in entries:
|
for entry in entries:
|
||||||
child = p / entry.name
|
child = p / entry.name
|
||||||
self.assertIsNotNone(entry)
|
self.assertIsNotNone(entry)
|
||||||
|
|
|
@ -597,7 +597,7 @@ TypeError is now raised instead of ValueError for some logical errors.
|
||||||
.. nonce: Jat5kq
|
.. nonce: Jat5kq
|
||||||
.. section: Library
|
.. 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
|
children and their file attributes. This is a trivial wrapper of
|
||||||
:func:`os.scandir`.
|
:func:`os.scandir`.
|
||||||
|
|
||||||
|
|
|
@ -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`.
|
Loading…
Add table
Add a link
Reference in a new issue