Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e83187b8f | ||
|
|
84d10f0be8 | ||
|
|
b7b549b545 | ||
|
|
e511bc7277 | ||
|
|
5691f59613 | ||
|
|
2144213c30 | ||
|
|
6404f345e5 |
22
HISTORY.md
22
HISTORY.md
@@ -6,6 +6,28 @@ dev
|
||||
|
||||
- \[Short description of non-trivial change.\]
|
||||
|
||||
|
||||
2.34.2 (2026-05-14)
|
||||
-------------------
|
||||
- Moved `headers` input type back to `Mapping` to avoid invariance issues
|
||||
with `MutableMapping` and inferred dict types. Users calling
|
||||
`Request.headers.update()` may need to narrow typing in their code. (#7441)
|
||||
|
||||
|
||||
2.34.1 (2026-05-13)
|
||||
-------------------
|
||||
|
||||
**Bugfixes**
|
||||
- Widened `json` input type from `dict` and `list` to `Mapping`
|
||||
and `Sequence`. (#7436)
|
||||
- Changed `headers` input type to MutableMapping and removed `None` from
|
||||
`Request.headers` typing to improve handling for users. (#7431)
|
||||
- `Response.reason` moved from `str | None` to `str` to improve handling
|
||||
for users. (#7437)
|
||||
- Fixed a bug where some bodies with custom `__getattr__` implementations
|
||||
weren't being properly detected as Iterables. (#7433)
|
||||
|
||||
|
||||
2.34.0 (2026-05-11)
|
||||
-------------------
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
__title__ = "requests"
|
||||
__description__ = "Python HTTP for Humans."
|
||||
__url__ = "https://requests.readthedocs.io"
|
||||
__version__ = "2.34.0"
|
||||
__build__ = 0x023400
|
||||
__version__ = "2.34.2"
|
||||
__build__ = 0x023402
|
||||
__author__ = "Kenneth Reitz"
|
||||
__author_email__ = "me@kennethreitz.org"
|
||||
__license__ = "Apache-2.0"
|
||||
|
||||
@@ -9,7 +9,7 @@ by external code.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable, Iterable, Mapping, MutableMapping
|
||||
from collections.abc import Callable, Iterable, Mapping, MutableMapping, Sequence
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
@@ -109,8 +109,7 @@ if TYPE_CHECKING:
|
||||
bytes | str | Iterable[bytes | str] | SupportsRead[bytes | str] | None
|
||||
)
|
||||
|
||||
HeadersType: TypeAlias = CaseInsensitiveDict[str] | Mapping[str, str | bytes]
|
||||
HeadersUpdateType: TypeAlias = Mapping[str, str | bytes | None]
|
||||
HeadersType: TypeAlias = Mapping[str, str | bytes] | None
|
||||
|
||||
CookiesType: TypeAlias = RequestsCookieJar | Mapping[str, str]
|
||||
|
||||
@@ -139,13 +138,19 @@ if TYPE_CHECKING:
|
||||
VerifyType: TypeAlias = bool | str
|
||||
CertType: TypeAlias = str | tuple[str, str] | None
|
||||
JsonType: TypeAlias = (
|
||||
None | bool | int | float | str | list["JsonType"] | dict[str, "JsonType"]
|
||||
None
|
||||
| bool
|
||||
| int
|
||||
| float
|
||||
| str
|
||||
| Sequence["JsonType"]
|
||||
| Mapping[str, "JsonType"]
|
||||
)
|
||||
|
||||
# TypedDicts for Unpack kwargs (PEP 692)
|
||||
|
||||
class BaseRequestKwargs(TypedDict, total=False):
|
||||
headers: Mapping[str, str | bytes] | None
|
||||
headers: HeadersType
|
||||
cookies: RequestsCookieJar | CookieJar | dict[str, str] | None
|
||||
files: FilesType
|
||||
auth: AuthType
|
||||
|
||||
@@ -310,7 +310,7 @@ class Request(RequestHooksMixin):
|
||||
|
||||
method: str | None
|
||||
url: _t.UriType | None
|
||||
headers: CaseInsensitiveDict[str] | Mapping[str, str | bytes] | None
|
||||
headers: Mapping[str, str | bytes]
|
||||
files: _t.FilesType
|
||||
data: _t.DataType
|
||||
json: _t.JsonType
|
||||
@@ -322,7 +322,7 @@ class Request(RequestHooksMixin):
|
||||
self,
|
||||
method: str | None = None,
|
||||
url: _t.UriType | None = None,
|
||||
headers: Mapping[str, str | bytes] | None = None,
|
||||
headers: _t.HeadersType = None,
|
||||
files: _t.FilesType = None,
|
||||
data: _t.DataType = None,
|
||||
params: _t.ParamsType = None,
|
||||
@@ -596,9 +596,9 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
|
||||
if not isinstance(body, bytes):
|
||||
body = body.encode("utf-8")
|
||||
|
||||
if isinstance(data, Iterable) and not isinstance(
|
||||
data, (str, bytes, list, tuple, Mapping)
|
||||
):
|
||||
# data that proxies attributes to underlying objects needs hasattr
|
||||
is_iterable = isinstance(data, Iterable) or hasattr(data, "__iter__")
|
||||
if is_iterable and not isinstance(data, (str, bytes, list, tuple, Mapping)):
|
||||
try:
|
||||
length = super_len(data)
|
||||
except (TypeError, AttributeError, UnsupportedOperation):
|
||||
@@ -741,7 +741,7 @@ class Response:
|
||||
url: str
|
||||
encoding: str | None
|
||||
history: list[Response]
|
||||
reason: str | None
|
||||
reason: str
|
||||
cookies: RequestsCookieJar
|
||||
elapsed: datetime.timedelta
|
||||
request: PreparedRequest
|
||||
@@ -790,7 +790,7 @@ class Response:
|
||||
self.history = []
|
||||
|
||||
#: Textual reason of responded HTTP Status, e.g. "Not Found" or "OK".
|
||||
self.reason = None
|
||||
self.reason = None # type: ignore[assignment]
|
||||
|
||||
#: A CookieJar of Cookies the server sent back.
|
||||
self.cookies = cookiejar_from_dict({})
|
||||
|
||||
@@ -560,7 +560,7 @@ class Session(SessionRedirectMixin):
|
||||
url: _t.UriType,
|
||||
params: _t.ParamsType = None,
|
||||
data: _t.DataType = None,
|
||||
headers: Mapping[str, str | bytes] | None = None,
|
||||
headers: _t.HeadersType = None,
|
||||
cookies: RequestsCookieJar | CookieJar | dict[str, str] | None = None,
|
||||
files: _t.FilesType = None,
|
||||
auth: _t.AuthType = None,
|
||||
|
||||
@@ -2073,6 +2073,21 @@ class TestRequests:
|
||||
|
||||
assert "Unable to rewind request body" in str(e)
|
||||
|
||||
def test_getattr_proxy_stream_follows_redirect(self, httpbin):
|
||||
"""Ensure stream wrappers that don't implement __iter__ directly are still detected."""
|
||||
|
||||
class AttrProxy:
|
||||
def __init__(self):
|
||||
self._file = io.BytesIO(b"data")
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self._file, name)
|
||||
|
||||
r = requests.post(
|
||||
httpbin("redirect-to?url=/post&status_code=307"), data=AttrProxy()
|
||||
)
|
||||
assert r.json()["data"] == "data"
|
||||
|
||||
def _patch_adapter_gzipped_redirect(self, session, url):
|
||||
adapter = session.get_adapter(url=url)
|
||||
org_build_response = adapter.build_response
|
||||
|
||||
Reference in New Issue
Block a user