pathlib cheatsheet — use this instead of os.path in modern Python.

Construct

from pathlib import Path

p = Path("/var/log/app.log")
p = Path("data") / "users.csv"        # joining
p = Path.home() / ".config" / "app"
p = Path.cwd()                         # current dir

Common paths

Path.home()                            # ~
Path.cwd()                             # current working dir
Path(__file__).parent                  # dir of current file
Path(__file__).resolve()               # absolute path

Properties

p = Path("/var/log/app.log")
p.name              # "app.log"
p.stem              # "app"
p.suffix            # ".log"
p.suffixes          # [".log"]
p.parent            # Path("/var/log")
p.parts             # ("/", "var", "log", "app.log")
p.anchor            # "/"

Checks

p.exists()
p.is_file()
p.is_dir()
p.is_symlink()
p.is_absolute()

Read / write

# Text
p.read_text(encoding="utf-8")
p.write_text("hello", encoding="utf-8")

# Bytes
data = p.read_bytes()
p.write_bytes(b"...")

# Lines
for line in p.read_text().splitlines():
    ...

Open as context manager

with p.open("r") as f:
    content = f.read()

with p.open("wb") as f:
    f.write(b"data")

Create / delete

p.mkdir()
p.mkdir(parents=True, exist_ok=True)    # like mkdir -p
p.unlink()                              # delete file
p.unlink(missing_ok=True)
p.rmdir()                               # remove empty dir

import shutil
shutil.rmtree(p)                        # remove dir + contents

Move / rename

p.rename("new_name.txt")
p.replace("new_name.txt")               # overwrites destination

# Within same FS
new = p.parent / "new_name.txt"
p.rename(new)

Copy

import shutil
shutil.copy(src, dst)
shutil.copy2(src, dst)                  # preserves metadata
shutil.copytree(src_dir, dst_dir)

Traverse

# Direct children
for child in p.iterdir():
    print(child)

# Glob
for f in p.glob("*.txt"):
    ...

# Recursive
for f in p.rglob("*.py"):
    ...

# Glob with patterns
list(p.glob("**/*.json"))               # all .json recursive
list(p.glob("data/[0-9]*.csv"))         # data/0.csv, data/1.csv, ...

Stat / metadata

s = p.stat()
s.st_size           # bytes
s.st_mtime          # modified time (epoch)

from datetime import datetime
datetime.fromtimestamp(s.st_mtime)

Match patterns

p.match("*.txt")              # bool
p.match("**/*.py")
p.resolve()                   # absolute, symlinks resolved
p.absolute()                  # absolute, symlinks not resolved
p.expanduser()                # ~ → home dir

Common patterns

# Read JSON
import json
data = json.loads(p.read_text())
p.write_text(json.dumps(data))

# Read CSV
import csv
with p.open() as f:
    reader = csv.DictReader(f)
    for row in reader: ...

# Touch (create empty file or update mtime)
p.touch()
p.touch(exist_ok=False)       # error if exists

# Walk dir tree (like os.walk)
for root, dirs, files in os.walk(p):
    ...

# Or with pathlib
def walk(p):
    for child in p.iterdir():
        yield child
        if child.is_dir():
            yield from walk(child)

Cross-platform

Path("/var/log/app.log")           # works on Linux/Mac
Path("C:/Users/me/file.txt")       # works on Windows
Path("a") / "b" / "c"              # forward slashes auto

# Force POSIX or Windows
from pathlib import PurePosixPath, PureWindowsPath

Tmp paths

import tempfile

with tempfile.TemporaryDirectory() as tmp:
    p = Path(tmp) / "x.txt"
    p.write_text("hi")
# dir cleaned up

with tempfile.NamedTemporaryFile(delete=False) as f:
    f.write(b"data")
    name = f.name

Common mistakes

  • Mixing os.path and pathlib — pick one.
  • str(p) + "/sub" — use p / "sub".
  • p.unlink() without missing_ok=True — error if missing.
  • glob("*.py") when you want recursive — use rglob.

Read this next

If you want my pathlib utilities library, it’s at rajpoot.dev .


Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .