# Behavior traces — psf/requests

> Canonical page: https://codevetter.com/unpack/requests/behavior-traces

# Behavior traces — psf/requests

Scanned commit 611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60 (2026-09-21). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

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)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

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