# Contributor handoff — psf/requests

> Canonical page: https://codevetter.com/unpack/requests/agent-handoff

# Contributor handoff — psf/requests

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

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)

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)
