CodeVetter
← All unpacks
CodeVetter · Repo Unpack evidence

psf/requests

Scanned commit 611c6162cb dated 2026-09-21 — last upstream commit. Generated by codevetter 1.12.0 on 2026-09-22.

github.com/psf/requests ↗ · 124 files · 1.5 MB

Requests Requests is a simple, yet elegant, HTTP library.

Deep analysis by claude-opus-5 via the claude CLI on 2026-09-22 (338s) — every claim below links to its cited source files.

Overview

Requests is the PSF-maintained Python HTTP client library (`v2.34.2`, `src/requests/__version__.py`) that wraps urllib3 in a human-friendly API: `requests.get(...)` returns a `Response` with `.text`, `.json()`, `.cookies`, and `.raise_for_status()`. It is a pure-library distribution — no CLI, no server, no database — whose entire user surface is the seven module-level verb functions in `api.py` plus the `Session` object that persists cookies, auth, proxies, and a urllib3 connection pool across calls. What is distinctive in this commit is how much of the codebase is now type-machinery and hardening rather than new features: a private `_types.py` with `TypedDict` kwargs unpacking, pyright in `strict` mode over `src/requests/`, a pinned/hash-locked GitHub Actions setup audited by zizmor, and an explicit anti-LLM contribution policy. The architecture is deliberately layered so that the transport is swappable: `Session.mount(prefix, adapter)` is the single extension seam that every custom TLS, retry, or mock-transport integration in the ecosystem hangs off.

System map

There is no application entrypoint — the package root `src/requests/__init__.py` is the runtime boundary, and it does dependency-compatibility checking, optional pyOpenSSL SNI injection, and re-export of the public API before anything else runs. Beneath it sit four layers: the stateless verb wrappers (`api.py`), the stateful `Session` (`sessions.py`), the request/response value objects (`models.py`), and the urllib3-backed transport (`adapters.py`). Cross-cutting support lives in `utils.py` (proxies, netrc, header validation, length probing), `cookies.py` (a `CookieJar`/`MutableMapping` hybrid), `structures.py` (`CaseInsensitiveDict`, `LookupDict`), and `exceptions.py`. There is no storage layer: the only persistent state is the in-memory `RequestsCookieJar` on a `Session` and urllib3's connection pools. Build is setuptools via `pyproject.toml`; test/lint/typecheck/publish are four separate GitHub Actions workflows, and distribution is a Trusted-Publishing PyPI upload on `v*` tags.

All 14 system map claims →

Feature catalog

Every user-facing feature is a Python API, not a route or command. The catalog splits into: the seven HTTP verb helpers and their `Session` methods; request-construction features (params, form/JSON/multipart/streamed bodies, file uploads, IDNA hostnames); response-consumption features (`.text` with charset auto-detection, `.json()` with UTF-BOM sniffing, `.iter_content`/`.iter_lines` streaming, `.links` header parsing, `.raise_for_status`); and connection features (pooling, redirects, cookie persistence, proxy resolution including SOCKS, TLS verification and mTLS client certs, netrc auth, digest auth). Two auxiliary surfaces exist: `python -m requests.help` prints a JSON environment report for bug filing, and `python -m requests.certs` prints the CA bundle path.

All 15 feature catalog claims →

Data flow

Input arrives as loosely-typed Python kwargs at `api.request()` or `Session.request()`, is funnelled into a `Request` value object, then normalised into a fully-serialised `PreparedRequest` (bytes body, `CaseInsensitiveDict` headers, percent-encoded URL string) by `Session.prepare_request()`. Session-level and environment-level settings are merged in at two distinct points — `merge_setting`/`merge_hooks` during preparation, and `merge_environment_settings` for proxies/verify/cert/stream just before send. The `PreparedRequest` crosses the adapter boundary into urllib3, and the urllib3 response crosses back through `HTTPAdapter.build_response()`, which is the sole constructor of `Response` objects. State ownership is clean: `Session` owns cookies, adapters and defaults; `PreparedRequest` owns the wire representation; `Response` owns `_content`/`_content_consumed` and lazily pulls bytes off `raw`. Output boundaries are the `Response` accessors and the environment mutations in `should_bypass_proxies` (via `set_environ`) and `extract_zipped_paths` (via `tempfile.mkstemp`).

All 15 data flow claims →

Behavior traces

Three flows carry most of the library's behaviour: the one-shot `requests.get()` path, the redirect-following loop inside `Session.send`, and the digest-auth 401 retry. All three converge on `Session.send` → `HTTPAdapter.send` → `build_response`. The redirect loop is a generator (`resolve_redirects`) that is fully drained inside `send`, which is why history arrives pre-assembled on the returned `Response`. The digest flow is unusual in that it re-enters the transport from a response hook (`r.connection.send(prep)`), bypassing `Session.send` entirely.

All 15 behavior traces claims →

Testing signals

Testing is pytest-only, concentrated in one 108 KB file: `tests/test_requests.py` holds 237 of the ~347 test functions, with `tests/test_utils.py` (64) and `tests/test_lowlevel.py` (13) next. Integration tests run against a real local `httpbin` via the `pytest-httpbin` fixture (wrapped by `conftest.prepare_url` to guarantee a trailing slash), while wire-level behaviour uses a hand-rolled socket `Server` thread in `tests/testserver/server.py`. TLS behaviour is covered by checked-in fixture certificates (`tests/certs/{valid,expired,mtls}`) plus a `trustme`-generated no-SAN server fixture. Doctests in the library modules are executed by `--doctest-modules`. Gaps: `adapters.py` — the single most security-relevant module — has a one-test dedicated file, and there is no coverage threshold enforced anywhere.

All 15 testing signals claims →

Risk map

As a library in ~4M dependent repos, the blast radius of any change here is close to maximal, and the highest-risk surfaces are the ones that decide whether credentials cross a trust boundary: `should_strip_auth`, `rebuild_proxies`, `prepare_url`, and `cert_verify`. Several behaviours are knowingly non-ideal and preserved for backwards compatibility — the http→https auth-retention carve-out, `_basic_auth_str` accepting non-strings, and `LookupDict.__getitem__` silently returning `None`. Environment trust is on by default (`trust_env=True`), so `.netrc`, `$NETRC`, `REQUESTS_CA_BUNDLE`/`CURL_CA_BUNDLE` and `*_proxy` variables all influence behaviour implicitly. Filesystem and process-global side effects exist in `extract_zipped_paths`, `atomic_open`, `set_environ`, and the import-time `pyopenssl.inject_into_urllib3()`.

All 17 risk map claims →

Extension points

There is one primary extension seam and several secondary ones. The primary is the Transport Adapter: `Session.mount(prefix, adapter)` keeps `self.adapters` sorted by descending prefix length and `get_adapter` picks the longest matching prefix, so any URL prefix can be routed to a `BaseAdapter` subclass — this is how retry policies, custom TLS contexts, and test doubles are injected. Secondary seams are the response hook system, the `AuthBase` callable protocol, the `SessionRedirectMixin` methods documented as overridable, and the packager-facing `certs.where()`. Nearly every private-looking `HTTPAdapter` method carries a docstring saying it exists specifically for subclass overrides.

All 16 extension points claims →

Contributor handoff

Treat this repo as a stability-first, extremely high-blast-radius library: the maintainers protect version, changelog, workflows and pyproject behind CODEOWNERS, and the project has a written policy banning unsupervised agentic contributions. Read `sessions.py`, `models.py` and `adapters.py` before touching anything; almost every non-obvious branch in them encodes a specific GitHub issue number that you should look up before 'simplifying' it. Local loop is `make init` then `make test`; before pushing, run `pre-commit run --all-files` and `pyright src/requests/`, since lint and typecheck are separate required CI gates. The biggest trap is that `tests/` is excluded from pyright but `src/requests/` is checked in `strict` mode, so type annotations in library code must be complete and the codebase is dense with deliberate `# type: ignore` comments carrying explanatory reasons — preserve them.

All 16 contributor handoff claims →

Languages

LanguageFilesShare of source
Python3797%
Markdown13docs/config
YAML12docs/config
CSS1docs/config
HTML1docs/config
TOML13%

Start reading here

Probable source entrypoint: setup.py — inferred from the directory tree and manifest, not the upstream docs.

Source roots

src/ (20 files)

Source layout — src/

PathFiles
src/requests/20

Repository structure

Top-level pathFiles
tests/42
docs/25
src/20
.github/19
ext/2

Docs found: README.md

Package

pyproject.toml — requests v{attr = "requests.__version__.__version__"}, 3 declared dependencies.

securitysocksuse_chardet_on_py3

CI & config entrypoints

Structural health signals

Average score 7.7/10 across 22 analyzed source files; 5 hotspots surfaced.

These are deterministic review leads — size, churn, and structural signals — not proof of a bug in this project.

Recent history