Part VIII — Open source walkthrough: inside requests
Reading good code is how you move from "I can write Python" to "I write
Python like the people who build the libraries". This Part reads one real,
popular package end to end. The choice is
requests: it is one of the most
downloaded packages on PyPI, it is small (about 6 400 lines in
src/requests/), its design has been copied by httpx and many others,
and its call path runs straight down the series: from a Python function
call, through HTTP (Net Ch 28),
TLS (Sec Ch 28),
TCP (Net Ch 21),
DNS (Net Ch 18),
to the connect(2) syscall (OS Ch 2).
The code below is quoted from requests 2.34.2 and urllib3 2.8.0, the versions current when this guide was written. Line numbers move between releases; function names rarely do.
63. How to read a real codebase
63.1 Get the code and its tests running first
git clone https://github.com/psf/requests && cd requests
uv venv && source .venv/bin/activate
uv pip install -r requirements-dev.txt # -e .[socks], pytest, pytest-httpbin, trustme
pytest tests/ -q -x # a green baseline before you change anything
requirements-dev.txt tells you how the project tests itself:
pytest-httpbin runs a local copy of the httpbin HTTP testing service, so
the tests make real HTTP requests without touching the internet, and
trustme generates throwaway TLS certificates for HTTPS tests.
63.2 A reading method that works on any project
- Read the README and the public API first. In Python, that is
__init__.py: it lists what the package exports. - Find the entry point for the feature you care about and trace one call all the way down, writing the chain of function names as you go. Do not read files top to bottom.
- Run it under a debugger with a breakpoint at the bottom of the chain
and read the stack (
breakpoint(), or your editor's debugger). The stack is the call path. - Read the tests for the function you are studying; they show the intended behaviour and the edge cases the authors worried about.
- Read
git log -p/git blameon a surprising line; the commit message or linked issue usually explains it.
# Step 3 in practice: stop inside the socket connect and print the Python stack.
import socket
import traceback
import requests
real_connect = socket.socket.connect
def traced_connect(self, addr):
traceback.print_stack(limit=14) # who called connect()?
return real_connect(self, addr)
socket.socket.connect = traced_connect
requests.get("https://example.com", timeout=5)
That one experiment prints the whole chain this Part walks through. Try it before reading on, and compare.
63.3 The map
src/requests/
├── __init__.py public API: get, post, Session, exceptions; dependency version checks
├── api.py requests.get/post/... : thin wrappers that create a Session
├── sessions.py Session: settings, cookies, redirects, adapter selection ◀── the core
├── models.py Request, PreparedRequest (exact bytes to send), Response
├── adapters.py HTTPAdapter: translates to urllib3, maps exceptions ◀── the boundary
├── auth.py HTTPBasicAuth, HTTPDigestAuth, the AuthBase interface
├── cookies.py a CookieJar that behaves like a dict
├── exceptions.py RequestException hierarchy
├── hooks.py the "response" hook
├── structures.py CaseInsensitiveDict, LookupDict
├── utils.py proxies from env, encodings, netrc, header helpers
└── certs.py where the CA bundle lives (certifi)
urllib3 (a separate package): PoolManager → HTTPSConnectionPool → HTTPSConnection → socket + ssl
The split matters: requests is the human-friendly API and policy (sessions, redirects, cookies, auth); urllib3 is the HTTP engine (connection pooling, the wire protocol, retries, TLS wrapping).