19. The standard library you will use every week
Python ships "batteries included". Before you uv add a package, check
whether the standard library already does it.
19.1 Dates, times, and time zones
from datetime import datetime, timedelta, timezone
from zoneinfo import ZoneInfo
now = datetime.now(timezone.utc) # always store and compare in UTC
blr = now.astimezone(ZoneInfo("Asia/Kolkata")) # convert only for display
print(now.isoformat(timespec="seconds"), blr.strftime("%Y-%m-%d %H:%M %Z"))
deadline = now + timedelta(days=7)
print((deadline - now).total_seconds())
print(datetime.fromisoformat("2026-10-04T10:15:00+00:00"))
datetime.now() < now
# TypeError: can't compare offset-naive and offset-aware datetimes
Never use naive datetimes (no time zone) for anything that crosses a
machine boundary. For measuring durations, use time.perf_counter() or
time.monotonic(), never wall-clock time, which jumps when NTP corrects it.
19.2 logging
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s %(name)s %(message)s",
)
log = logging.getLogger("billing") # one logger per module: getLogger(__name__)
log.info("charging %s cents to %s", 1500, "acct-7") # lazy %-formatting, not f-strings
try:
1 / 0
except ZeroDivisionError:
log.exception("charge failed") # ERROR level + full traceback
Libraries call logging.getLogger(__name__) and never configure logging;
applications configure it once at startup. Chapter 68 turns this into
structured JSON logs with request IDs.
19.3 subprocess, shutil, tempfile, os
import shutil
import subprocess
import tempfile
from pathlib import Path
result = subprocess.run(
["git", "--version"], capture_output=True, text=True, check=False, timeout=10
)
print(result.returncode, result.stdout.strip())
with tempfile.TemporaryDirectory() as tmp:
src = Path(tmp) / "a.txt"
src.write_text("x")
shutil.copy2(src, Path(tmp) / "b.txt") # copy with metadata
print(sorted(p.name for p in Path(tmp).iterdir()))
print(shutil.which("python3"), shutil.disk_usage("/").free // 2**30, "GiB free")
Chapter 34 covers subprocess properly, including why shell=True is
dangerous.
19.4 re: regular expressions
import re
ipv4 = re.compile(r"\b(?:\d{1,3}\.){3}\d{1,3}\b")
text = "client 10.0.0.7 -> 203.0.113.9 port 443"
print(ipv4.findall(text)) # ['10.0.0.7', '203.0.113.9']
m = re.fullmatch(r"(?P<user>[a-z_][a-z0-9_-]{0,31})", "deploy_bot")
print(m["user"] if m else "invalid")
print(re.sub(r"(?i)password=\S+", "password=***", "login user=ada password=hunter2"))
Always use raw strings (r"...") for patterns. Beware of catastrophic
backtracking on untrusted input ((a+)+$), a denial-of-service class
covered in Ch 47.
19.5 The rest of the essentials
| Module | Use it for |
|---|---|
argparse |
command-line interfaces (Ch 11) |
pathlib, shutil, glob |
files and directories |
json, csv, tomllib, sqlite3 |
data formats and an embedded database |
collections |
Counter, defaultdict, deque, OrderedDict, ChainMap |
itertools, functools, operator |
iteration, caching, partial application |
dataclasses, enum, typing |
data modeling |
hashlib, hmac, secrets |
hashing, MACs, secure random tokens (Ch 48) |
concurrent.futures, threading, multiprocessing, asyncio |
concurrency (Part III) |
socket, ssl, selectors, http.client, urllib.parse |
networking (Part V) |
statistics, math, random, decimal, fractions |
numbers |
heapq, bisect |
priority queues, sorted-list search |
contextlib |
context-manager helpers |
unittest.mock |
test doubles (Ch 20) |
cProfile, tracemalloc, timeit, pdb |
profiling and debugging (Ch 31, 69) |