Symlink-Safe Directory Walk
Walk a directory without following links that escape the root, recording skipped entries and enforcing a depth limit.
Code
import os
from pathlib import Path
def walk_confined(root: str, *, max_depth: int = 32) -> tuple[list[str], list[str]]:
root_path = Path(root).resolve(strict=True)
if not root_path.is_dir():
raise NotADirectoryError(root)
files: list[str] = []
skipped: list[str] = []
stack: list[tuple[Path, int]] = [(root_path, 0)]
while stack:
directory, depth = stack.pop()
if depth >= max_depth:
skipped.append(str(directory.relative_to(root_path)))
continue
with os.scandir(directory) as entries:
ordered = sorted(entries, key=lambda entry: entry.name)
for entry in ordered:
entry_path = Path(entry.path)
if entry.is_symlink():
try:
target = entry_path.resolve(strict=True)
except OSError:
skipped.append(str(entry_path.relative_to(root_path)))
continue
if target.is_relative_to(root_path) and target.is_file():
files.append(str(entry_path.relative_to(root_path)))
else:
skipped.append(str(entry_path.relative_to(root_path)))
elif entry.is_dir(follow_symlinks=False):
stack.append((entry_path, depth + 1))
elif entry.is_file(follow_symlinks=False):
files.append(str(entry_path.relative_to(root_path)))
return sorted(files), sorted(skipped)
def main() -> None:
root = Path(__file__).resolve().parent
files, skipped = walk_confined(str(root), max_depth=2)
print(f"files: {len(files)}")
for name in files[:5]:
print(" ", Path(name).as_posix())
print(f"skipped: {len(skipped)}")
if __name__ == "__main__":
main()Report a problem with this exampleOpens a pre-filled issue on GitHub.