Paths & the File System

Working with files means working with paths: joining folder names, finding a file's extension, listing a directory, creating folders. The pathlib module represents paths as objects with clear methods, and works the same on Windows, macOS and Linux.

Path objects

PY path.py
from pathlib import Path

p = Path("projects") / "reports" / "2026-q3.final.csv"

print(p)
print(p.name)
print(p.stem)
print(p.suffix, p.suffixes)
print(p.parent)
print(p.parts)
print(p.with_suffix(".xlsx"))
Output
projects/reports/2026-q3.final.csv
2026-q3.final.csv
2026-q3.final
.csv ['.final', '.csv']
projects/reports
('projects', 'reports', '2026-q3.final.csv')
projects/reports/2026-q3.final.xlsx

The / operator joins path parts using the correct separator for the operating system — \ on Windows, / elsewhere. No more gluing strings together.

Useful locations

PY locations.py
from pathlib import Path

print(Path.cwd())              # current working directory
print(Path.home())             # the user's home folder
print(Path(__file__).parent)   # the folder containing this script

Building paths relative to Path(__file__).parent makes a script find its data files no matter which directory it's run from.

Creating, reading and writing

PY read_write.py
from pathlib import Path

folder = Path("demo/output")
folder.mkdir(parents=True, exist_ok=True)

report = folder / "report.txt"
report.write_text("Total: 42\n", encoding="utf-8")

print(report.exists(), report.is_file(), folder.is_dir())
print(report.read_text(encoding="utf-8").strip())
print(report.stat().st_size, "bytes")
Output
True True True
Total: 42
10 bytes

mkdir(parents=True, exist_ok=True) creates any missing parent folders and doesn't complain if the folder already exists. write_text and read_text are one-line shortcuts for small files.

Listing and searching

PY listing.py
from pathlib import Path

base = Path("site")
for name in ["index.html", "about.html", "css/main.css", "img/logo.png", "img/icons/x.svg"]:
    path = base / name
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text("…", encoding="utf-8")

print(sorted(p.name for p in base.iterdir()))
print(sorted(p.name for p in base.glob("*.html")))
print(sorted(str(p.relative_to(base)) for p in base.rglob("*") if p.is_file() and p.suffix in {".png", ".svg"}))
Output
['about.html', 'css', 'img', 'index.html']
['about.html', 'index.html']
['img/icons/x.svg', 'img/logo.png']
  • iterdir() lists a folder's direct contents.
  • glob("*.html") matches a pattern in one folder; rglob searches all subfolders recursively.
  • relative_to shows a path relative to another.

Renaming, moving and deleting

PY manage.py
from pathlib import Path
import shutil

work = Path("work")
work.mkdir(exist_ok=True)
draft = work / "draft.txt"
draft.write_text("v1", encoding="utf-8")

final = draft.rename(work / "final.txt")
backup = Path(shutil.copy(final, work / "final.bak"))
print(sorted(p.name for p in work.iterdir()))

backup.unlink()
shutil.rmtree(work)
print(work.exists())
Output
['final.bak', 'final.txt']
False
TaskUse
Rename or move a filepath.rename(new) or shutil.move(src, dst)
Copy a file / a whole foldershutil.copy(src, dst) / shutil.copytree(src, dst)
Delete a filepath.unlink(missing_ok=True)
Delete an empty folder / a folder and everything in itpath.rmdir() / shutil.rmtree(path)
Careful: shutil.rmtree deletes everything inside a folder permanently, without using the recycle bin. Double-check the path before running it.

The os module

Older code uses os and os.path for the same jobs. You'll still use os for environment variables and process information:

PY os_module.py
import os

os.environ["APP_MODE"] = "test"
print(os.environ.get("APP_MODE"))
print(os.environ.get("MISSING_VAR", "default value"))
print(os.path.join("a", "b", "c.txt"))
print(os.path.splitext("photo.jpeg"))
Output
test
default value
a/b/c.txt
('photo', '.jpeg')

Environment variables are the standard way to pass configuration — like API keys and database URLs — to a program without hard-coding them.

Temporary files

PY tempfiles.py
import tempfile
from pathlib import Path

with tempfile.TemporaryDirectory() as tmp:
    scratch = Path(tmp) / "scratch.txt"
    scratch.write_text("temporary data", encoding="utf-8")
    print(scratch.read_text(encoding="utf-8"))

print(scratch.exists())
Output
temporary data
False

The temporary folder and everything in it are deleted when the with block ends — ideal for tests and intermediate files.

Try it yourself

Try it yourself
Console output