Type-Safe Python Decorators

Decorators are everywhere in Python. Logging, retries, caching, auth checks - if you’ve built anything non-trivial, you’ve written one. But the moment you try to add type hints, things get weird. Your IDE loses autocomplete. You end up with Callable[..., Any] and pretend the problem doesn’t exist, because Callable can’t express “whatever arguments the wrapped function takes.” PEP 612 introduced ParamSpec (Python 3.10+) to solve exactly this. This post walks through using it - from trivial cases to the genuinely painful scenarios I hit while building a multi-LLM agent backend. Read the TL;DR section for a quick summary. ...

February 1, 2026 · 8 min · Deepankar Mahapatro

Claude Code hooks for Slack

Complex tasks in Claude Code can take a while. Run a few in parallel across different repos and it gets hard to track which ones are done and which ones need input. Claude Code supports hooks - shell commands that run in response to events. Hook into the Stop and Notification events to send a Slack message when a task completes or when Claude needs attention. The notification script Using PEP 723 inline metadata to keep everything in one file: ...

January 14, 2026 · 2 min · Deepankar Mahapatro

TIL: Inline dependencies in Python scripts

When sharing a Python script as a gist, you’d typically include a requirements.txt or a pyproject.toml with uv.lock. Multiple files for one script. Turns out there’s a cleaner way. PEP 723 lets you embed dependencies and Python version requirements directly in the script: # /// script # requires-python = ">=3.11" # dependencies = [ # "requests", # "rich", # ] # /// import requests from rich import print response = requests.get("https://api.github.com/zen") print(f"[bold green]{response.text}[/bold green]") Run it with uv: ...

January 13, 2026 · 1 min · Deepankar Mahapatro

Detecting event loop blocking in asyncio

If you’re writing async Python, you’ve probably blocked the event loop without knowing it. Your code runs. Your tests pass. But in production, p90 latencies spike and timeouts appear seemingly at random. The culprit? Synchronous code hiding inside your async def functions. Python’s asyncio is cooperative. When you await something, you’re yielding control back to the event loop so other tasks can run. But if you call synchronous code, even accidentally, the entire event loop freezes. Every other coroutine waits. Every concurrent request hangs. Every other user gets blocked. ...

January 11, 2026 · 3 min · Deepankar Mahapatro