comparison cutils/util/walk.py @ 266:0add8276e6b8

treesum: Handle errors like broken symlinks properly
author Franz Glasner <fzglas.hg@dom66.de>
date Tue, 18 Feb 2025 12:39:04 +0100
parents 188f448ab5e9
children c02a57df2a29
comparison
equal deleted inserted replaced
265:188f448ab5e9 266:0add8276e6b8
51 its results. 51 its results.
52 52
53 """ 53 """
54 54
55 __slots__ = ("_name", "_path", # encoded as given in the ctor 55 __slots__ = ("_name", "_path", # encoded as given in the ctor
56 "_is_symlink", "_is_dir", "_stat_result", 56 "_is_symlink", "_is_dir", "_stat_result", "_stat_errno",
57 "_stat_errstr",
57 "_alt_fsname", "_alt_u8name") 58 "_alt_fsname", "_alt_u8name")
58 59
59 def __init__(self, name, path): 60 def __init__(self, name, path):
60 self._name = name # the name as given in the constructor 61 self._name = name # the name as given in the constructor
61 """The name exactly as given in the ctor""" 62 """The name exactly as given in the ctor"""
62 self._path = _unix_path(path) 63 self._path = _unix_path(path)
63 """The path as given in the ctor -- but normalized to have slashes""" 64 """The path as given in the ctor -- but normalized to have slashes"""
64 self._is_symlink = self._is_dir = self._stat_result = None 65 self._is_symlink = self._is_dir = self._stat_result = \
66 self._stat_errno = self._stat_errstr = None
65 self._alt_fsname = self._alt_u8name = _notset 67 self._alt_fsname = self._alt_u8name = _notset
66 68
67 @property 69 @property
68 def name(self): 70 def name(self):
69 """The original name exactly as given in the ctor""" 71 """The original name exactly as given in the ctor"""
230 232
231 @property 233 @property
232 def stat(self): 234 def stat(self):
233 return self._stat_result 235 return self._stat_result
234 236
237 @property
238 def stat_errno(self):
239 return self._stat_errno
240
241 @property
242 def stat_errstr(self):
243 return self._stat_errstr
244
235 def __repr__(self): 245 def __repr__(self):
236 tag = "" 246 tag = ""
237 if self._is_symlink: 247 if self._is_symlink:
238 tag += "l" 248 tag += "l"
239 if self._is_dir: 249 if self._is_dir:
259 # 269 #
260 # If is_symlink() raises an OSError, consider that the entry 270 # If is_symlink() raises an OSError, consider that the entry
261 # is not a symbolic link, same behaviour than os.path.islink(). 271 # is not a symbolic link, same behaviour than os.path.islink().
262 # 272 #
263 w._is_symlink = False 273 w._is_symlink = False
264 # Do not supress errors here and (consistently) follow symlinks 274 # Consistently follow symlinks
265 w._stat_result = entry.stat(follow_symlinks=True) 275 try:
276 w._stat_result = entry.stat(follow_symlinks=True)
277 except OSError as e:
278 w._stat_result = None
279 w._stat_errno = e.errno
280 w._stat_errstr = e.strerror
266 return w 281 return w
267 282
268 @classmethod 283 @classmethod
269 def from_path_name(cls_, path, name, _do_stat=True): 284 def from_path_name(cls_, path, name, _do_stat=True):
270 """`_do_stat` is to be used only for testing purposes""" 285 """`_do_stat` is to be used only for testing purposes"""
284 # If is_symlink() raises an OSError, consider that the entry 299 # If is_symlink() raises an OSError, consider that the entry
285 # is not a symbolic link, same behaviour than os.path.islink(). 300 # is not a symbolic link, same behaviour than os.path.islink().
286 # 301 #
287 w._is_symlink = False 302 w._is_symlink = False
288 if _do_stat: 303 if _do_stat:
289 w._stat_result = os.stat(w._path) 304 try:
305 w._stat_result = os.stat(w._path)
306 except OSError as e:
307 w._stat_result = None
308 w._stat_errno = e.errno
309 w._stat_errstr = e.strerror
290 return w 310 return w
291 311
292 @classmethod 312 @classmethod
293 def from_readlink(cls_, path): 313 def from_readlink(cls_, path):
294 w = cls_(os.path.basename(path), path) 314 w = cls_(os.path.basename(path), path)