Skip to content

Commit

Permalink
pythonGH-119169: Implement pathlib.Path.walk() using os.walk() (p…
Browse files Browse the repository at this point in the history
…ython#119573)

For silly reasons, pathlib's generic implementation of `walk()` currently
resides in `glob._Globber`. This commit moves it into
`pathlib._abc.PathBase.walk()` where it really belongs, and makes
`pathlib.Path.walk()` call `os.walk()`.
  • Loading branch information
barneygale authored May 29, 2024
1 parent a150679 commit 7ff61f5
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 39 deletions.
37 changes: 0 additions & 37 deletions Lib/glob.py
Original file line number Diff line number Diff line change
Expand Up @@ -519,43 +519,6 @@ def select_exists(self, path, exists=False):
elif self.lexists(path):
yield path

@classmethod
def walk(cls, root, top_down, on_error, follow_symlinks):
"""Walk the directory tree from the given root, similar to os.walk().
"""
paths = [root]
while paths:
path = paths.pop()
if isinstance(path, tuple):
yield path
continue
try:
with cls.scandir(path) as scandir_it:
dirnames = []
filenames = []
if not top_down:
paths.append((path, dirnames, filenames))
for entry in scandir_it:
name = entry.name
try:
if entry.is_dir(follow_symlinks=follow_symlinks):
if not top_down:
paths.append(cls.parse_entry(entry))
dirnames.append(name)
else:
filenames.append(name)
except OSError:
filenames.append(name)
except OSError as error:
if on_error is not None:
on_error(error)
else:
if top_down:
yield path, dirnames, filenames
if dirnames:
prefix = cls.add_slash(path)
paths += [cls.concat_path(prefix, d) for d in reversed(dirnames)]


class _StringGlobber(_Globber):
lexists = staticmethod(os.path.lexists)
Expand Down
32 changes: 31 additions & 1 deletion Lib/pathlib/_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,37 @@ def rglob(self, pattern, *, case_sensitive=None, recurse_symlinks=True):

def walk(self, top_down=True, on_error=None, follow_symlinks=False):
"""Walk the directory tree from this directory, similar to os.walk()."""
return self._globber.walk(self, top_down, on_error, follow_symlinks)
paths = [self]
while paths:
path = paths.pop()
if isinstance(path, tuple):
yield path
continue
dirnames = []
filenames = []
if not top_down:
paths.append((path, dirnames, filenames))
try:
for child in path.iterdir():
try:
if child.is_dir(follow_symlinks=follow_symlinks):
if not top_down:
paths.append(child)
dirnames.append(child.name)
else:
filenames.append(child.name)
except OSError:
filenames.append(child.name)
except OSError as error:
if on_error is not None:
on_error(error)
if not top_down:
while not isinstance(paths.pop(), tuple):
pass
continue
if top_down:
yield path, dirnames, filenames
paths += [path.joinpath(d) for d in reversed(dirnames)]

def absolute(self):
"""Return an absolute version of this path
Expand Down
4 changes: 3 additions & 1 deletion Lib/pathlib/_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -672,7 +672,9 @@ def walk(self, top_down=True, on_error=None, follow_symlinks=False):
"""Walk the directory tree from this directory, similar to os.walk()."""
sys.audit("pathlib.Path.walk", self, on_error, follow_symlinks)
root_dir = str(self)
results = self._globber.walk(root_dir, top_down, on_error, follow_symlinks)
if not follow_symlinks:
follow_symlinks = os._walk_symlinks_as_files
results = os.walk(root_dir, top_down, on_error, follow_symlinks)
for path_str, dirnames, filenames in results:
if root_dir == '.':
path_str = path_str[2:]
Expand Down

0 comments on commit 7ff61f5

Please sign in to comment.