Python pathlib: make scripts portable across computers
In this article (6 sections)
Use pathlib to express paths as filesystem objects, then decide explicitly what relative paths mean. Replacing backslashes with forward slashes does not by itself make an analysis portable: the working directory, input contract and output permissions still matter.
A useful portable script accepts user data paths as arguments and locates bundled resources relative to its own module when appropriate. It does not assume that every colleague has the author's Downloads folder.
Distinguish two path roots
In a script, Path.cwd() represents the process's current working directory. Path(__file__).resolve().parent identifies the script's directory. They can differ when a scheduler launches a script from elsewhere.
The Python reporting lab accepts --input and --output paths. Relative arguments are intentionally interpreted from the caller's working directory. Its README therefore tells the reader where to run the command.
Bundled reference files could instead be located beside the script. Do not apply that rule indiscriminately to user-supplied data: a caller who passes reports/january.csv usually expects that argument to refer to their chosen workspace.
Build paths without manual separators
from pathlib import Path
from tempfile import TemporaryDirectory
with TemporaryDirectory() as directory:
root = Path(directory)
output = root / "monthly reports" / "summary.txt"
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text("January: INR 47.50\n", encoding="utf-8")
assert output.read_text(encoding="utf-8") == "January: INR 47.50\n"
assert output.name == "summary.txt"
assert output.suffix == ".txt"
print("Path with spaces round-tripped successfully")The slash operator joins path components using the platform's path rules. The pathlib reference documents path construction and file methods. A path object does not guarantee that the destination exists or is writable; the actual operation can still fail.
Protect the source from output collisions
A reporting script should not overwrite its source because input and output arguments happen to resolve to the same location. The lab compares its input path with each intended output file before writing.
from pathlib import Path
from tempfile import TemporaryDirectory
from report import run
with TemporaryDirectory() as directory:
output = Path(directory)
source = output / "accepted_orders.csv"
original = Path("raw_orders.csv").read_bytes()
source.write_bytes(original)
try:
run(source, output, "2026-01")
except ValueError as error:
assert str(error) == "output_would_overwrite_input"
else:
raise AssertionError("Input/output collision was accepted")
assert source.read_bytes() == original
print("Input preserved after collision rejection")This is a useful local guard, not a complete defense against concurrent filesystem changes, hard-link aliases or adversarial directory manipulation. For this exercise, its tested promise is narrow: the explicitly resolved input destination is not one of the output filenames.
Handle files as operations, not assumptions
An exists() check can improve an error message, but a file can change between checking and opening. Keep exception handling around the read or write itself. A PermissionError should not be converted into an empty dataset.
Use explicit encodings for text and bytes methods for hashes or binary content. In the lab, the same input bytes are hashed and parsed so the summary identifies the content actually read.
Avoid changing the process's working directory inside a reusable calculation function. That global side effect can change the meaning of paths elsewhere in the program. Pass Path objects into the function instead.
Test portability with changed surroundings
Copy the lab to a folder whose name contains spaces, invoke it from a different directory with explicit paths, and compare the summary's business values. The absolute location may change; the source hash and accepted totals should remain consistent for identical bytes.
Exercise: create a temporary source and output tree, run a successful report, then try a nonexistent input. Verify that the second run fails visibly and that the original source remains byte-for-byte unchanged.
NeuraPath's Data Analytics with Generative AI course connects Python file handling with reproducibility. A portable report makes its path assumptions visible and leaves the source evidence intact.
Continue learning
This article is part of the Python foundations for analysts sequence. Use the neighbouring tasks when you need the prerequisite or the next application.
- Review the prerequisite or neighbouring task in Build a command-line report with argparse.
- Continue with Write useful logs for a scheduled analysis.
Pankit Kumar has 10 years in Data Science & AI, building and shipping production systems in regulated pharma and clinical environments. He is a freelance trainer at Boston Institute of Analytics, AnalytixLabs and Scaler, and has taught this material to thousands of working professionals.
This article is part of our Data Analytics with Generative AI programme — 3–4 months. The full analyst stack — Excel, SQL, Power BI and Python pipelines — then a generative-AI layer you can prove is right.
Explore Data Analytics with Generative AI