A DirEntry can cache stat data even when the named file changes afterward.
Python os.scandir metadata: a cached DirEntry is not a fresh file check
Operation contract
The fixture scans an owned temporary directory, obtains a receipt file's size, rewrites the file with a longer payload, and reads its cached DirEntry size again. A fresh os.stat call reports the new size. The comparison isolates metadata caching from the file's current bytes.
Failure boundary
A fresh stat still does not make a later open atomic with that check. Concurrent replacement or symlink changes require descriptor-based ownership rules, not an earlier filename test. This program changes its own file and makes no claim about hostile filesystem races.
Working program
import os
from pathlib import Path
from tempfile import TemporaryDirectory
with TemporaryDirectory() as directory:
receipt_path = Path(directory) / "receipt-47.state"
receipt_path.write_bytes(b"ok")
with os.scandir(directory) as entries:
receipt_entry = next(entry for entry in entries if entry.name == receipt_path.name)
before = receipt_entry.stat(follow_symlinks=False).st_size
receipt_path.write_bytes(b"paid")
cached = receipt_entry.stat(follow_symlinks=False).st_size
fresh = os.stat(receipt_entry.path, follow_symlinks=False).st_size
print("before", before)
print("cached", cached)
print("fresh", fresh)Output
before 2
cached 2
fresh 4Costs and limits
scandir avoids some metadata system calls during enumeration; a fresh stat incurs another call. Retaining DirEntry objects for later decisions is both stale-data risk and unnecessary memory retention.
Common Mistakes
- Do not store DirEntry metadata as a lasting file identity.
- A fresh stat followed by a separate open still permits a race.
- Directory enumeration order is unspecified; sort names when output order matters.
Connected lessons
- Python pathlib files: specify encoding and close the resource owner
- Python POSIX filename bytes: round-trip an undecodable path
- Python file replacement: write a sibling file before changing the name
Continue with Python POSIX dir_fd and O_NOFOLLOW: reject a final symlink on an owned open.
