Chapter 5: Important Libraries - Interview Preparation Notes

Focus on libraries commonly used in DevOps/Cloud/Product roles and interviews.

Table of Contents

  1. os and pathlib (filesystem, environment)
  2. sys and argparse (CLI handling)
  3. datetime (dates, times, timezones)
  4. json (configs, serialization)
  5. re (regular expressions)
  6. logging (structured logs)
  7. requests (HTTP basics)
  8. Common Interview Questions
  9. Practice Snippets

1. os and pathlib

import os
from pathlib import Path
 
# Environment variables
api_key = os.getenv("API_KEY", "default-key")
os.environ["MODE"] = "dev"
 
# Current working directory
print(os.getcwd())
 
# Join and normalize paths (prefer pathlib)
base = Path.home() / "projects" / "myapp"
logs_dir = base / "logs"
logs_dir.mkdir(parents=True, exist_ok=True)
 
# Iterate files
for p in logs_dir.glob("*.log"):
    print(p.name, p.stat().st_size)
 
# Read/write text files with pathlib
config_file = base / "config.json"
config_file.write_text("{}", encoding="utf-8")
print(config_file.read_text(encoding="utf-8"))
 
# Cross-platform temp directory
import tempfile
with tempfile.TemporaryDirectory() as tmp:
    tmp_path = Path(tmp)
    (tmp_path / "file.txt").write_text("hello")

Notes:

  • Prefer pathlib.Path over string paths; cleaner and cross-platform.
  • Use exist_ok=True and parents=True safely.

2. sys and argparse

import sys
import argparse
 
# Access CLI args directly
print(sys.argv)  # ['script.py', 'arg1', 'arg2']
 
# argparse for robust CLIs
parser = argparse.ArgumentParser(description="Process numbers")
parser.add_argument("numbers", nargs="+", type=int, help="Numbers to sum")
parser.add_argument("--verbose", "-v", action="store_true", help="Verbose output")
args = parser.parse_args(["1", "2", "3", "-v"])  # example usage
 
if args.verbose:
    print(f"Adding {args.numbers}")
print(sum(args.numbers))

Notes:

  • Use argparse for validation, help text, and defaults.

3. datetime

from datetime import date, datetime, timedelta, timezone
 
# Current time (naive vs aware)
now_naive = datetime.now()
now_utc = datetime.now(timezone.utc)
 
# Parsing and formatting
s = "2025-09-19 14:30:00"
ts = datetime.strptime(s, "%Y-%m-%d %H:%M:%S")
print(ts.strftime("%b %d, %Y %I:%M %p"))
 
# Arithmetic
week_later = now_utc + timedelta(days=7)
 
# Convert timezone
ist = timezone(timedelta(hours=5, minutes=30))
now_ist = now_utc.astimezone(ist)

Notes:

  • Prefer timezone-aware datetimes in apps.
  • For complex tz handling, consider pytz or zoneinfo (Py3.9+).

4. json

import json
from pathlib import Path
 
settings = {"debug": True, "retries": 3}
 
# Serialize to string
s = json.dumps(settings, indent=2)
 
# Write to file
Path("settings.json").write_text(s, encoding="utf-8")
 
# Read from file
loaded = json.loads(Path("settings.json").read_text(encoding="utf-8"))
 
# Custom objects: use default hook
from dataclasses import dataclass, asdict
 
@dataclass
class User:
    name: str
    age: int
 
u = User("Alice", 30)
json_str = json.dumps(asdict(u))

Notes:

  • Use asdict with dataclasses for clean JSON.
  • For YAML in infra contexts, PyYAML is common but ensure safe loading (safe_load).

5. re (Regular Expressions)

import re
 
text = "Order #12345 shipped to email user@example.com on 2025-09-19"
 
# Search for first match
m = re.search(r"#(\d+)", text)
order_id = m.group(1) if m else None
 
# Find all emails
emails = re.findall(r"[\w.-]+@[\w.-]+", text)
 
# Replace dates to another format
new_text = re.sub(r"(\d{4})-(\d{2})-(\d{2})", r"\3/\2/\1", text)
 
# Named groups
pattern = re.compile(r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})")
match = pattern.search("2025-12-01")
if match:
    print(match.group("year"), match.groupdict())

Notes:

  • Use raw strings r"..." for regex patterns.
  • Precompile with re.compile when reusing patterns.

6. logging

import logging
 
# Basic config
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(name)s %(message)s",
)
logger = logging.getLogger("app")
logger.info("App started")
 
# File handler
file_handler = logging.FileHandler("app.log")
file_handler.setLevel(logging.DEBUG)
formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s")
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
 
logger.debug("Debug details")
logger.error("Something went wrong", exc_info=True)

Notes:

  • Avoid print for production logs; use logging with handlers/formatters.
  • Use exc_info=True to capture stack traces.

7. requests (HTTP basics)

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
 
# Simple GET with timeout
resp = requests.get("https://httpbin.org/get", timeout=5)
print(resp.status_code, resp.json())
 
# POST JSON
payload = {"name": "alice"}
resp = requests.post("https://httpbin.org/post", json=payload, timeout=5)
 
# Session with retries
session = requests.Session()
retry = Retry(total=3, backoff_factor=0.5, status_forcelist=[500, 502, 503, 504])
session.mount("https://", HTTPAdapter(max_retries=retry))
resp = session.get("https://httpbin.org/status/503", timeout=5)

Notes:

  • Always set timeouts; consider retries for transient errors.
  • Prefer sessions to reuse connections.

8. Common Interview Questions

  • How to read/write files portably across OSes?
  • Difference between os.path and pathlib?
  • How do you parse CLI args and provide help?
  • Timezone-aware vs naive datetimes? How to convert?
  • JSON serialization of custom objects?
  • Practical uses of regex groups and re.sub?
  • How to configure logging for file and console?
  • How to implement request retries and timeouts?

9. Practice Snippets

  1. Safe config loader (JSON)
import json
from pathlib import Path
from typing import Any
 
def load_config(path: str) -> dict[str, Any]:
    p = Path(path)
    if not p.exists():
        return {}
    try:
        return json.loads(p.read_text(encoding="utf-8"))
    except json.JSONDecodeError:
        return {}
  1. Grep-like search with regex
import re
from pathlib import Path
 
def grep(pattern: str, path: str) -> list[str]:
    rx = re.compile(pattern)
    return [line for line in Path(path).read_text(encoding="utf-8").splitlines() if rx.search(line)]
  1. CLI sum with argparse
import argparse
 
def cli_sum(args: list[str]) -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("nums", type=int, nargs="+")
    ns = parser.parse_args(args)
    return sum(ns.nums)