Python Basics: Inventory Directory Entries Without Following Symlinks
An inventory should state whether a symbolic link counts as a file or as a link. This example keeps those categories separate and sorts names so the report is repeatable.
Prepare a scratch directory
Tested on Linux with CPython 3.12.14. You need Bash, Python, a text editor, and the standard mktemp, rm, and rmdir utilities. No external packages are required. Run without -O so assertions remain enabled.
Keep the same terminal open. Stop if setup fails, before saving the source:
start_dir="$PWD"
work_dir="$(mktemp -d)" || exit 1
cd -- "$work_dir" || exit 1Run the example
Save this as scandir-symlinks-demo.py:
import os
from pathlib import Path
from tempfile import TemporaryDirectory
with TemporaryDirectory(prefix="inventory-") as directory:
root = Path(directory)
(root / "report.txt").write_bytes(b"ready\n")
(root / "archive").mkdir()
(root / "latest").symlink_to("report.txt")
(root / "missing").symlink_to("absent.txt")
with os.scandir(root) as entries:
for entry in sorted(entries, key=lambda item: item.name):
if entry.is_symlink():
kind = "symlink"
elif entry.is_file(follow_symlinks=False):
kind = f"file ({entry.stat(follow_symlinks=False).st_size} bytes)"
elif entry.is_dir(follow_symlinks=False):
kind = "directory"
else:
kind = "other"
print(f"{entry.name}: {kind}")
with os.scandir(root) as entries:
links = {entry.name: entry for entry in entries if entry.is_symlink()}
print("latest resolves to file:", links["latest"].is_file())
print("missing resolves to file:", links["missing"].is_file())Run it:
python3 -B scandir-symlinks-demo.pyObserved output:
archive: directory
latest: symlink
missing: symlink
report.txt: file (6 bytes)
latest resolves to file: True
missing resolves to file: FalseRead the result
Both latest and the dangling missing entry remain links in the inventory. The later checks intentionally use the default link-following behavior: one reaches the regular file and the other has no target. The six-byte size includes the newline written to report.txt.
Apply it carefully
Directory iteration order is unspecified, so sort when a stable report matters. A DirEntry can cache metadata; do not retain entries as a permanently current view. Concurrent deletion or permission changes can cause errors, and checking a path before opening it does not prevent a race. This is an inventory of a private fixture, not a secure file-opening policy.
The official os.scandir reference documents the APIs used here.
Clean up
Remove the saved source, return to the starting directory, and remove the empty practice directory:
rm -- scandir-symlinks-demo.py
cd -- "$start_dir" || exit 1
rmdir -- "$work_dir"
unset start_dir work_dirIf the directory is not empty, inspect its remaining contents before removing anything else.
Acknowledgements
This post was created with assistance from AI.