-
-
Notifications
You must be signed in to change notification settings - Fork 9.8k
Add inline types to Requests #7272
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
nateprewitt
wants to merge
9
commits into
main
Choose a base branch
from
inline_types_rfc
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
412bc3d
Add inline types to Requests
nateprewitt 781caba
Fix accidental reversions during squash
nateprewitt b686e75
Add kwargs TypedDicts and Unpack typing
nateprewitt 5204ac3
Constrain Response attribute types
nateprewitt 541fb87
Add Final annotations
nateprewitt 4a6d321
Test oldest and newest version for typing
nateprewitt 5c93c71
Make length positional like io.Reader
nateprewitt f651c9b
Fix Hook types
nateprewitt 09d56f0
Fix extraneous assignments from cleanup
nateprewitt File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| name: Type Check | ||
|
|
||
| on: [push, pull_request] | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| typecheck: | ||
| runs-on: ubuntu-24.04 | ||
| timeout-minutes: 10 | ||
| strategy: | ||
| matrix: | ||
| python-version: ["3.10", "3.14"] | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
| with: | ||
| persist-credentials: false | ||
|
|
||
| - name: Set up Python | ||
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | ||
| with: | ||
| python-version: ${{ matrix.python-version }} | ||
|
|
||
| - name: Install dependencies | ||
| run: | | ||
| python -m pip install pip==26.0.1 | ||
| python -m pip install -e . --group typecheck | ||
|
|
||
| - name: Run pyright | ||
| run: python -m pyright src/requests/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,170 @@ | ||
| """ | ||
| requests._types | ||
| ~~~~~~~~~~~~~~~ | ||
|
|
||
| This module contains type aliases used internally by the Requests library. | ||
| These types are not part of the public API and must not be relied upon | ||
| by external code. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from collections.abc import Callable, Iterable, Mapping, MutableMapping | ||
| from typing import ( | ||
| TYPE_CHECKING, | ||
| Any, | ||
| Protocol, | ||
| TypeAlias, | ||
| TypeVar, | ||
| runtime_checkable, | ||
| ) | ||
|
|
||
| _T_co = TypeVar("_T_co", covariant=True) | ||
|
|
||
|
|
||
| @runtime_checkable | ||
| class SupportsRead(Protocol[_T_co]): | ||
| def read(self, length: int = ..., /) -> _T_co: ... | ||
|
|
||
|
|
||
| @runtime_checkable | ||
| class SupportsItems(Protocol): | ||
| def items(self) -> Iterable[tuple[Any, Any]]: ... | ||
|
|
||
|
|
||
| # These are needed at runtime for default_hooks() return type | ||
| HookType: TypeAlias = Callable[["Response"], Any] | ||
| HooksInputType: TypeAlias = Mapping[str, Iterable[HookType] | HookType] | ||
|
|
||
|
|
||
| def is_prepared(request: PreparedRequest) -> TypeIs[_ValidatedRequest]: | ||
| """Verify a PreparedRequest has been fully prepared.""" | ||
| if TYPE_CHECKING: | ||
| return request.url is not None and request.method is not None | ||
| # noop at runtime to avoid AssertionError | ||
| return True | ||
|
|
||
|
|
||
| if TYPE_CHECKING: | ||
| from http.cookiejar import CookieJar | ||
| from typing import TypeAlias, TypedDict | ||
|
|
||
| from typing_extensions import TypeIs # move to typing when Python >= 3.13 | ||
|
|
||
| from .auth import AuthBase | ||
| from .cookies import RequestsCookieJar | ||
| from .models import PreparedRequest, Response | ||
| from .structures import CaseInsensitiveDict | ||
|
|
||
| class _ValidatedRequest(PreparedRequest): | ||
| """Subtype asserting a PreparedRequest has been fully prepared before calling. | ||
|
|
||
| The override suppression is required because mutable attribute types are | ||
| invariant (Liskov), but we only narrow after preparation is complete. This | ||
| is the explicit contract for Requests but Python's typing doesn't have a | ||
| better way to represent the requirement. | ||
| """ | ||
|
|
||
| url: str # type: ignore[reportIncompatibleVariableOverride] | ||
| method: str # type: ignore[reportIncompatibleVariableOverride] | ||
|
|
||
| # Type aliases for core API concepts (ordered by request() signature) | ||
| UriType: TypeAlias = str | bytes | ||
|
|
||
| _ParamsMappingKeyType: TypeAlias = str | bytes | int | float | ||
| _ParamsMappingValueType: TypeAlias = ( | ||
| str | bytes | int | float | Iterable[str | bytes | int | float] | None | ||
| ) | ||
| ParamsType: TypeAlias = ( | ||
| Mapping[_ParamsMappingKeyType, _ParamsMappingValueType] | ||
| | tuple[tuple[_ParamsMappingKeyType, _ParamsMappingValueType], ...] | ||
| | Iterable[tuple[_ParamsMappingKeyType, _ParamsMappingValueType]] | ||
| | str | ||
| | bytes | ||
| | None | ||
| ) | ||
|
|
||
| KVDataType: TypeAlias = Iterable[tuple[Any, Any]] | Mapping[Any, Any] | ||
|
|
||
| EncodableDataType: TypeAlias = KVDataType | str | bytes | SupportsRead[str | bytes] | ||
|
|
||
| DataType: TypeAlias = ( | ||
| KVDataType | ||
| | Iterable[bytes | str] | ||
| | str | ||
| | bytes | ||
| | SupportsRead[str | bytes] | ||
| | None | ||
| ) | ||
|
|
||
| BodyType: TypeAlias = ( | ||
| bytes | str | Iterable[bytes | str] | SupportsRead[bytes | str] | None | ||
| ) | ||
|
|
||
| HeadersType: TypeAlias = CaseInsensitiveDict[str] | Mapping[str, str | bytes] | ||
| HeadersUpdateType: TypeAlias = Mapping[str, str | bytes | None] | ||
|
|
||
| CookiesType: TypeAlias = RequestsCookieJar | Mapping[str, str] | ||
|
|
||
| # Building blocks for FilesType | ||
| _FileName: TypeAlias = str | None | ||
| _FileContent: TypeAlias = SupportsRead[str | bytes] | str | bytes | ||
| _FileSpecBasic: TypeAlias = tuple[_FileName, _FileContent] | ||
| _FileSpecWithContentType: TypeAlias = tuple[_FileName, _FileContent, str] | ||
| _FileSpecWithHeaders: TypeAlias = tuple[ | ||
| _FileName, _FileContent, str, CaseInsensitiveDict[str] | Mapping[str, str] | ||
| ] | ||
| _FileSpec: TypeAlias = ( | ||
| _FileContent | _FileSpecBasic | _FileSpecWithContentType | _FileSpecWithHeaders | ||
| ) | ||
| FilesType: TypeAlias = ( | ||
| Mapping[str, _FileSpec] | Iterable[tuple[str, _FileSpec]] | None | ||
| ) | ||
|
|
||
| AuthType: TypeAlias = ( | ||
| tuple[str, str] | AuthBase | Callable[[PreparedRequest], PreparedRequest] | None | ||
| ) | ||
|
|
||
| TimeoutType: TypeAlias = float | tuple[float | None, float | None] | None | ||
| ProxiesType: TypeAlias = MutableMapping[str, str] | ||
| HooksType: TypeAlias = dict[str, list[HookType]] | None | ||
| VerifyType: TypeAlias = bool | str | ||
| CertType: TypeAlias = str | tuple[str, str] | None | ||
| JsonType: TypeAlias = ( | ||
| None | bool | int | float | str | list["JsonType"] | dict[str, "JsonType"] | ||
| ) | ||
|
|
||
| # TypedDicts for Unpack kwargs (PEP 692) | ||
|
|
||
| class BaseRequestKwargs(TypedDict, total=False): | ||
| headers: Mapping[str, str | bytes] | None | ||
| cookies: RequestsCookieJar | CookieJar | dict[str, str] | None | ||
| files: FilesType | ||
| auth: AuthType | ||
| timeout: TimeoutType | ||
| allow_redirects: bool | ||
| proxies: dict[str, str] | None | ||
| hooks: HooksType | ||
| stream: bool | None | ||
| verify: VerifyType | None | ||
| cert: CertType | ||
|
|
||
| class RequestKwargs(BaseRequestKwargs, total=False): | ||
| """kwargs for request(), options(), head(), delete().""" | ||
|
|
||
| params: ParamsType | ||
| data: DataType | ||
| json: JsonType | ||
|
|
||
| class GetKwargs(BaseRequestKwargs, total=False): | ||
| data: DataType | ||
| json: JsonType | ||
|
|
||
| class PostKwargs(BaseRequestKwargs, total=False): | ||
| params: ParamsType | ||
|
|
||
| class DataKwargs(BaseRequestKwargs, total=False): | ||
| """kwargs for put(), patch().""" | ||
|
|
||
| params: ParamsType | ||
| json: JsonType | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it would be lovely if these type hints were available through a "public" interface, since I've often written code that wraps a requests call, where I would want to use the ParamsType to annotate my own code
I say public in quotes because I could import this, but it feels wrong
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree having some form of public hints for the top-level APIs is potentially warranted. I started very conservative here because often times new code goes into Requests and immediately comes someone else's critical dependency. I don't want to create a binding contract that people start surfacing in their code, and then "break" when we need to update/tweak it.
I think once we're really happy with the types, the main argument parameters could be surfaced. I've deferred that for now until we can make a more informed decision. Presumably everything calling in is already typed sufficiently.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fair! thank you for doing this and considering carefully :)