punkreq 0.1.0__tar.gz
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.
- punkreq-0.1.0/.gitignore +13 -0
- punkreq-0.1.0/LICENSE +28 -0
- punkreq-0.1.0/PKG-INFO +145 -0
- punkreq-0.1.0/README.md +113 -0
- punkreq-0.1.0/punkreq/__init__.py +84 -0
- punkreq-0.1.0/punkreq/_auth.py +43 -0
- punkreq-0.1.0/punkreq/_client.py +591 -0
- punkreq-0.1.0/punkreq/_config.py +174 -0
- punkreq-0.1.0/punkreq/_connect.py +131 -0
- punkreq-0.1.0/punkreq/_content.py +180 -0
- punkreq-0.1.0/punkreq/_cookies.py +189 -0
- punkreq-0.1.0/punkreq/_decoders.py +289 -0
- punkreq-0.1.0/punkreq/_exceptions.py +177 -0
- punkreq-0.1.0/punkreq/_headers.py +118 -0
- punkreq-0.1.0/punkreq/_models.py +404 -0
- punkreq-0.1.0/punkreq/_multipart.py +160 -0
- punkreq-0.1.0/punkreq/_pool.py +295 -0
- punkreq-0.1.0/punkreq/_proxies.py +240 -0
- punkreq-0.1.0/punkreq/_redirects.py +70 -0
- punkreq-0.1.0/punkreq/_transport.py +249 -0
- punkreq-0.1.0/punkreq/_urls.py +401 -0
- punkreq-0.1.0/punkreq/_version.py +1 -0
- punkreq-0.1.0/punkreq/asyncio.py +13 -0
- punkreq-0.1.0/punkreq/py.typed +0 -0
- punkreq-0.1.0/punkreq/tonio.py +13 -0
- punkreq-0.1.0/pyproject.toml +116 -0
- punkreq-0.1.0/tests/test_client.py +653 -0
- punkreq-0.1.0/tests/test_config.py +88 -0
- punkreq-0.1.0/tests/test_content.py +113 -0
- punkreq-0.1.0/tests/test_cookies.py +78 -0
- punkreq-0.1.0/tests/test_decoders.py +127 -0
- punkreq-0.1.0/tests/test_e2e_client.py +122 -0
- punkreq-0.1.0/tests/test_e2e_transport.py +169 -0
- punkreq-0.1.0/tests/test_exceptions.py +74 -0
- punkreq-0.1.0/tests/test_headers.py +127 -0
- punkreq-0.1.0/tests/test_models.py +243 -0
- punkreq-0.1.0/tests/test_multipart.py +95 -0
- punkreq-0.1.0/tests/test_pool.py +283 -0
- punkreq-0.1.0/tests/test_proxies.py +262 -0
- punkreq-0.1.0/tests/test_redirects.py +126 -0
- punkreq-0.1.0/tests/test_urls.py +221 -0
punkreq-0.1.0/.gitignore
ADDED
punkreq-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
Copyright 2026 Giovanni Barillari
|
|
2
|
+
|
|
3
|
+
Redistribution and use in source and binary forms, with or without
|
|
4
|
+
modification, are permitted provided that the following conditions are
|
|
5
|
+
met:
|
|
6
|
+
|
|
7
|
+
1. Redistributions of source code must retain the above copyright
|
|
8
|
+
notice, this list of conditions and the following disclaimer.
|
|
9
|
+
|
|
10
|
+
2. Redistributions in binary form must reproduce the above copyright
|
|
11
|
+
notice, this list of conditions and the following disclaimer in the
|
|
12
|
+
documentation and/or other materials provided with the distribution.
|
|
13
|
+
|
|
14
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
15
|
+
contributors may be used to endorse or promote products derived from
|
|
16
|
+
this software without specific prior written permission.
|
|
17
|
+
|
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
19
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
20
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
|
21
|
+
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
22
|
+
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
23
|
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
|
24
|
+
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
25
|
+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
26
|
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
27
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
28
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
punkreq-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: punkreq
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: An async HTTP client built on httpunk
|
|
5
|
+
Project-URL: Homepage, https://github.com/gi0baro/punkreq
|
|
6
|
+
Project-URL: Funding, https://github.com/sponsors/gi0baro
|
|
7
|
+
Project-URL: Source, https://github.com/gi0baro/punkreq
|
|
8
|
+
Project-URL: Issues, https://github.com/gi0baro/punkreq/issues
|
|
9
|
+
Author-email: Giovanni Barillari <g@baro.dev>
|
|
10
|
+
License-Expression: BSD-3-Clause
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: async,client,http,httpunk
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Programming Language :: Python :: Free Threading :: 3 - Stable
|
|
24
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Requires-Dist: httpunk~=0.1.0
|
|
27
|
+
Provides-Extra: brotli
|
|
28
|
+
Requires-Dist: brotli~=1.2.0; extra == 'brotli'
|
|
29
|
+
Provides-Extra: tonio
|
|
30
|
+
Requires-Dist: tonio~=0.8.1; (python_version >= '3.14') and extra == 'tonio'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# punkreq
|
|
34
|
+
|
|
35
|
+
punkreq is an async HTTP client for Python, built on top of
|
|
36
|
+
[httpunk](https://github.com/gi0baro/httpunk).
|
|
37
|
+
|
|
38
|
+
> **Note:** punkreq is currently in alpha stage.
|
|
39
|
+
|
|
40
|
+
> **Note:** punkreq was built with substantial help from LLMs, under human supervision.
|
|
41
|
+
|
|
42
|
+
## In a nutshell
|
|
43
|
+
|
|
44
|
+
- Async only, with multiple runtime backends: import the client from the
|
|
45
|
+
module matching your runtime – `punkreq.asyncio` or `punkreq.tonio`
|
|
46
|
+
- HTTP/1.1 and HTTP/2, negotiated via TLS ALPN by default; HTTP/2 prior
|
|
47
|
+
knowledge with `http1=False`
|
|
48
|
+
- Connection pooling with keep-alive (h1) and multiplexing (h2)
|
|
49
|
+
- Transparent response decompression: gzip and deflate everywhere, zstd on
|
|
50
|
+
Python 3.14+, brotli with the `punkreq[brotli]` extra
|
|
51
|
+
- Conservative automatic retries — only requests the server provably never
|
|
52
|
+
processed (HTTP/2 graceful GOAWAY, refused streams, keep-alive races)
|
|
53
|
+
- Opt-in cookie jar, basic/bearer auth, multipart uploads
|
|
54
|
+
- HTTP proxies: `HTTP_PROXY`/`HTTPS_PROXY`/`NO_PROXY` environment support,
|
|
55
|
+
CONNECT tunneling for https destinations.
|
|
56
|
+
- Zero runtime dependencies beyond httpunk
|
|
57
|
+
|
|
58
|
+
## Installation
|
|
59
|
+
|
|
60
|
+
```shell
|
|
61
|
+
pip install punkreq
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Quickstart
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
import asyncio
|
|
68
|
+
from punkreq.asyncio import Client
|
|
69
|
+
|
|
70
|
+
async def main():
|
|
71
|
+
async with Client(base_url="https://api.example.com") as client:
|
|
72
|
+
response = await client.post("/items", json={"name": "widget"})
|
|
73
|
+
response.raise_for_status()
|
|
74
|
+
print(await response.json())
|
|
75
|
+
|
|
76
|
+
asyncio.run(main())
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
One-off requests without managing a client:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from punkreq import asyncio as punkreq
|
|
83
|
+
|
|
84
|
+
response = await punkreq.get("https://www.example.com")
|
|
85
|
+
print(response.status_code, response.headers["content-type"])
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Responses resolve as soon as the head arrives; the body is read on demand —
|
|
89
|
+
`await response.read()` / `text()` / `json()`, or streamed:
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
async with Client() as client:
|
|
93
|
+
response = await client.get("https://example.com/large.bin")
|
|
94
|
+
async for chunk in response.iter_bytes(chunk_size=65536):
|
|
95
|
+
...
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Reading to the end releases the connection automatically. When you might stop
|
|
99
|
+
early, use the request as a context manager so the response is always closed:
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
async with client.get("https://example.com/huge.ndjson") as response:
|
|
103
|
+
async for line in response.iter_lines():
|
|
104
|
+
if found(line):
|
|
105
|
+
break # the block exit closes the response and frees the connection
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Configuration
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
import punkreq
|
|
112
|
+
from punkreq.asyncio import Client
|
|
113
|
+
|
|
114
|
+
client = Client(
|
|
115
|
+
base_url="https://api.example.com",
|
|
116
|
+
headers={"x-api-key": "..."},
|
|
117
|
+
auth=("user", "pass"), # or punkreq.BearerAuth("token")
|
|
118
|
+
cookies={}, # enables the cookie jar (off by default)
|
|
119
|
+
timeout=punkreq.Timeout(5.0, total=30.0), # no timeouts by default
|
|
120
|
+
limits=punkreq.Limits(max_connections=100),
|
|
121
|
+
proxy="http://proxy.internal:3128", # env vars honored by default
|
|
122
|
+
verify=True, # bool | CA bundle path | ssl.SSLContext
|
|
123
|
+
)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
By default: redirects on (max 10), no timeouts, no cookie jar unless requested,
|
|
127
|
+
HTTP/2 enabled, 90s keep-alive expiry, unlimited connections.
|
|
128
|
+
|
|
129
|
+
`Timeout` has four independent fields — `total` (a deadline for the whole
|
|
130
|
+
request, redirects included), `connect`, `read` and `pool`. There is no write
|
|
131
|
+
timeout: request bodies are written full-duplex from background tasks, so
|
|
132
|
+
`total` is the bound for slow uploads.
|
|
133
|
+
|
|
134
|
+
## Backends
|
|
135
|
+
|
|
136
|
+
punkreq never asks for a backend at call sites — pick it by import:
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
from punkreq.asyncio import Client # asyncio, available everywhere
|
|
140
|
+
from punkreq.tonio import Client # tonio, free-threaded CPython >= 3.14 on Unix
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## License
|
|
144
|
+
|
|
145
|
+
punkreq is released under the BSD License.
|
punkreq-0.1.0/README.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# punkreq
|
|
2
|
+
|
|
3
|
+
punkreq is an async HTTP client for Python, built on top of
|
|
4
|
+
[httpunk](https://github.com/gi0baro/httpunk).
|
|
5
|
+
|
|
6
|
+
> **Note:** punkreq is currently in alpha stage.
|
|
7
|
+
|
|
8
|
+
> **Note:** punkreq was built with substantial help from LLMs, under human supervision.
|
|
9
|
+
|
|
10
|
+
## In a nutshell
|
|
11
|
+
|
|
12
|
+
- Async only, with multiple runtime backends: import the client from the
|
|
13
|
+
module matching your runtime – `punkreq.asyncio` or `punkreq.tonio`
|
|
14
|
+
- HTTP/1.1 and HTTP/2, negotiated via TLS ALPN by default; HTTP/2 prior
|
|
15
|
+
knowledge with `http1=False`
|
|
16
|
+
- Connection pooling with keep-alive (h1) and multiplexing (h2)
|
|
17
|
+
- Transparent response decompression: gzip and deflate everywhere, zstd on
|
|
18
|
+
Python 3.14+, brotli with the `punkreq[brotli]` extra
|
|
19
|
+
- Conservative automatic retries — only requests the server provably never
|
|
20
|
+
processed (HTTP/2 graceful GOAWAY, refused streams, keep-alive races)
|
|
21
|
+
- Opt-in cookie jar, basic/bearer auth, multipart uploads
|
|
22
|
+
- HTTP proxies: `HTTP_PROXY`/`HTTPS_PROXY`/`NO_PROXY` environment support,
|
|
23
|
+
CONNECT tunneling for https destinations.
|
|
24
|
+
- Zero runtime dependencies beyond httpunk
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```shell
|
|
29
|
+
pip install punkreq
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Quickstart
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
import asyncio
|
|
36
|
+
from punkreq.asyncio import Client
|
|
37
|
+
|
|
38
|
+
async def main():
|
|
39
|
+
async with Client(base_url="https://api.example.com") as client:
|
|
40
|
+
response = await client.post("/items", json={"name": "widget"})
|
|
41
|
+
response.raise_for_status()
|
|
42
|
+
print(await response.json())
|
|
43
|
+
|
|
44
|
+
asyncio.run(main())
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
One-off requests without managing a client:
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from punkreq import asyncio as punkreq
|
|
51
|
+
|
|
52
|
+
response = await punkreq.get("https://www.example.com")
|
|
53
|
+
print(response.status_code, response.headers["content-type"])
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Responses resolve as soon as the head arrives; the body is read on demand —
|
|
57
|
+
`await response.read()` / `text()` / `json()`, or streamed:
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
async with Client() as client:
|
|
61
|
+
response = await client.get("https://example.com/large.bin")
|
|
62
|
+
async for chunk in response.iter_bytes(chunk_size=65536):
|
|
63
|
+
...
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Reading to the end releases the connection automatically. When you might stop
|
|
67
|
+
early, use the request as a context manager so the response is always closed:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
async with client.get("https://example.com/huge.ndjson") as response:
|
|
71
|
+
async for line in response.iter_lines():
|
|
72
|
+
if found(line):
|
|
73
|
+
break # the block exit closes the response and frees the connection
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Configuration
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
import punkreq
|
|
80
|
+
from punkreq.asyncio import Client
|
|
81
|
+
|
|
82
|
+
client = Client(
|
|
83
|
+
base_url="https://api.example.com",
|
|
84
|
+
headers={"x-api-key": "..."},
|
|
85
|
+
auth=("user", "pass"), # or punkreq.BearerAuth("token")
|
|
86
|
+
cookies={}, # enables the cookie jar (off by default)
|
|
87
|
+
timeout=punkreq.Timeout(5.0, total=30.0), # no timeouts by default
|
|
88
|
+
limits=punkreq.Limits(max_connections=100),
|
|
89
|
+
proxy="http://proxy.internal:3128", # env vars honored by default
|
|
90
|
+
verify=True, # bool | CA bundle path | ssl.SSLContext
|
|
91
|
+
)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
By default: redirects on (max 10), no timeouts, no cookie jar unless requested,
|
|
95
|
+
HTTP/2 enabled, 90s keep-alive expiry, unlimited connections.
|
|
96
|
+
|
|
97
|
+
`Timeout` has four independent fields — `total` (a deadline for the whole
|
|
98
|
+
request, redirects included), `connect`, `read` and `pool`. There is no write
|
|
99
|
+
timeout: request bodies are written full-duplex from background tasks, so
|
|
100
|
+
`total` is the bound for slow uploads.
|
|
101
|
+
|
|
102
|
+
## Backends
|
|
103
|
+
|
|
104
|
+
punkreq never asks for a backend at call sites — pick it by import:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from punkreq.asyncio import Client # asyncio, available everywhere
|
|
108
|
+
from punkreq.tonio import Client # tonio, free-threaded CPython >= 3.14 on Unix
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## License
|
|
112
|
+
|
|
113
|
+
punkreq is released under the BSD License.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from ._auth import Auth, BasicAuth, BearerAuth
|
|
2
|
+
from ._client import USE_CLIENT_DEFAULT, ResponseHandle, UseClientDefault
|
|
3
|
+
from ._config import Limits, Proxy, Timeout
|
|
4
|
+
from ._content import AsyncByteStream, ByteStream
|
|
5
|
+
from ._cookies import Cookies
|
|
6
|
+
from ._exceptions import (
|
|
7
|
+
CloseError,
|
|
8
|
+
ConnectError,
|
|
9
|
+
ConnectTimeout,
|
|
10
|
+
CookieConflict,
|
|
11
|
+
DecodingError,
|
|
12
|
+
HTTPError,
|
|
13
|
+
HTTPStatusError,
|
|
14
|
+
InvalidURL,
|
|
15
|
+
LocalProtocolError,
|
|
16
|
+
NetworkError,
|
|
17
|
+
PoolTimeout,
|
|
18
|
+
ProtocolError,
|
|
19
|
+
ProxyError,
|
|
20
|
+
ReadError,
|
|
21
|
+
ReadTimeout,
|
|
22
|
+
RemoteProtocolError,
|
|
23
|
+
RequestError,
|
|
24
|
+
RequestNotRead,
|
|
25
|
+
StreamClosed,
|
|
26
|
+
StreamConsumed,
|
|
27
|
+
StreamError,
|
|
28
|
+
TimeoutException,
|
|
29
|
+
TooManyRedirects,
|
|
30
|
+
TransportError,
|
|
31
|
+
UnsupportedProtocol,
|
|
32
|
+
WriteError,
|
|
33
|
+
)
|
|
34
|
+
from ._headers import Headers
|
|
35
|
+
from ._models import Request, Response
|
|
36
|
+
from ._urls import URL, QueryParams
|
|
37
|
+
from ._version import __version__ as __version__
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
__all__ = [
|
|
41
|
+
"URL",
|
|
42
|
+
"USE_CLIENT_DEFAULT",
|
|
43
|
+
"AsyncByteStream",
|
|
44
|
+
"Auth",
|
|
45
|
+
"BasicAuth",
|
|
46
|
+
"BearerAuth",
|
|
47
|
+
"ByteStream",
|
|
48
|
+
"CloseError",
|
|
49
|
+
"ConnectError",
|
|
50
|
+
"ConnectTimeout",
|
|
51
|
+
"CookieConflict",
|
|
52
|
+
"Cookies",
|
|
53
|
+
"DecodingError",
|
|
54
|
+
"HTTPError",
|
|
55
|
+
"HTTPStatusError",
|
|
56
|
+
"Headers",
|
|
57
|
+
"InvalidURL",
|
|
58
|
+
"Limits",
|
|
59
|
+
"LocalProtocolError",
|
|
60
|
+
"NetworkError",
|
|
61
|
+
"PoolTimeout",
|
|
62
|
+
"ProtocolError",
|
|
63
|
+
"Proxy",
|
|
64
|
+
"ProxyError",
|
|
65
|
+
"QueryParams",
|
|
66
|
+
"ReadError",
|
|
67
|
+
"ReadTimeout",
|
|
68
|
+
"RemoteProtocolError",
|
|
69
|
+
"Request",
|
|
70
|
+
"RequestError",
|
|
71
|
+
"RequestNotRead",
|
|
72
|
+
"Response",
|
|
73
|
+
"ResponseHandle",
|
|
74
|
+
"StreamClosed",
|
|
75
|
+
"StreamConsumed",
|
|
76
|
+
"StreamError",
|
|
77
|
+
"Timeout",
|
|
78
|
+
"TimeoutException",
|
|
79
|
+
"TooManyRedirects",
|
|
80
|
+
"TransportError",
|
|
81
|
+
"UnsupportedProtocol",
|
|
82
|
+
"UseClientDefault",
|
|
83
|
+
"WriteError",
|
|
84
|
+
]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
import typing
|
|
5
|
+
|
|
6
|
+
from ._models import Request
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
__all__ = ["Auth", "BasicAuth", "BearerAuth"]
|
|
10
|
+
|
|
11
|
+
AuthTypes = typing.Union["Auth", typing.Tuple[str, str], None]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Auth:
|
|
15
|
+
"""Base class: override `apply` to mutate the outgoing request."""
|
|
16
|
+
|
|
17
|
+
def apply(self, request: Request) -> None:
|
|
18
|
+
raise NotImplementedError()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BasicAuth(Auth):
|
|
22
|
+
def __init__(self, username: str, password: str = "") -> None:
|
|
23
|
+
token = base64.b64encode(f"{username}:{password}".encode()).decode("ascii")
|
|
24
|
+
self._header = f"Basic {token}"
|
|
25
|
+
|
|
26
|
+
def apply(self, request: Request) -> None:
|
|
27
|
+
request.headers["authorization"] = self._header
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class BearerAuth(Auth):
|
|
31
|
+
def __init__(self, token: str) -> None:
|
|
32
|
+
self._header = f"Bearer {token}"
|
|
33
|
+
|
|
34
|
+
def apply(self, request: Request) -> None:
|
|
35
|
+
request.headers["authorization"] = self._header
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def coerce_auth(auth: AuthTypes) -> Auth | None:
|
|
39
|
+
if auth is None or isinstance(auth, Auth):
|
|
40
|
+
return auth
|
|
41
|
+
if isinstance(auth, tuple) and len(auth) == 2:
|
|
42
|
+
return BasicAuth(auth[0], auth[1])
|
|
43
|
+
raise TypeError(f"Invalid 'auth' argument: {auth!r}")
|