Skip to content

RFC: Adding inline type annotations to Requests #7271

@nateprewitt

Description

@nateprewitt

Tip

Want to skip the background? Jump to How to test or What feedback we need.

Motivation

Requests has notably been without inline type annotations since type hints entered the Python ecosystem. We've had aspirations of bringing Requests up to par with community standards for a few years but have been unable to properly express the typing contract. This change doesn't solve all of those gaps, but does make progress towards that goal.

The maintainers of typeshed have provided an incredibly useful alternative for Requests typing for quite a while. There are some limitations of type stubs that can't fully express the Requests API though. Cases like iter_content having different return typing based on input, or PreparedRequests needing to be state checked can't be easily expressed. Inline annotations let us own the type contract alongside the code and should help drive better development decisions in the future.

For most use cases, the public API should be accurately typed. There are some internals that are currently suppressed because they need discussion, but the package passes pyright strict mode without errors. We've flagged where the sharp edges are and can refine those over time.

The main goal is to start this migration with community input to make sure we're covering all usage. The "magic" of Requests is that it's very flexible, which also means people use it in creative ways the maintainers don't always anticipate. We want to identify those edge cases early in this process before the types ship in a release.

What's changed

  • Every class, function, method, and module-level variable in src/requests/ is annotated
  • No public API changes
  • Passes typechecking with pyright strict mode
  • New internal _types.py module for shared type aliases and Protocols
  • from __future__ import annotations applied everywhere
  • There are some minor bug fixes included, but they address legitimate defects in Requests that resulted in unhandled runtime exceptions.

Design philosophy

Types should describe the code, not change behavior. This was the primary goal in this process, not adding new constraints.

Where typing forced a choice between runtime strictness and backward compatibility, we chose backward compatibility. That's the lens I'd view this through when things may differ from expectations.

How to test

Install from the branch:

python -m pip install git+https://github.com/psf/requests.git@inline_types_rfc

or

uv pip install git+https://github.com/psf/requests.git@inline_types_rfc

Then:

  • Run your existing test suite
  • Run pyright or mypy against your code that uses Requests
  • Try your usual Requests patterns and see if anything doesn't typecheck

What feedback we're looking for

Important

We want feedback from people who actively maintain projects that depend on Requests or use it heavily. Please share your real experience testing this against your code.

Comments that are clearly AI-generated will be hidden or removed. This is a library written "for Humans". The conversation is between maintainers and users, not a prompt.

If you do find issues, we want to hear about it. Specifically:

  • Compatibility: Does this break anything in your project? Especially if you're doing something unusual with Requests internals.
  • Type accuracy: Do the types match how you actually use the API? Are return types too narrow or too wide?
  • Missing coverage: Is there a pattern you use that we're not covering?

We're not looking for style opinions on the annotations themselves or nitpicks on suppression comments. Those are tracked on the PR.

Known limitations

  • str | bytes URL handling: Requests has historically accepted both str and bytes for URLs. The typing currently declares str only. This is a known gap we're still working through.
  • urllib3 stub gaps: Some type: ignore comments exist because urllib3's type stubs don't fully cover all the methods we use.
  • Optional dependency typing: chardet/charset_normalizer detection is imperfect under strict mode since these are optional imports.
  • RequestsCookieJar and MutableMapping: The RequestsCookieJar's conformance to MutableMapping has some known mismatches (extended method signatures, __iter__ yielding Cookie objects instead of strings) that date back to when the class was first written against the Python 2.6 ABC contract. The current types don't fix that, but should be clearer about what the class does. Future work may be able to refine this further.

For more details and rationale, see the PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions