# psf/requests — codebase unpack

> Canonical page: https://codevetter.com/unpack/requests

This is a deterministic Repo Unpack scan of psf/requests — a structural map, not a review, security audit, or maintainer-endorsed description.

- Scanned commit: 611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60 (2026-09-21)
- Last upstream commit: Bump the actions group with 3 updates (#7628)
- Generated by codevetter 1.12.0 on 2026-09-22
- 124 files scanned · 1.5 MB

## Languages

- Python: 37 files
- Markdown: 13 files
- YAML: 12 files
- CSS: 1 files
- HTML: 1 files
- TOML: 1 files

## Repository structure

- tests/ — 42 files
- docs/ — 25 files
- src/ — 20 files
- .github/ — 19 files
- ext/ — 2 files

Probable source entrypoint: `setup.py` (inferred from the tree, not upstream docs)

## Source layout — src/

- src/requests/ — 20 files

## Package

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

## Structural health signals

Average score 7.7/10 across 22 files; 5 hotspots. These are deterministic review leads, not proof of a bug.

## Recent history

- 611c6162cbc4 (2026-09-21) Bump the actions group with 3 updates (#7628)
- dae7ef63b4df (2026-09-02) Bump https://github.com/astral-sh/ruff-pre-commit (#7616)
- 5460f467b02e (2026-08-24) Update lock-issues.yml to latest dependencies (#7609)
- 6af0b94158bb (2026-08-24) Bump https://github.com/astral-sh/ruff-pre-commit (#7608)
- 8f8b212de8c2 (2026-08-17) Bump https://github.com/astral-sh/ruff-pre-commit (#7606)

## 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.
- The package root is the de-facto entrypoint: importing `requests` runs `check_compatibility()` to assert urllib3 >= 1.21.1 and chardet 3.0.2–8.0.0 / charset_normalizer 2.0.0–4.0.0, emitting `RequestsDependencyWarning` on mismatch rather than failing the import. — [src/requests/__init__.py#L58-L118](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/__init__.py#L58-L118)
- Import also attempts `pyopenssl.inject_into_urllib3()` when `ssl.HAS_SNI` is falsy and then version-checks `cryptography` via `_check_cryptography()`, warning if below 1.3.4 — a side effect that mutates urllib3 globally at import time. — [src/requests/__init__.py#L120-L143](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/__init__.py#L120-L143)
- The public API surface is fixed by the `__all__` tuple in `__init__.py`: 7 verb functions (`get/post/put/patch/delete/head/options`) plus `request`, the `Session`/`Request`/`PreparedRequest`/`Response` types, `codes`, and 9 exception classes. — [src/requests/__init__.py#L160-L212](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/__init__.py#L160-L212) [src/requests/api.py#L23-L180](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/api.py#L23-L180)
- `api.request()` is stateless by construction — it opens `sessions.Session()` in a `with` block for every call specifically to close sockets and avoid `ResourceWarning`, meaning module-level calls get zero connection reuse. — [src/requests/api.py#L64-L70](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/api.py#L64-L70)
- `Session.__init__` establishes all mutable session state (headers from `default_headers()`, empty proxies/params/hooks, `verify=True`, `max_redirects=DEFAULT_REDIRECT_LIMIT` of 30, `trust_env=True`) and mounts an `HTTPAdapter` for both `https://` and `http://`. — [src/requests/sessions.py#L441-L500](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L441-L500) [src/requests/models.py#L103](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L103)
- The transport boundary is `HTTPAdapter.send()`, which calls `conn.urlopen(..., redirect=False, preload_content=False, decode_content=False, retries=self.max_retries)` — requests deliberately disables urllib3's own redirect and decode handling and reimplements both. — [src/requests/adapters.py#L634-L748](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/adapters.py#L634-L748)
- The only external integrations are the four hard runtime dependencies declared in `pyproject.toml`: `charset_normalizer>=2,<4`, `idna>=2.5,<4`, `urllib3>=1.26,<3`, `certifi>=2023.5.7`; SOCKS and chardet are optional extras. — [pyproject.toml#L17-L23](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/pyproject.toml#L17-L23) [pyproject.toml#L50-L54](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/pyproject.toml#L50-L54)
- The CA bundle boundary is `certs.where()`, a one-line re-export of `certifi.where` that the module docstring explicitly names as the seam distro packagers should patch; it is captured once at import into `utils.DEFAULT_CA_BUNDLE_PATH`. — [src/requests/certs.py](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/certs.py) [src/requests/utils.py#L82](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/utils.py#L82)
- Build/test commands live in the `Makefile`: `make init` installs `requirements-dev.txt`, `make test` runs `pytest tests`, `make ci` adds `--junitxml=report.xml`, and `make coverage` runs pytest-cov against `src/requests`. — [Makefile#L1-L17](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/Makefile#L1-L17) [requirements-dev.txt](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/requirements-dev.txt)
- pytest is configured with `addopts = "--doctest-modules"` and `testpaths = ["tests"]`, so the docstring examples in `api.py`, `sessions.py`, and `status_codes.py` are executed as tests alongside the suite. — [pyproject.toml#L100-L104](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/pyproject.toml#L100-L104) [src/requests/status_codes.py#L1-L19](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/status_codes.py#L1-L19)
- Deployment is PyPI-only via `.github/workflows/publish.yml`, which builds with `SOURCE_DATE_EPOCH` pinned to the commit time for reproducibility and publishes through `pypa/gh-action-pypi-publish` with `attestations: true` and `id-token: write` (Trusted Publishing, no stored API token). — [.github/workflows/publish.yml#L28-L100](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/.github/workflows/publish.yml#L28-L100)
- Five CI workflows gate the repo: `run-tests.yml` (matrix of Python 3.10–3.15-dev plus 3.14t free-threaded and pypy-3.11, across ubuntu/macOS/windows), `lint.yml` (pre-commit), `typecheck.yml` (pyright), `codeql-analysis.yml`, and `zizmor.yml` for Actions-security scanning. — [.github/workflows/run-tests.yml#L1-L40](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/.github/workflows/run-tests.yml#L1-L40) [.github/workflows/typecheck.yml](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/.github/workflows/typecheck.yml) [.github/workflows/zizmor.yml](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/.github/workflows/zizmor.yml)
- `packages.py` is a compatibility shim that aliases `sys.modules['requests.packages.urllib3.*']` to the real urllib3/idna/chardet modules so pre-2.16 imports still resolve to identical module objects. — [src/requests/packages.py](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/packages.py) [tests/test_packages.py](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/tests/test_packages.py)
- Docs are a separate Sphinx build deployed to Read the Docs with the `dirhtml` builder on Python 3.12, producing PDF and ePub alongside HTML; `make docs` builds them locally. — [.readthedocs.yaml](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/.readthedocs.yaml) [docs/conf.py](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/docs/conf.py) [Makefile#L26-L28](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/Makefile#L26-L28)

## 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.
- Seven verb helpers are exposed at module level and mirrored as `Session` methods; `head()` uniquely defaults `allow_redirects` to `False` at both levels while `get`/`options` default it to `True`. — [src/requests/api.py#L100-L110](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/api.py#L100-L110) [src/requests/sessions.py#L653-L680](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L653-L680)
- Request bodies support four distinct shapes decided in `prepare_body`: JSON via `complexjson.dumps(json, allow_nan=False)` → `application/json`; multipart via `_encode_files`; form-encoding via `_encode_params` → `application/x-www-form-urlencoded`; and streamed iterables/file objects that set `Transfer-Encoding: chunked` when length is unknown. — [src/requests/models.py#L576-L651](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L576-L651)
- Multipart uploads accept 2-, 3-, and 4-tuples (`filename, fileobj`, `+content_type`, `+custom_headers`) and are assembled into urllib3 `RequestField` objects by `encode_multipart_formdata`. — [src/requests/models.py#L183-L250](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L183-L250) [src/requests/api.py#L36-L40](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/api.py#L36-L40)
- Streaming bodies and file uploads are mutually exclusive — combining them raises `NotImplementedError("Streamed bodies and files are mutually exclusive.")`. — [src/requests/models.py#L622-L626](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L622-L626)
- Internationalised domains are supported by `_get_idna_encoded_host()`, which applies `idna.encode(host, uts46=True)` for any non-ASCII host and raises `InvalidURL("URL has an invalid label.")` on failure. — [src/requests/models.py#L474-L482](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L474-L482) [src/requests/models.py#L525-L534](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L525-L534)
- `Response.text` decodes using the header-declared charset, falling back to `apparent_encoding` (charset_normalizer or chardet), and finally to `utf-8` with `errors="replace"` if no detection library is installed. — [src/requests/models.py#L1054-L1090](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L1054-L1090) [src/requests/models.py#L897-L904](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L897-L904)
- `Response.json()` sniffs UTF-8/16/32 via `guess_json_utf()` when no encoding is declared and the body exceeds 3 bytes, and normalises every decode failure into `requests.exceptions.JSONDecodeError`. — [src/requests/models.py#L1091-L1125](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L1091-L1125) [src/requests/utils.py#L1008](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/utils.py#L1008)
- Streaming download is offered by `iter_content(chunk_size, decode_unicode)` and the line-oriented `iter_lines(delimiter=...)`; re-iterating a consumed non-streamed response raises `StreamConsumedError`. — [src/requests/models.py#L914-L978](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L914-L978) [src/requests/models.py#L994-L1033](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L994-L1033)
- Pagination via RFC 5988 is supported by `Response.links`, which parses the `Link` header through `parse_header_links()` and keys results by `rel` (falling back to `url`). — [src/requests/models.py#L1127-L1142](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L1127-L1142) [src/requests/utils.py#L965](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/utils.py#L965)
- `requests.codes` is a `LookupDict` supporting attribute and item access with multiple aliases per code — including the joke aliases `codes['\\o/']` and `codes['✓']` for 200 — generated by `_init()` which also appends the full table to the module docstring. — [src/requests/status_codes.py#L22-L31](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/status_codes.py#L22-L31) [src/requests/status_codes.py#L109-L128](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/status_codes.py#L109-L128) [src/requests/structures.py#L96-L131](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/structures.py#L96-L131)
- Three auth handlers ship: `HTTPBasicAuth`, `HTTPProxyAuth` (same header under `Proxy-Authorization`), and `HTTPDigestAuth` supporting MD5, MD5-SESS, SHA, SHA-256 and SHA-512 with `qop=auth`. — [src/requests/auth.py#L85-L122](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/auth.py#L85-L122) [src/requests/auth.py#L157-L215](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/auth.py#L157-L215)
- `.netrc` credentials are honoured automatically when `trust_env` is true, checked at `$NETRC` if set otherwise `~/.netrc` and `~/_netrc`, and re-applied on cross-host redirects by `rebuild_auth`. — [src/requests/utils.py#L231-L280](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/utils.py#L231-L280) [src/requests/sessions.py#L309-L332](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L309-L332)
- SOCKS proxy support is optional: `proxy_manager_for()` routes `socks*` proxies to `SOCKSProxyManager`, and a stub raising `InvalidSchema("Missing dependencies for SOCKS support.")` is installed when PySocks is absent. — [src/requests/adapters.py#L62-L68](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/adapters.py#L62-L68) [src/requests/adapters.py#L269-L305](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/adapters.py#L269-L305) [pyproject.toml#L52](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/pyproject.toml#L52)
- `python -m requests.help` (i.e. `help.main()`) prints a sorted, indented JSON bug-report blob covering platform, interpreter implementation, system OpenSSL version, urllib3, chardet/charset_normalizer, pyOpenSSL, cryptography, idna and requests versions. — [src/requests/help.py#L67-L132](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/help.py#L67-L132) [tests/test_help.py](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/tests/test_help.py)
- Sessions are picklable: `__getstate__`/`__setstate__` serialise the 12 attributes listed in `Session.__attrs__`, while `HTTPAdapter.__setstate__` rebuilds the unpicklable `PoolManager` by re-calling `init_poolmanager`. — [src/requests/sessions.py#L427-L440](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L427-L440) [src/requests/sessions.py#L895-L902](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L895-L902) [src/requests/adapters.py#L223-L238](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/adapters.py#L223-L238)

## 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`).
- Settings precedence is implemented by `merge_setting(request_setting, session_setting)`: non-Mapping request values win outright, Mappings are merged with request keys overriding, and any key explicitly set to `None` is deleted from the merged result — that is how callers suppress a session default header. — [src/requests/sessions.py#L75-L104](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L75-L104)
- Hooks need special merging because `{'response': []}` would otherwise wipe session hooks; `merge_hooks` short-circuits when either side has an empty response list. — [src/requests/sessions.py#L107-L123](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L107-L123)
- Cookies flow through a three-way merge in `prepare_request`: `merge_cookies(merge_cookies(RequestsCookieJar(), self.cookies), request.cookies)`, so per-request cookies layer over session cookies into a fresh jar rather than mutating the session's. — [src/requests/sessions.py#L511-L556](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L511-L556) [src/requests/cookies.py#L604-L625](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/cookies.py#L604-L625)
- `PreparedRequest.prepare()` runs a fixed six-step pipeline — method, url, headers, cookies, body, auth — then hooks; the ordering comment states auth must be last so schemes like OAuth can sign a fully-formed request and still register hooks. — [src/requests/models.py#L424-L452](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L424-L452)
- `prepare_auth` mutates the prepared request by `self.__dict__.update(r.__dict__)` from whatever the auth callable returns, then recomputes Content-Length — so an auth handler can legitimately rewrite body and headers. — [src/requests/models.py#L670-L697](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L670-L697)
- Credentials embedded in the URL are extracted into auth during preparation: `prepare_auth` calls `get_auth_from_url(self.url)` when no explicit auth was supplied, and `prepare_url` reassembles netloc with the `auth@` prefix preserved. — [src/requests/models.py#L672-L676](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L672-L676) [src/requests/utils.py#L1070](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/utils.py#L1070) [src/requests/models.py#L536-L541](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L536-L541)
- Environment enters the flow at `merge_environment_settings`, which (only when `trust_env`) seeds proxies from `getproxies()` and overrides `verify` from `REQUESTS_CA_BUNDLE` or `CURL_CA_BUNDLE` — explicitly to be cURL-compatible. — [src/requests/sessions.py#L831-L868](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L831-L868)
- Proxy selection is layered: `resolve_proxies` consults `should_bypass_proxies` (NO_PROXY, CIDR matching, platform `proxy_bypass`), then `select_proxy` picks by the key precedence `scheme://host` → `scheme` → `all://host` → `all`. — [src/requests/utils.py#L810-L871](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/utils.py#L810-L871) [src/requests/utils.py#L885-L940](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/utils.py#L885-L940)
- Body length determination flows through `super_len()`, which probes `__len__`, `.len`, `fileno()`+`os.fstat`, and `seek/tell`, subtracting the current file position so partially-read files report the remaining bytes. — [src/requests/utils.py#L160-L228](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/utils.py#L160-L228) [src/requests/models.py#L654-L668](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L654-L668)
- Header values are validated at the input boundary by `check_header_validity`, which regex-matches against `HEADER_VALIDATORS` to reject CR/LF and leading-whitespace — the CRLF-injection guard. — [src/requests/models.py#L565-L574](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L565-L574) [src/requests/utils.py#L1087-L1120](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/utils.py#L1087-L1120) [src/requests/_internal_utils.py#L13-L24](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/_internal_utils.py#L13-L24)
- The response side is constructed exclusively in `HTTPAdapter.build_response()`, which lifts status, `CaseInsensitiveDict` headers, encoding from `get_encoding_from_headers`, `raw`, reason, url, extracts server cookies into `response.cookies`, and back-links `request` and `connection`. — [src/requests/adapters.py#L365-L402](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/adapters.py#L365-L402)
- Cookies persist onto the Session after the fact: `Session.send` calls `extract_cookies_to_jar(self.cookies, request, r.raw)` for the final response and separately for every entry in `r.history`. — [src/requests/sessions.py#L791-L800](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L791-L800) [src/requests/cookies.py#L135-L152](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/cookies.py#L135-L152)
- Response bytes are pulled lazily: `content` joins `iter_content(CONTENT_CHUNK_SIZE)` (10 KiB) once and caches into `_content`, flipping `_content_consumed`; `Session.send` forces this by touching `r.content` whenever `stream` is falsy. — [src/requests/models.py#L1035-L1052](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L1035-L1052) [src/requests/models.py#L104](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L104) [src/requests/sessions.py#L826-L829](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L826-L829)
- urllib3 exceptions are translated into the requests hierarchy at exactly one place — the `try/except` ladder in `HTTPAdapter.send` mapping `MaxRetryError` reasons to `ConnectTimeout`/`RetryError`/`ProxyError`/`SSLError` and `ProtocolError`/`OSError` to `ConnectionError`. — [src/requests/adapters.py#L703-L747](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/adapters.py#L703-L747) [src/requests/exceptions.py#L66-L150](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/exceptions.py#L66-L150)
- `should_bypass_proxies` temporarily mutates process environment via the `set_environ('no_proxy', ...)` context manager before calling the stdlib `proxy_bypass`, restoring the prior value in a `finally` block. — [src/requests/utils.py#L787-L808](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/utils.py#L787-L808) [src/requests/utils.py#L860-L868](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/utils.py#L860-L868)

## 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.
- Import trace: `requests/__init__.py` runs `check_compatibility` → optional `pyopenssl.inject_into_urllib3` → `warnings.simplefilter('ignore', DependencyWarning)` → re-exports → `logging.getLogger(__name__).addHandler(NullHandler())` → `simplefilter('default', FileModeWarning, append=True)`. — [src/requests/__init__.py#L108-L219](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/__init__.py#L108-L219)
- One-shot GET trace: `api.get()` → `api.request()` opens `Session()` in a `with` → `Session.request()` → builds `Request` → `Session.prepare_request()` → `merge_environment_settings()` → `Session.send()` → returns, and `__exit__` calls `Session.close()` closing every adapter. — [src/requests/api.py#L64-L86](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/api.py#L64-L86) [src/requests/sessions.py#L557-L651](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L557-L651) [src/requests/sessions.py#L883-L886](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L883-L886)
- Preparation trace: `prepare_request` → `PreparedRequest.prepare()` → `prepare_method` (uppercases) → `prepare_url` (IDNA + `requote_uri(urlunparse(...))`) → `prepare_headers` (`check_header_validity` per header) → `prepare_cookies` (sets `Cookie` header once) → `prepare_body` → `prepare_auth` → `prepare_hooks`. — [src/requests/models.py#L424-L452](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L424-L452) [src/requests/models.py#L467-L563](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L467-L563)
- Send trace: `Session.send` defaults stream/verify/cert, rejects a bare `Request` with `ValueError("You can only send PreparedRequests.")`, resolves the adapter by longest matching prefix via `get_adapter`, times the call with `preferred_clock` into `r.elapsed`, then dispatches the `response` hook. — [src/requests/sessions.py#L752-L791](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L752-L791) [src/requests/sessions.py#L68-L72](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L68-L72)
- Adapter trace: `HTTPAdapter.send` → `get_connection_with_tls_context` → `cert_verify` (sets `conn.ca_certs`/`cert_file`, raising `OSError` if any path is missing) → `request_url` (full URL for proxied HTTP, path-only otherwise) → `add_headers` (no-op hook) → `conn.urlopen(...)` → `build_response`. — [src/requests/adapters.py#L634-L700](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/adapters.py#L634-L700) [src/requests/adapters.py#L307-L363](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/adapters.py#L307-L363)
- Redirect trace: `Session.send` calls `resolve_redirects(r, request)` and drains the generator into `history`; the generator per hop consumes `resp.content`, checks `len(resp.history) >= self.max_redirects` raising `TooManyRedirects`, calls `resp.close()`, normalises the Location, and copies the request. — [src/requests/sessions.py#L802-L816](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L802-L816) [src/requests/sessions.py#L186-L245](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L186-L245)
- On a non-307/308 redirect the loop strips `Content-Length`, `Content-Type` and `Transfer-Encoding` and blanks `prepared_request.body`, then always pops the `Cookie` header before re-deriving it from the jar. — [src/requests/sessions.py#L246-L266](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L246-L266)
- Method rewriting per hop is `rebuild_method`: 303 → GET (unless HEAD), 302 → GET (unless HEAD, 'what browsers do, despite standards'), and 301 on a POST → GET. — [src/requests/sessions.py#L370-L392](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L370-L392)
- Auth rewriting per hop is `rebuild_auth` → `should_strip_auth`, which drops `Authorization` on any hostname change but deliberately preserves it for http:80 → https:443 on the same host for backwards compatibility. — [src/requests/sessions.py#L154-L184](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L154-L184) [src/requests/sessions.py#L309-L332](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L309-L332)
- Body rewind trace: a redirect with `_body_position` set and a length header triggers `rewind_body(prepared_request)`; a prior failed `tell()` stores a sentinel `object()` so the rewind raises `UnrewindableBodyError` instead of hanging the connection. — [src/requests/sessions.py#L277-L287](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L277-L287) [src/requests/models.py#L611-L620](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L611-L620) [src/requests/utils.py#L1139](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/utils.py#L1139)
- Non-following trace: when `allow_redirects=False`, `Session.send` primes `r._next` by taking one item from `resolve_redirects(..., yield_requests=True)` inside a `try/except StopIteration`, which is what backs `Response.next`. — [src/requests/sessions.py#L818-L825](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L818-L825) [src/requests/models.py#L892-L895](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L892-L895)
- Digest trace: `HTTPDigestAuth.__call__` registers `handle_401` and `handle_redirect` as response hooks and records `r.body.tell()`; on a 4xx `handle_401` parses `www-authenticate`, consumes and closes the response, copies the request, builds the `Authorization` header and re-sends via `r.connection.send(prep, **kwargs)`, appending the original to `_r.history`. — [src/requests/auth.py#L321-L343](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/auth.py#L321-L343) [src/requests/auth.py#L273-L319](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/auth.py#L273-L319)
- Digest state is per-thread: `init_per_thread_state()` lazily populates `threading.local()` with `last_nonce`, `nonce_count`, `chal`, `pos` and `num_401_calls`, and the retry is capped at `num_401_calls < 2`. — [src/requests/auth.py#L143-L156](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/auth.py#L143-L156) [src/requests/auth.py#L284-L286](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/auth.py#L284-L286)
- Streaming-read trace: `iter_content` builds a generator that prefers `self.raw.stream(chunk_size, decode_content=True)`, translating `ProtocolError`→`ChunkedEncodingError`, `DecodeError`→`ContentDecodingError`, `ReadTimeoutError`→`ConnectionError` and urllib3 `SSLError`→`requests.SSLError`, then sets `_content_consumed`. — [src/requests/models.py#L934-L958](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L934-L958)
- Teardown trace: `Session.close()` iterates adapters calling `HTTPAdapter.close()`, which does `poolmanager.clear()` and clears every cached proxy manager. — [src/requests/sessions.py#L883-L886](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L883-L886) [src/requests/adapters.py#L555-L563](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/adapters.py#L555-L563)

## 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.
- The framework is pytest with `minversion = 6.2`, `testpaths = ["tests"]`, and `--doctest-modules` plus `NORMALIZE_WHITESPACE ELLIPSIS` optionflags, so module docstring examples are part of the suite. — [pyproject.toml#L100-L104](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/pyproject.toml#L100-L104)
- Test distribution is extremely lopsided: `tests/test_requests.py` defines 237 `test_` functions across 7 classes (`TestRequests`, `TestCaseInsensitiveDict`, `TestMorselToCookieExpires`, `TestMorselToCookieMaxAge`, `TestTimeout`, `RedirectSession`, `TestPreparingURLs`), versus 64 in `test_utils.py` and 13 in `test_lowlevel.py`. — [tests/test_requests.py#L84-L2760](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/tests/test_requests.py#L84-L2760) [tests/test_utils.py](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/tests/test_utils.py) [tests/test_lowlevel.py](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/tests/test_lowlevel.py)
- An autouse fixture `clean_proxy_environ` monkeypatches away `http_proxy`, `https_proxy`, `no_proxy`, `ftp_proxy` and `all_proxy` (both cases) for every test, so proxy tests must set env vars explicitly. — [tests/conftest.py#L25-L32](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/tests/conftest.py#L25-L32)
- Integration coverage runs against a live httpbin server through the `httpbin`/`httpbin_secure` fixtures, pinned via `pytest-httpbin==2.1.0` and `httpbin~=0.10.0` in the `test` dependency group. — [tests/conftest.py#L14-L41](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/tests/conftest.py#L14-L41) [pyproject.toml#L57-L66](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/pyproject.toml#L57-L66)
- Wire-level tests use a purpose-built threaded socket server with `text_response_server` / `basic_response_server` helpers, exercising chunked upload, conflicting Content-Length, RFC 1808 non-ASCII redirects and fragment handling. — [tests/testserver/server.py#L27-L70](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/tests/testserver/server.py#L27-L70) [tests/test_lowlevel.py#L24-L106](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/tests/test_lowlevel.py#L24-L106) [tests/test_lowlevel.py#L308-L406](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/tests/test_lowlevel.py#L308-L406)
- Digest auth is covered at the socket level by three dedicated tests: `test_digestauth_401_count_reset_on_redirect`, `test_digestauth_401_only_sent_once`, and `test_digestauth_only_on_4xx`. — [tests/test_lowlevel.py#L127-L289](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/tests/test_lowlevel.py#L127-L289) [src/requests/auth.py#L268-L319](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/auth.py#L268-L319)
- TLS paths are tested with committed fixture certs — expired server cert, valid server cert, and an mTLS client cert — including `test_different_connection_pool_for_mtls_settings` verifying pool-key separation. — [tests/certs/README.md](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/tests/certs/README.md) [tests/test_requests.py#L2940-L3055](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/tests/test_requests.py#L2940-L3055)
- A `nosan_server` fixture mints a CA and a commonName-only (no subjectAltName) certificate at runtime using `trustme`, backing the SNI/SAN warning tests. — [tests/conftest.py#L44-L66](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/tests/conftest.py#L44-L66) [tests/test_requests.py#L1030-L1040](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/tests/test_requests.py#L1030-L1040)
- `test_response_iter_lines_reentrant` is marked `@pytest.mark.xfail` and documents a known defect: `Response.iter_lines()` is not reentrant-safe, matching the docstring note on the method itself. — [tests/test_requests.py#L2200-L2208](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/tests/test_requests.py#L2200-L2208) [src/requests/models.py#L1005-L1015](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L1005-L1015)
- Behaviour differences between urllib3 1.x and 2.x are handled by skips keyed on `is_urllib3_1`, e.g. `test_content_length_for_string_data_counts_bytes` skips on 1.x because it encodes strings as latin-1 rather than utf-8. — [tests/test_requests.py#L3073-L3086](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/tests/test_requests.py#L3073-L3086) [src/requests/compat.py#L21-L27](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/compat.py#L21-L27) [src/requests/utils.py#L163-L167](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/utils.py#L163-L167)
- `adapters.py` has a single dedicated regression test (`test_request_url_handles_leading_path_separators`); the rest of the adapter is exercised only indirectly through `test_requests.py`. — [tests/test_adapters.py](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/tests/test_adapters.py) [src/requests/adapters.py#L565-L598](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/adapters.py#L565-L598)
- CI runs `make ci` (`pytest tests --junitxml=report.xml`) across 8 Python versions × 3 OSes, plus two special jobs: `no_chardet` uninstalls both detection libraries, and `urllib3` pins `urllib3<2`. — [.github/workflows/run-tests.yml#L10-L95](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/.github/workflows/run-tests.yml#L10-L95) [Makefile#L5-L8](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/Makefile#L5-L8)
- Typechecking is enforced in CI as a separate gate: pyright in `strict` mode over `include = ["src/requests"]`, run on Python 3.10 and 3.14 — note `tests/` is deliberately outside the typechecked surface. — [pyproject.toml#L106-L113](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/pyproject.toml#L106-L113) [.github/workflows/typecheck.yml#L24-L32](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/.github/workflows/typecheck.yml#L24-L32)
- Coverage is available locally via `make coverage` with a `.coveragerc` whose only directive omits `requests/packages/*` — a stale path given the package now lives under `src/requests/`; no minimum threshold is configured and no workflow uploads coverage. — [.coveragerc](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/.coveragerc) [Makefile#L13-L14](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/Makefile#L13-L14) [.github/workflows/run-tests.yml](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/.github/workflows/run-tests.yml)
- Lint runs as its own workflow executing pre-commit (`ruff-check --fix`, `ruff-format`, plus whitespace/merge-conflict hooks), with `tests/test_lowlevel.py` explicitly excluded from `ruff-format`. — [.pre-commit-config.yaml#L1-L23](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/.pre-commit-config.yaml#L1-L23) [.github/workflows/lint.yml](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/.github/workflows/lint.yml)

## 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()`.
- `should_strip_auth` intentionally keeps the `Authorization` header across an http:80 → https:443 redirect on the same host; the comment states this is not RFC 7235 behaviour and is retained only for backwards compatibility. — [src/requests/sessions.py#L154-L184](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L154-L184)
- `rebuild_proxies` reconstructs `Proxy-Authorization` from proxy URL credentials but skips it when the target scheme starts with `https`, explicitly to avoid leaking the header into a TLS tunnel — a subtle invariant easy to break in a refactor. — [src/requests/sessions.py#L334-L368](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L334-L368)
- Any URL containing a colon whose scheme does not start with `http` bypasses all URL preparation and is assigned verbatim to `self.url`, so `mailto:`/`data:`-style inputs skip IDNA, requoting and host validation entirely. — [src/requests/models.py#L503-L509](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L503-L509)
- `verify=False` disables certificate and hostname validation at the urllib3 level (`cert_reqs = 'CERT_NONE'`, ca_certs cleared); the Session docstring is explicit that this exposes callers to MitM and is for testing only. — [src/requests/adapters.py#L307-L345](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/adapters.py#L307-L345) [src/requests/sessions.py#L466-L478](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L466-L478)
- `trust_env=True` by default means an attacker-controlled or accidentally-set `REQUESTS_CA_BUNDLE`/`CURL_CA_BUNDLE` silently replaces the trust store for every request on the session. — [src/requests/sessions.py#L487-L490](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L487-L490) [src/requests/sessions.py#L852-L860](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L852-L860)
- `get_netrc_auth` reads credentials from a path taken from the `$NETRC` environment variable without restriction, and swallows `NetrcParseError`/`OSError` unless `raise_errors=True` — failures to read credentials are silent by default. — [src/requests/utils.py#L231-L280](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/utils.py#L231-L280)
- `extract_zipped_paths` performs file IO outside the repo root: it walks a path upward looking for a zip archive, then extracts a member into a `tempfile.mkstemp()` file that is never cleaned up, returning that path to the caller. — [src/requests/utils.py#L290-L326](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/utils.py#L290-L326)
- `_basic_auth_str` still accepts non-string usernames/passwords, coercing them with a `DeprecationWarning` promising removal in 3.0.0; the source comment preserves it precisely because callers depend on it. — [src/requests/auth.py#L34-L76](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/auth.py#L34-L76)
- Digest auth uses MD5 by default when the server omits an `algorithm` directive; MD5/SHA-1 usage is flagged `usedforsecurity=False` to appease FIPS builds but the weak-hash negotiation is server-driven. — [src/requests/auth.py#L166-L186](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/auth.py#L166-L186)
- `handle_401` re-sends the authenticated request directly through `r.connection.send(prep, **kwargs)`, bypassing `Session.send` — so session-level cookie extraction, hooks, and redirect resolution do not run for that hop. — [src/requests/auth.py#L300-L317](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/auth.py#L300-L317) [src/requests/sessions.py#L752-L791](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L752-L791)
- `prepare_auth` blanket-applies `self.__dict__.update(r.__dict__)` from a user-supplied callable, meaning a third-party auth class can overwrite any attribute of the PreparedRequest including `url` and `hooks`. — [src/requests/models.py#L684-L697](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L684-L697)
- `LookupDict.__getitem__` deliberately falls through to `None` for unknown keys, so a typo'd `requests.codes['okey']` silently yields `None` instead of raising — a footgun with no test-enforced guard. — [src/requests/structures.py#L118-L122](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/structures.py#L118-L122)
- `HTTPAdapter.get_connection` is live deprecated code retained for subclass compatibility; it emits a `DeprecationWarning` and, unlike `get_connection_with_tls_context`, does not incorporate verify/cert into the pool key. — [src/requests/adapters.py#L512-L554](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/adapters.py#L512-L554) [src/requests/adapters.py#L455-L511](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/adapters.py#L455-L511)
- `compat.py` is largely legacy: it still exports `is_py2`, `is_py3`, `basestring`, `numeric_types` and `integer_types` for a Python 2 that the project no longer supports (`requires-python = ">=3.10"`), and shadows the builtin `str`/`bytes` names. — [src/requests/compat.py#L51-L115](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/compat.py#L51-L115) [pyproject.toml#L16](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/pyproject.toml#L16)
- `packages.py` mutates the global `sys.modules` table at import time to alias urllib3/idna/chardet under `requests.packages.*` — a process-wide side effect purely for pre-2.16 backwards compatibility. — [src/requests/packages.py](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/packages.py)
- `.coveragerc` omits `requests/packages/*`, a path that no longer exists since the move to `src/` layout, so the omit rule is dead configuration. (inferred) — [.coveragerc](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/.coveragerc) [pyproject.toml#L95-L96](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/pyproject.toml#L95-L96)
- Release-critical files are protected by CODEOWNERS requiring lead-maintainer review: `.github/workflows/`, `src/requests/__version__.py`, `HISTORY.md` and `pyproject.toml`. — [.github/CODEOWNERS](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/.github/CODEOWNERS)

## 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.
- `Session.mount(prefix, adapter)` is the registry: it inserts into the `self.adapters` OrderedDict then re-pops every shorter key so adapters stay in descending prefix-length order. — [src/requests/sessions.py#L888-L896](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L888-L896)
- `Session.get_adapter(url)` resolves by case-insensitive `startswith` over that ordered map, raising `InvalidSchema(f"No connection adapters were found for {url!r}")` when nothing matches — so registering a new scheme is purely a mount call. — [src/requests/sessions.py#L870-L881](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L870-L881) [src/requests/exceptions.py#L114-L116](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/exceptions.py#L114-L116)
- `BaseAdapter` defines the minimal contract — `send(request, stream, timeout, verify, cert, proxies)` and `close()` — that any replacement transport must implement. — [src/requests/adapters.py#L122-L156](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/adapters.py#L122-L156)
- Eight `HTTPAdapter` methods are documented as subclass-override points rather than user API: `init_poolmanager`, `proxy_manager_for`, `cert_verify`, `build_response`, `get_connection_with_tls_context`, `request_url`, `add_headers`, and `proxy_headers`. — [src/requests/adapters.py#L239-L268](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/adapters.py#L239-L268) [src/requests/adapters.py#L365-L372](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/adapters.py#L365-L372) [src/requests/adapters.py#L599-L632](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/adapters.py#L599-L632)
- `HTTPAdapter.add_headers()` is an intentional empty hook — 'As of v2.0 this does nothing by default, but is left for overriding by users that subclass' — and is called on every send with the full kwargs. — [src/requests/adapters.py#L599-L612](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/adapters.py#L599-L612) [src/requests/adapters.py#L648-L655](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/adapters.py#L648-L655)
- `init_poolmanager(**pool_kwargs)` forwards arbitrary kwargs to `urllib3.PoolManager`, which is the documented way to inject a custom `ssl_context` or TLS version. — [src/requests/adapters.py#L239-L267](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/adapters.py#L239-L267) [docs/user/advanced.rst#L968-L1055](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/docs/user/advanced.rst#L968-L1055)
- The hook registry is the `HOOKS = ['response']` list in `hooks.py`, with `default_hooks()` building `{event: []}` and `dispatch_hook()` chaining callables so each non-None return replaces the data for the next hook. — [src/requests/hooks.py#L23-L48](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/hooks.py#L23-L48)
- Hooks can be attached per-request (`hooks=` kwarg), per-session (`Session.hooks`), or programmatically via `RequestHooksMixin.register_hook` / `deregister_hook`, which both `Request` and `PreparedRequest` inherit. — [src/requests/models.py#L254-L282](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L254-L282) [src/requests/sessions.py#L456-L458](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L456-L458)
- Custom auth plugs in via `AuthBase`, whose `__call__(self, r) -> PreparedRequest` raises `NotImplementedError` by default; `prepare_auth` treats any non-2-tuple auth value as such a callable. — [src/requests/auth.py#L78-L83](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/auth.py#L78-L83) [src/requests/models.py#L681-L691](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L681-L691)
- `SessionRedirectMixin` is a separately-inheritable class exposing `get_redirect_target`, `should_strip_auth`, `resolve_redirects`, `rebuild_auth`, `rebuild_proxies` and `rebuild_method` — the tests subclass it directly as `RedirectSession` to exercise redirect logic without a network. — [src/requests/sessions.py#L127-L392](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L127-L392) [tests/test_requests.py#L2608](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/tests/test_requests.py#L2608)
- `get_redirect_target`'s docstring explicitly invites a custom mixin that caches the redirect location on the response as a private attribute, acknowledging the method is called redundantly per hop. — [src/requests/sessions.py#L134-L140](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L134-L140)
- `certs.where()` is the packager extension point: the module docstring instructs distributors to change its definition to return a separately packaged CA bundle. — [src/requests/certs.py](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/certs.py)
- Character-detection backends are pluggable at import: `_resolve_char_detection()` tries `chardet` then `charset_normalizer`, and `Response.apparent_encoding` degrades to a plain `utf-8` assumption if neither is installed. — [src/requests/compat.py#L36-L48](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/compat.py#L36-L48) [src/requests/models.py#L897-L904](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L897-L904)
- The status-code table is data-driven: adding a code means adding an entry to the `_codes` dict, and `_init()` generates both lowercase and uppercase attributes plus the docstring table automatically. — [src/requests/status_codes.py#L22-L108](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/status_codes.py#L22-L108) [src/requests/status_codes.py#L109-L128](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/status_codes.py#L109-L128)
- Custom HTTP verbs need no new code — `Session.request(method, ...)` uppercases whatever string it is given, which is the documented 'Custom Verbs' capability. — [src/requests/sessions.py#L618-L625](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L618-L625) [docs/user/advanced.rst#L926-L944](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/docs/user/advanced.rst#L926-L944)
- Optional extras are declared as installable feature flags in `pyproject.toml`: `socks` (PySocks), `use_chardet_on_py3` (chardet), and a now-empty `security` extra kept for compatibility. — [pyproject.toml#L49-L54](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/pyproject.toml#L49-L54) [tox.ini](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/tox.ini)

## 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.
- `.github/AI_POLICY.md` states 'Absolutely no unsupervised agentic tools' and requires a human copyright owner per contribution with no LLM bots in `Co-authored-by:` — do not open PRs against this upstream without explicit human sponsorship. — [.github/AI_POLICY.md#L1-L30](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/.github/AI_POLICY.md#L1-L30)
- Read first, in order: `src/requests/api.py` (surface), `src/requests/sessions.py` (orchestration), `src/requests/models.py` (value objects), `src/requests/adapters.py` (transport). These four contain the whole request lifecycle. — [src/requests/api.py](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/api.py) [src/requests/sessions.py](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py) [src/requests/models.py](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py) [src/requests/adapters.py](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/adapters.py)
- Local setup and test: `make init` installs `requirements-dev.txt` (editable `.[socks]`, pytest, pytest-httpbin, httpbin, trustme); `make test` runs `pytest tests`; `make ci` matches CI exactly. — [Makefile#L1-L8](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/Makefile#L1-L8) [requirements-dev.txt](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/requirements-dev.txt)
- Two additional gates must pass before merge: `pre-commit run --all-files` (ruff-check --fix and ruff-format) and `python -m pyright src/requests/` under `typeCheckingMode = "strict"`. — [.github/workflows/lint.yml#L18-L23](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/.github/workflows/lint.yml#L18-L23) [.github/workflows/typecheck.yml#L24-L32](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/.github/workflows/typecheck.yml#L24-L32) [pyproject.toml#L106-L108](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/pyproject.toml#L106-L108)
- Style is enforced by ruff with rule sets E/W/F/I/UP/T10 targeting py310, ignoring E203/E501/UP031, with per-file `E402,F401` exemptions for `src/requests/__init__.py` and `src/requests/compat.py` — do not 'clean up' those unused imports, they are the public re-export surface. — [pyproject.toml#L74-L92](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/pyproject.toml#L74-L92)
- `src/requests/_types.py` is explicitly documented as not part of the public API; it hosts the `RequestKwargs`/`GetKwargs`/`PostKwargs`/`DataKwargs` TypedDicts consumed via `Unpack` in `api.py` and `sessions.py`, so adding a request kwarg means editing both the runtime signature and the TypedDict. — [src/requests/_types.py#L1-L20](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/_types.py#L1-L20) [src/requests/api.py#L14-L25](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/api.py#L14-L25)
- `_types.is_prepared()` is a typing-only narrowing helper that returns `True` unconditionally at runtime; the many `assert _is_prepared(request)` lines are type assertions, not validation — do not convert them into runtime checks. — [src/requests/_types.py#L47-L53](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/_types.py#L47-L53) [src/requests/adapters.py#L633-L636](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/adapters.py#L633-L636)
- Safe edit boundary: new transport behaviour belongs in an `HTTPAdapter` subclass or the documented override methods, not in `Session.send`, because `Session.send` is also reached by `resolve_redirects` and any change there multiplies across every redirect hop. (inferred) — [src/requests/sessions.py#L752-L829](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L752-L829) [src/requests/adapters.py#L599-L632](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/adapters.py#L599-L632)
- Trap: `prepare_cookies` can only meaningfully run once per PreparedRequest — cookielib will not regenerate the header if `Cookie` already exists, which is why `resolve_redirects` pops the header before re-preparing. — [src/requests/models.py#L699-L720](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/models.py#L699-L720) [src/requests/sessions.py#L262-L273](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L262-L273)
- Trap: branch comments cite issue numbers as the rationale (e.g. #1084 for the 307/308 carve-out, #3490 for the purged headers, #1704 for POST-301→GET, #3772 for digest-only-on-4xx, #5229 for the `fileno` AttributeError). Look these up before changing the branch. — [src/requests/sessions.py#L246-L253](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L246-L253) [src/requests/sessions.py#L385-L387](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/sessions.py#L385-L387) [src/requests/utils.py#L181-L184](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/utils.py#L181-L184)
- Trap: `compat.py` rebinds `str` and `bytes` to the builtins and exports `basestring = (str, bytes)`; modules importing from it (`auth.py`, `utils.py`, `adapters.py`) rely on these shadows, so removing them is a wide breaking change. — [src/requests/compat.py#L108-L115](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/compat.py#L108-L115) [src/requests/auth.py#L20](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/auth.py#L20) [src/requests/adapters.py#L36](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/adapters.py#L36)
- Tests requiring network-like behaviour need the local httpbin fixture; use the `httpbin` fixture (which yields a URL-joining callable, not a string) rather than hardcoding `https://httpbin.org`. — [tests/conftest.py#L14-L41](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/tests/conftest.py#L14-L41)
- New doctests are load-bearing: because `--doctest-modules` is on, any `>>>` example added to a library docstring will be executed by `make test` and must actually pass. — [pyproject.toml#L100-L104](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/pyproject.toml#L100-L104) [src/requests/adapters.py#L177-L184](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/adapters.py#L177-L184)
- Version bumps live in `src/requests/__version__.py` (both `__version__` and the hex `__build__`), are resolved dynamically by setuptools, and are CODEOWNERS-gated — never bump as part of a feature PR. — [src/requests/__version__.py](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/__version__.py) [pyproject.toml#L97-L98](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/pyproject.toml#L97-L98) [.github/CODEOWNERS](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/.github/CODEOWNERS)
- Compatibility floor is Python 3.10 enforced in three places — `requires-python`, the `setup.py` guard that prints an error and exits 1, and ruff's `target-version = "py310"` — so no 3.11+ syntax. — [pyproject.toml#L16](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/pyproject.toml#L16) [setup.py#L1-L6](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/setup.py#L1-L6) [pyproject.toml#L69-L71](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/pyproject.toml#L69-L71)
- Behaviour must hold across both urllib3 major versions and with no charset library at all; CI has dedicated `urllib3 1.x` and `No Character Detection` jobs, and `compat.is_urllib3_1` exists for version-conditional code. — [.github/workflows/run-tests.yml#L42-L95](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/.github/workflows/run-tests.yml#L42-L95) [src/requests/compat.py#L21-L27](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/compat.py#L21-L27)

Source repository: https://github.com/psf/requests
Full evidence corpus: https://github.com/Codevetter/codevetter/tree/main/benchmarks/repo-unpacks/requests.json

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
