pyreqwest 0.5.1__cp313-cp313-musllinux_1_1_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyreqwest/__init__.py +3 -0
- pyreqwest/__init__.pyi +1 -0
- pyreqwest/_pyreqwest.cpython-313-aarch64-linux-musl.so +0 -0
- pyreqwest/bytes/__init__.py +16 -0
- pyreqwest/bytes/__init__.pyi +106 -0
- pyreqwest/client/__init__.py +21 -0
- pyreqwest/client/__init__.pyi +341 -0
- pyreqwest/client/types.py +54 -0
- pyreqwest/cookie/__init__.py +5 -0
- pyreqwest/cookie/__init__.pyi +174 -0
- pyreqwest/exceptions/__init__.py +193 -0
- pyreqwest/http/__init__.py +19 -0
- pyreqwest/http/__init__.pyi +344 -0
- pyreqwest/middleware/__init__.py +5 -0
- pyreqwest/middleware/__init__.pyi +12 -0
- pyreqwest/middleware/asgi/__init__.py +5 -0
- pyreqwest/middleware/asgi/asgi.py +168 -0
- pyreqwest/middleware/types.py +26 -0
- pyreqwest/multipart/__init__.py +5 -0
- pyreqwest/multipart/__init__.pyi +75 -0
- pyreqwest/proxy/__init__.py +5 -0
- pyreqwest/proxy/__init__.pyi +47 -0
- pyreqwest/py.typed +0 -0
- pyreqwest/pytest_plugin/__init__.py +8 -0
- pyreqwest/pytest_plugin/internal/__init__.py +0 -0
- pyreqwest/pytest_plugin/internal/assert_eq.py +6 -0
- pyreqwest/pytest_plugin/internal/assert_message.py +123 -0
- pyreqwest/pytest_plugin/internal/matcher.py +34 -0
- pyreqwest/pytest_plugin/internal/plugin.py +15 -0
- pyreqwest/pytest_plugin/mock.py +493 -0
- pyreqwest/pytest_plugin/types.py +26 -0
- pyreqwest/request/__init__.py +25 -0
- pyreqwest/request/__init__.pyi +200 -0
- pyreqwest/response/__init__.py +19 -0
- pyreqwest/response/__init__.pyi +157 -0
- pyreqwest/types.py +12 -0
- pyreqwest-0.5.1.dist-info/METADATA +106 -0
- pyreqwest-0.5.1.dist-info/RECORD +42 -0
- pyreqwest-0.5.1.dist-info/WHEEL +4 -0
- pyreqwest-0.5.1.dist-info/entry_points.txt +2 -0
- pyreqwest-0.5.1.dist-info/licenses/LICENSE +201 -0
- pyreqwest.libs/libgcc_s-39080030.so.1 +0 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""HTTP cookie types backed by Rust's cookie and cookie_store crates."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Sequence
|
|
4
|
+
from datetime import datetime, timedelta
|
|
5
|
+
from typing import Any, Literal, Self, TypeAlias, overload
|
|
6
|
+
|
|
7
|
+
from pyreqwest.http import Url
|
|
8
|
+
|
|
9
|
+
SameSite: TypeAlias = Literal["Strict", "Lax", "None"]
|
|
10
|
+
|
|
11
|
+
class Cookie:
|
|
12
|
+
"""An immutable HTTP cookie. Lightweight Python wrapper around the internal Rust cookie::Cookie type.
|
|
13
|
+
Use `with_*` methods to create modified copies of a Cookie.
|
|
14
|
+
|
|
15
|
+
See also Rust [docs](https://docs.rs/cookie/latest/cookie/struct.Cookie.html) for more details.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, name: str, value: str) -> None:
|
|
19
|
+
"""Create a cookie with the given name and value (no attributes)."""
|
|
20
|
+
|
|
21
|
+
@staticmethod
|
|
22
|
+
def parse(cookie: str) -> "Cookie":
|
|
23
|
+
"""Parses a Cookie from the given HTTP cookie header value string."""
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
def parse_encoded(cookie: str) -> "Cookie":
|
|
27
|
+
"""Like parse, but does percent-decoding of keys and values."""
|
|
28
|
+
|
|
29
|
+
@staticmethod
|
|
30
|
+
def split_parse(cookie: str) -> list["Cookie"]:
|
|
31
|
+
"""Parses the HTTP Cookie header, a series of cookie names and value separated by `;`."""
|
|
32
|
+
|
|
33
|
+
@staticmethod
|
|
34
|
+
def split_parse_encoded(cookie: str) -> list["Cookie"]:
|
|
35
|
+
"""Like split_parse, but does percent-decoding of keys and values."""
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def name(self) -> str:
|
|
39
|
+
"""Cookie name."""
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def value(self) -> str:
|
|
43
|
+
"""Raw cookie value as set (may contain surrounding whitespace)."""
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def value_trimmed(self) -> str:
|
|
47
|
+
"""Value with surrounding whitespace trimmed."""
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def http_only(self) -> bool:
|
|
51
|
+
"""Whether the HttpOnly attribute is set."""
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def secure(self) -> bool:
|
|
55
|
+
"""Whether the Secure attribute is set."""
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def same_site(self) -> SameSite | None:
|
|
59
|
+
"""SameSite attribute, or None if unspecified."""
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def partitioned(self) -> bool:
|
|
63
|
+
"""Whether the Partitioned attribute is set."""
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def max_age(self) -> timedelta | None:
|
|
67
|
+
"""Max-Age attribute duration, or None if not present."""
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def path(self) -> str | None:
|
|
71
|
+
"""Path attribute that scopes the cookie, or None if not present."""
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def domain(self) -> str | None:
|
|
75
|
+
"""Domain attribute that scopes the cookie, or None if not present."""
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def expires_datetime(self) -> datetime | None:
|
|
79
|
+
"""Absolute expiration time (Expires), or None if not present."""
|
|
80
|
+
|
|
81
|
+
def encode(self) -> str:
|
|
82
|
+
"""Returns cookie string with percent-encoding applied."""
|
|
83
|
+
|
|
84
|
+
def stripped(self) -> str:
|
|
85
|
+
"""Return just the 'name=value' pair."""
|
|
86
|
+
|
|
87
|
+
def with_name(self, name: str) -> Self:
|
|
88
|
+
"""Set name, returning a new Cookie."""
|
|
89
|
+
|
|
90
|
+
def with_value(self, value: str) -> Self:
|
|
91
|
+
"""Set value, returning a new Cookie."""
|
|
92
|
+
|
|
93
|
+
def with_http_only(self, http_only: bool) -> Self:
|
|
94
|
+
"""Set HttpOnly attribute, returning a new Cookie."""
|
|
95
|
+
|
|
96
|
+
def with_secure(self, secure: bool) -> Self:
|
|
97
|
+
"""Set Secure attribute, returning a new Cookie."""
|
|
98
|
+
|
|
99
|
+
def with_same_site(self, same_site: SameSite | None) -> Self:
|
|
100
|
+
"""Set SameSite attribute, returning a new Cookie."""
|
|
101
|
+
|
|
102
|
+
def with_partitioned(self, partitioned: bool) -> Self:
|
|
103
|
+
"""Set Partitioned attribute, returning a new Cookie."""
|
|
104
|
+
|
|
105
|
+
def with_max_age(self, max_age: timedelta | None) -> Self:
|
|
106
|
+
"""Set Max-Age attribute, returning a new Cookie."""
|
|
107
|
+
|
|
108
|
+
def with_path(self, path: str | None) -> Self:
|
|
109
|
+
"""Set Path attribute, returning a new Cookie."""
|
|
110
|
+
|
|
111
|
+
def with_domain(self, domain: str | None) -> Self:
|
|
112
|
+
"""Set Domain attribute, returning a new Cookie."""
|
|
113
|
+
|
|
114
|
+
def with_expires_datetime(self, expires: datetime | None) -> Self:
|
|
115
|
+
"""Set Expires attribute, returning a new Cookie."""
|
|
116
|
+
|
|
117
|
+
def __contains__(self, item: Any) -> bool: ...
|
|
118
|
+
def __copy__(self) -> Self: ...
|
|
119
|
+
def __hash__(self) -> int: ...
|
|
120
|
+
@overload
|
|
121
|
+
def __getitem__(self, index: int) -> str: ...
|
|
122
|
+
@overload
|
|
123
|
+
def __getitem__(self, index: slice) -> Sequence[str]: ...
|
|
124
|
+
def __len__(self) -> int: ...
|
|
125
|
+
def __eq__(self, other: object) -> bool: ...
|
|
126
|
+
def __lt__(self, other: object) -> bool: ...
|
|
127
|
+
def __le__(self, other: object) -> bool: ...
|
|
128
|
+
|
|
129
|
+
class CookieStore:
|
|
130
|
+
"""Thread-safe in-memory cookie store (domain/path aware). Mirrors the behavior of Rust's cookie_store.
|
|
131
|
+
|
|
132
|
+
See also Rust [docs](https://docs.rs/cookie_store/latest/cookie_store/struct.CookieStore.html) for more details.
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
def __init__(self) -> None:
|
|
136
|
+
"""Create an empty cookie store."""
|
|
137
|
+
|
|
138
|
+
def contains(self, domain: str, path: str, name: str) -> bool:
|
|
139
|
+
"""Returns true if the CookieStore contains an unexpired Cookie corresponding to the specified domain, path,
|
|
140
|
+
and name.
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
def contains_any(self, domain: str, path: str, name: str) -> bool:
|
|
144
|
+
"""Returns true if the CookieStore contains any (even an expired) Cookie corresponding to the specified
|
|
145
|
+
domain, path, and name.
|
|
146
|
+
"""
|
|
147
|
+
|
|
148
|
+
def get(self, domain: str, path: str, name: str) -> Cookie | None:
|
|
149
|
+
"""Returns a reference to the unexpired Cookie corresponding to the specified domain, path, and name."""
|
|
150
|
+
|
|
151
|
+
def get_any(self, domain: str, path: str, name: str) -> Cookie | None:
|
|
152
|
+
"""Returns a reference to the (possibly expired) Cookie corresponding to the specified domain, path, and
|
|
153
|
+
name.
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
def remove(self, domain: str, path: str, name: str) -> Cookie | None:
|
|
157
|
+
"""Removes a Cookie from the store, returning the Cookie if it was in the store."""
|
|
158
|
+
|
|
159
|
+
def matches(self, url: Url | str) -> list[Cookie]:
|
|
160
|
+
"""Returns a collection of references to unexpired cookies that path- and domain-match request_url, as well as
|
|
161
|
+
having HttpOnly and Secure attributes compatible with the request_url.
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
def insert(self, cookie: Cookie | str, request_url: Url | str) -> None:
|
|
165
|
+
"""Insert a cookie as if set by a response for request_url."""
|
|
166
|
+
|
|
167
|
+
def clear(self) -> None:
|
|
168
|
+
"""Remove all cookies from the store."""
|
|
169
|
+
|
|
170
|
+
def get_all_unexpired(self) -> list[Cookie]:
|
|
171
|
+
"""Return all unexpired cookies currently stored."""
|
|
172
|
+
|
|
173
|
+
def get_all_any(self) -> list[Cookie]:
|
|
174
|
+
"""Return all cookies in the store, including expired ones."""
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"""Exception classes."""
|
|
2
|
+
|
|
3
|
+
from json import JSONDecodeError as JSONDecodeError_
|
|
4
|
+
from typing import Any, Generic, TypedDict, TypeVar
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Cause(TypedDict):
|
|
8
|
+
"""A cause of an error."""
|
|
9
|
+
|
|
10
|
+
message: str
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CauseErrorDetails(TypedDict):
|
|
14
|
+
"""Details for errors that may have causes."""
|
|
15
|
+
|
|
16
|
+
causes: list[Cause] | None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class StatusErrorDetails(TypedDict):
|
|
20
|
+
"""Details for errors that have an associated HTTP status code."""
|
|
21
|
+
|
|
22
|
+
status: int
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
T = TypeVar("T", bound=CauseErrorDetails | StatusErrorDetails)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class PyreqwestError(Exception):
|
|
29
|
+
"""Base class for all pyreqwest errors."""
|
|
30
|
+
|
|
31
|
+
def __init__(self, message: str, *args: Any) -> None:
|
|
32
|
+
"""Internally initialized."""
|
|
33
|
+
assert isinstance(message, str)
|
|
34
|
+
Exception.__init__(self, message, *args)
|
|
35
|
+
self.message = message
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class DetailedPyreqwestError(PyreqwestError, Generic[T]):
|
|
39
|
+
"""Base class for all pyreqwest errors with details.
|
|
40
|
+
|
|
41
|
+
Details may be available in `details`.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def __init__(self, message: str, details: T) -> None:
|
|
45
|
+
"""Internally initialized."""
|
|
46
|
+
assert isinstance(details, dict)
|
|
47
|
+
PyreqwestError.__init__(self, message, details)
|
|
48
|
+
self.details = details
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class RequestError(DetailedPyreqwestError[T], Generic[T]):
|
|
52
|
+
"""Error while processing a request.
|
|
53
|
+
|
|
54
|
+
Details may be available in `details`.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class StatusError(RequestError[StatusErrorDetails]):
|
|
59
|
+
"""Error due to HTTP 4xx or 5xx status code. Raised when `error_for_status` is enabled.
|
|
60
|
+
|
|
61
|
+
The status code is available in `details["status"]`.
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class RedirectError(RequestError[CauseErrorDetails]):
|
|
66
|
+
"""Error due to too many redirects. Raised when `max_redirects` is exceeded.
|
|
67
|
+
|
|
68
|
+
Cause details may be available in `details["causes"]`.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class DecodeError(RequestError[CauseErrorDetails]):
|
|
73
|
+
"""Error while decoding the response.
|
|
74
|
+
|
|
75
|
+
Cause details may be available in `details["causes"]`.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class BodyDecodeError(DecodeError):
|
|
80
|
+
"""Error while decoding the request or response body.
|
|
81
|
+
|
|
82
|
+
Cause details may be available in `details["causes"]`.
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class JSONDecodeError(BodyDecodeError, JSONDecodeError_):
|
|
87
|
+
"""Error while decoding the response body as JSON.
|
|
88
|
+
|
|
89
|
+
This corresponds to Python's built-in `json.JSONDecodeError`. With the difference that `pos` and `colno` are byte
|
|
90
|
+
offsets instead of UTF8 char offsets. This difference is for efficient error handling (avoiding UTF8 conversions).
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
def __init__(self, message: str, details: dict[str, Any]) -> None:
|
|
94
|
+
"""Internally initialized."""
|
|
95
|
+
assert isinstance(details, dict)
|
|
96
|
+
assert isinstance(details["doc"], str) and isinstance(details["pos"], int)
|
|
97
|
+
JSONDecodeError_.__init__(self, message, details["doc"], details["pos"])
|
|
98
|
+
BodyDecodeError.__init__(self, message, {"causes": details["causes"]})
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class TransportError(RequestError[CauseErrorDetails]):
|
|
102
|
+
"""Error while processing the transport layer.
|
|
103
|
+
|
|
104
|
+
Cause details may be available in `details["causes"]`.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class RequestTimeoutError(TransportError, TimeoutError):
|
|
109
|
+
"""Error due to a timeout.
|
|
110
|
+
|
|
111
|
+
This indicates that the timeout configured for the request was reached.
|
|
112
|
+
Cause details may be available in `details["causes"]`.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class NetworkError(TransportError):
|
|
117
|
+
"""Error due to a network failure.
|
|
118
|
+
|
|
119
|
+
This indicates that the request could not be completed due to a network failure.
|
|
120
|
+
Cause details may be available in `details["causes"]`.
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class ConnectTimeoutError(RequestTimeoutError):
|
|
125
|
+
"""Timeout while connecting.
|
|
126
|
+
|
|
127
|
+
Cause details may be available in `details["causes"]`.
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class ReadTimeoutError(RequestTimeoutError):
|
|
132
|
+
"""Timeout while reading body.
|
|
133
|
+
|
|
134
|
+
Cause details may be available in `details["causes"]`.
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class WriteTimeoutError(RequestTimeoutError):
|
|
139
|
+
"""Timeout while sending body.
|
|
140
|
+
|
|
141
|
+
Cause details may be available in `details["causes"]`.
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class PoolTimeoutError(RequestTimeoutError):
|
|
146
|
+
"""Timeout while acquiring a connection from the pool.
|
|
147
|
+
|
|
148
|
+
Cause details may be available in `details["causes"]`.
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class ConnectError(NetworkError):
|
|
153
|
+
"""Network error while connecting.
|
|
154
|
+
|
|
155
|
+
Cause details may be available in `details["causes"]`.
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class ReadError(NetworkError):
|
|
160
|
+
"""Network error while reading body.
|
|
161
|
+
|
|
162
|
+
Cause details may be available in `details["causes"]`.
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class WriteError(NetworkError):
|
|
167
|
+
"""Network error while sending body.
|
|
168
|
+
|
|
169
|
+
Cause details may be available in `details["causes"]`.
|
|
170
|
+
"""
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class ClientClosedError(RequestError[CauseErrorDetails]):
|
|
174
|
+
"""Error due to user closing the client while request was being processed.
|
|
175
|
+
|
|
176
|
+
Cause details may be available in `details["causes"]`.
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class BuilderError(DetailedPyreqwestError[CauseErrorDetails], ValueError):
|
|
181
|
+
"""Error while building a request.
|
|
182
|
+
|
|
183
|
+
Cause details may be available in `details["causes"]`.
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class RequestPanicError(RequestError[CauseErrorDetails]):
|
|
188
|
+
"""Error due to a panic in the request processing.
|
|
189
|
+
|
|
190
|
+
This indicates a bug in pyreqwest or one of its dependencies.
|
|
191
|
+
Also, might be raised due to incorrect ProxyBuilder.custom implementation (limitation in reqwest error handling).
|
|
192
|
+
Cause details may be available in `details["causes"]`.
|
|
193
|
+
"""
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""HTTP utils classes and types."""
|
|
2
|
+
|
|
3
|
+
from pyreqwest._pyreqwest.http import (
|
|
4
|
+
HeaderMap,
|
|
5
|
+
HeaderMapItemsView,
|
|
6
|
+
HeaderMapKeysView,
|
|
7
|
+
HeaderMapValuesView,
|
|
8
|
+
Mime,
|
|
9
|
+
Url,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
__all__ = [ # noqa: RUF022
|
|
13
|
+
"Url",
|
|
14
|
+
"HeaderMap",
|
|
15
|
+
"Mime",
|
|
16
|
+
"HeaderMapItemsView",
|
|
17
|
+
"HeaderMapKeysView",
|
|
18
|
+
"HeaderMapValuesView",
|
|
19
|
+
]
|