
Their publication
PyDepth
Python · pydepth.com
Python, its runtimes, frameworks, and the systems beneath them — read from source, measured, not assumed.
Author
Elliot Sayer
Writes PyDepth — how Python behaves when you look underneath it.
Elliot Sayer writes about Python, its runtimes, frameworks, and the systems behind production applications. His work explores language behavior, concurrency, performance, tooling, and the trade-offs hidden beneath Python's deceptively simple surface. He prefers source code, reproducible experiments, and measurements over folklore.
PyDepth is a written record of how Python behaves when you look underneath it — not tutorials, not release summaries, not opinions about which framework won. Each entry takes a claim that gets repeated in code review, finds the mechanism that produced it, and measures whether it is still true on a current interpreter: object layout and the import system, the GIL and the event loop, annotation resolution and packaging, and what Django, FastAPI and SQLAlchemy do inside a real request rather than in the quickstart.
Three kinds of evidence, in that order of preference: the source that does the thing, a reproduction short enough to read inside the entry, and a measurement printed with its interpreter version and the machine it ran on. Every entry names the script it ran by path, and those paths resolve against a public experiments repository. Terminal output is pasted, never retyped and never tidied, and a figure that cannot be reproduced by running the script behind it does not get published.
Elliot Sayer is a publishing byline. It identifies the author of every article at pydepth.com, and nothing else — it is not the operator of the domain named in that site's legal pages.
Every issue so far
Under a Latin-1 locale, CPython 3.13 reads a UTF-8 file as mojibake and raises nothing. 3.15 reads it correctly and raises on the Latin-1 file 3.13 read fine.
Importing logging before asyncio moves 5.5 ms from asyncio's line to logging's. The program does the same work. The cumulative column charges whoever got there first.
A lazy import of asyncio costs no measurable time and no modules at startup. Read the name once and 26.4 of the 26.8 ms come back. Deferral is not removal.
On CPython 3.10 a NamedTuple attribute read cost 0.81x a read through an instance dictionary. On 3.11 it costs 1.90x. Nothing about NamedTuple changed.
sys.getsizeof reports 296 bytes for a class's first instance dict and 96 once twenty-six exist. The instance itself costs 96 bytes and getsizeof says 48.
A read through __slots__ costs 24% more than one through an instance dictionary on CPython 3.13.2. Rebuild the same version and the gap goes away.
Annotations cost nothing per call and everything at import. Reading __annotations__ takes 24 ns; resolving the same class with get_type_hints takes 3.4 µs, 140x more.
Removing Django's entire default middleware chain saved 0.02 ms per request. Fixing one N+1 query on a fifty-row list view saved 3.30 ms.
Awaiting a coroutine that never suspends costs 53 ns against 24 ns for a plain call. Wrapping the same coroutine in a Task and awaiting it costs 28,958 ns.
Eight threads of pure-Python arithmetic run at 0.98x the speed of one. The same eight threads doing IO run at 7.90x. The GIL is a scheduler, and it has a dial.
Importing asyncio costs 21.9 ms and pulls in 100 modules. Importing email costs 0.1 ms and pulls in one. Granularity predicts import cost; size does not.
An instance with three attributes costs 96 bytes, not the 344 sys.getsizeof reports, and __slots__ read 12% slower on the CPython 3.13.2 build measured here.
Elsewhere in the network
ElephantPHP · JS Ledger · JVM Scope · CSharpMind · SQLPress · Gopheria — six more languages, six more specialists, one imprint. Every language has a page of its own.