_walk.py 1.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  1. from __future__ import annotations
  2. import typing
  3. from collections import deque
  4. from collections.abc import Collection, Iterator
  5. from ._path import combine
  6. if typing.TYPE_CHECKING:
  7. from typing import Callable
  8. from ._base import FS
  9. from ._info import Info
  10. class BoundWalker:
  11. def __init__(self, fs: FS):
  12. self._fs = fs
  13. def _iter_walk(
  14. self, path: str, namespaces: Collection[str] | None = None
  15. ) -> Iterator[tuple[str, Info | None]]:
  16. """Walk files using a *breadth first* search."""
  17. queue = deque([path])
  18. push = queue.appendleft
  19. pop = queue.pop
  20. _scan = self._fs.scandir
  21. _combine = combine
  22. while queue:
  23. dir_path = pop()
  24. for info in _scan(dir_path, namespaces=namespaces):
  25. if info.is_dir:
  26. yield dir_path, info
  27. push(_combine(dir_path, info.name))
  28. else:
  29. yield dir_path, info
  30. yield path, None
  31. def _filter(
  32. self,
  33. include: Callable[[str, Info], bool] = lambda path, info: True,
  34. path: str = "/",
  35. namespaces: Collection[str] | None = None,
  36. ) -> Iterator[str]:
  37. _combine = combine
  38. for path, info in self._iter_walk(path, namespaces):
  39. if info is not None and include(path, info):
  40. yield _combine(path, info.name)
  41. def files(self, path: str = "/") -> Iterator[str]:
  42. yield from self._filter(lambda _, info: info.is_file, path)
  43. def dirs(self, path: str = "/") -> Iterator[str]:
  44. yield from self._filter(lambda _, info: info.is_dir, path)