Source code for eb_staged_rebuild.paths

"""Path checks that do not follow symlinks."""

import os
import stat


[docs] def abspath(path): return os.path.abspath(os.path.normpath(os.fspath(path)))
[docs] def is_within(child, parent): child = abspath(child) parent = abspath(parent) if child == parent: return True prefix = parent.rstrip(os.sep) + os.sep return child.startswith(prefix)
[docs] def lstat_kind(path): """``missing``, ``symlink``, ``dir``, ``file``, or ``other``.""" if not os.path.lexists(path): return "missing" mode = os.lstat(path).st_mode if stat.S_ISLNK(mode): return "symlink" if stat.S_ISDIR(mode): return "dir" if stat.S_ISREG(mode): return "file" return "other"
[docs] def is_real_dir(path): return lstat_kind(path) == "dir"
[docs] def assert_real_components(path, stop_at=None): """Return a refusal reason if any existing component of ``path`` is a symlink. ``stop_at`` is an ancestor that is not itself checked (used when the caller already accepted the software root). ``None`` checks every existing component. """ path = abspath(path) stop = abspath(stop_at) if stop_at else None current = path while True: if stop is not None and current == stop: break kind = lstat_kind(current) if kind == "symlink": return f"{current} is a symlink" if current == os.path.dirname(current): break parent = os.path.dirname(current) if not os.path.lexists(current): current = parent continue current = parent return None