comparison cutils/util/walk.py @ 178:dac26a2d9de5

Cleanup: remove non used walk-code
author Franz Glasner <fzglas.hg@dom66.de>
date Sat, 11 Jan 2025 21:18:53 +0100
parents 089c40240061
children 188de62caac6
comparison
equal deleted inserted replaced
177:089c40240061 178:dac26a2d9de5
5 # :- 5 # :-
6 r"""Utility sub-module to implement a heavily customized :func:`os.walk`. 6 r"""Utility sub-module to implement a heavily customized :func:`os.walk`.
7 7
8 """ 8 """
9 9
10 __all__ = ["walk", 10 __all__ = ["ScanDir"]
11 "ScanDir"]
12 11
13 12
14 import os 13 import os
15 try: 14 try:
16 from os import scandir 15 from os import scandir
19 from scandir import scandir 18 from scandir import scandir
20 except ImportError: 19 except ImportError:
21 scandir = None 20 scandir = None
22 21
23 from . import PY2 22 from . import PY2
24 from .cm import nullcontext
25 23
26 24
27 class WalkDirEntry(object): 25 class WalkDirEntry(object):
28 26
29 """A :class:`os.DirEntry` alike to be used in :func:`walk` and for 27 """A :class:`os.DirEntry` alike to be used in :func:`walk` and for
140 @staticmethod 138 @staticmethod
141 def sort_key(entry): 139 def sort_key(entry):
142 return entry._fsname 140 return entry._fsname
143 141
144 142
145 def walk(root, follow_symlinks=False):
146 """A heyvily customized :func:`os.walk` alike that differs from the
147 original:
148
149 - optimized for use in :command:`treesum`
150 - most errors are not suppressed
151 - the `root` is never part of the returned data
152 - the returned directory in "top" is not a string form but a list of
153 individual path segments
154 - there is only one yielded list
155
156 * contains :class:`WalkDirEntry`
157 * sorted by its fsname
158
159 The caller can easily get the old dirs and nondirs by filtering
160 the yielded list using "entry.is_dir".
161
162 - recurse into sub-directories first ("topdown=False")
163 - sort consistently all yielded lists by the filesystem encoding
164
165 .. note:: The implementation is based on Python 3.11 and needs a
166 functional :func:`os.scandir` or :func:`scandir.scandir`
167 implementation. It intentionally follows the logic in
168 Python 3.11 while it could be simplified because we are not
169 implementing some of the original flags (e.g. like
170 `topdown`).
171
172 """
173 normed_root = os.path.normpath(root)
174 yield from _walk(normed_root, tuple(), follow_symlinks=follow_symlinks)
175
176
177 if scandir: 143 if scandir:
178 144
179 def _walk(root, top, follow_symlinks): 145 class ScanDir(object):
180 """:func:`walk` helper.
181
182 Implemented using :func:`os.scandir`.
183
184 """
185 if top:
186 path = os.path.join(root, *top)
187 else:
188 path = root
189
190 fsobjects, walk_dirs = [], []
191
192 scandir_cm = scandir(path)
193 if not hasattr(scandir_cm, "close"):
194 scandir_cm = nullcontext(scandir_cm)
195 with scandir_cm as scandir_it:
196 while True:
197 try:
198 entry = WalkDirEntry.from_direntry(next(scandir_it))
199 except StopIteration:
200 break
201 fsobjects.append(entry)
202 #
203 # Always bottom-up: recurse into sub-directories, but exclude
204 # symlinks to directories if follow_symlinks is False
205 #
206 if entry.is_dir:
207 if follow_symlinks:
208 walk_into = True
209 else:
210 walk_into = not entry.is_symlink
211 if walk_into:
212 walk_dirs.append(entry)
213
214 # Sort by low-level filesystem encoding
215 walk_dirs.sort(key=WalkDirEntry.sort_key)
216 fsobjects.sort(key=WalkDirEntry.sort_key)
217
218 # Recurse into sub-directories
219 for wd in walk_dirs:
220 yield from _walk(root, top + (wd.name,), follow_symlinks)
221 # Yield after recursion if going bottom up
222 yield top, fsobjects
223
224
225 class ScanDir(object): # noqa: E303 too many blank lines
226 146
227 """An :func:`os.scandir` wrapper that is always an iterator and 147 """An :func:`os.scandir` wrapper that is always an iterator and
228 a context manager. 148 a context manager.
229 149
230 """ 150 """
251 if hasattr(self._scandir_it, "close"): 171 if hasattr(self._scandir_it, "close"):
252 self._scandir_it.close() 172 self._scandir_it.close()
253 173
254 else: 174 else:
255 175
256 def _walk(root, top, follow_symlinks): 176 class ScanDir(object):
257 """:func:`walk` helper.
258
259 Implemented using :func:`os.listdir`.
260
261 """
262 if top:
263 path = os.path.join(root, *top)
264 else:
265 path = root
266
267 fsobjects, walk_dirs = [], []
268
269 names = os.listdir(path)
270 for name in names:
271 entry = WalkDirEntry.from_path_name(path, name)
272 fsobjects.append(entry)
273 #
274 # Always bottom-up: recurse into sub-directories, but exclude
275 # symlinks to directories if follow_symlinks is False
276 #
277 if entry.is_dir:
278 if follow_symlinks:
279 walk_into = True
280 else:
281 walk_into = not entry.is_symlink
282 if walk_into:
283 walk_dirs.append(entry)
284
285 # Sort by low-level filesystem encoding
286 walk_dirs.sort(key=WalkDirEntry.sort_key)
287 fsobjects.sort(key=WalkDirEntry.sort_key)
288
289 # Recurse into sub-directories
290 for wd in walk_dirs:
291 yield from _walk(root, top + (wd.name,), follow_symlinks)
292 # Yield after recursion if going bottom up
293 yield top, fsobjects
294
295
296 class ScanDir(object): # noqa: E303 too many blank lines
297 177
298 """An :func:`os.scandir` wrapper that is always an iterator and 178 """An :func:`os.scandir` wrapper that is always an iterator and
299 a context manager. 179 a context manager.
300 180
301 """ 181 """