Test CSV report output against symlink and hardlink aliases
Also published in our Blogger archive.
Quick answer
A report path can look new while already being an alias for an existing CSV. Test both a hardlink and a symlink because they create different kinds of aliases: a hardlink is another directory entry for the same file, while a symlink stores a reference to a target path. Neither alias should be opened in exclusive creation mode.
Example
The fixture creates source.csv, then makes one hardlink and one symlink with report-like names. exclusive_result uses Path.open("x"), which asks the filesystem to create a new directory entry and returns exists when either alias is already present. The assertion proves both cases fail and rereads the source to show no replacement occurred.
from pathlib import Path
from tempfile import TemporaryDirectory
def exclusive_result(path):
try:
path.open("x", encoding="utf-8").close()
except FileExistsError:
return "exists"
return "created"
with TemporaryDirectory() as directory:
root = Path(directory)
source = root / "source.csv"
source.write_text("id\n1\n", encoding="utf-8")
hardlink = root / "hardlink-report.json"
hardlink.hardlink_to(source)
symlink = root / "symlink-report.json"
symlink.symlink_to(source)
results = [exclusive_result(hardlink), exclusive_result(symlink)]
assert results == ["exists", "exists"]
assert source.read_text(encoding="utf-8") == "id\n1\n"
print("hardlink=exists symlink=exists")
Expected stdout:
hardlink=exists symlink=exists
Reading the result
This test is about avoiding replacement of an existing path entry; it does not prove that a newly created report could never be redirected later by another process. Keep report creation and subsequent writing in the same controlled workflow. On platforms where symlink creation requires a privilege or is unavailable, mark that case as unsupported rather than pretending the hardlink test covered it.
String comparison is not a substitute for this check. source.csv, hardlink-report.json, and symlink-report.json are different spellings, yet exclusive creation rejects the latter two because they already exist. If a tool needs to explain the relationship, it may inspect resolved paths separately, but creation safety comes from the filesystem operation.
Sources
- Python pathlib Path.hardlink_to documentation
- Python pathlib Path.symlink_to documentation
- Python pathlib Path.open documentation
Prepared with AI assistance. The example uses synthetic data; its stated limits apply.