Mercurial > hgrepos > Python > apps > py-cutils
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) |
