punkasgi 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.
- punkasgi-0.1.0/.gitignore +11 -0
- punkasgi-0.1.0/LICENSE +28 -0
- punkasgi-0.1.0/PKG-INFO +117 -0
- punkasgi-0.1.0/README.md +84 -0
- punkasgi-0.1.0/punkasgi/__init__.py +6 -0
- punkasgi-0.1.0/punkasgi/__main__.py +5 -0
- punkasgi-0.1.0/punkasgi/_types.py +253 -0
- punkasgi-0.1.0/punkasgi/_version.py +1 -0
- punkasgi-0.1.0/punkasgi/config.py +329 -0
- punkasgi-0.1.0/punkasgi/importer.py +66 -0
- punkasgi-0.1.0/punkasgi/lifespan/__init__.py +0 -0
- punkasgi-0.1.0/punkasgi/lifespan/off.py +10 -0
- punkasgi-0.1.0/punkasgi/lifespan/on.py +142 -0
- punkasgi-0.1.0/punkasgi/logging.py +1 -0
- punkasgi-0.1.0/punkasgi/main.py +400 -0
- punkasgi-0.1.0/punkasgi/middleware/__init__.py +0 -0
- punkasgi-0.1.0/punkasgi/middleware/message_logger.py +101 -0
- punkasgi-0.1.0/punkasgi/middleware/proxy_headers.py +178 -0
- punkasgi-0.1.0/punkasgi/protocols/__init__.py +0 -0
- punkasgi-0.1.0/punkasgi/protocols/http.py +430 -0
- punkasgi-0.1.0/punkasgi/protocols/utils.py +52 -0
- punkasgi-0.1.0/punkasgi/protocols/websockets.py +643 -0
- punkasgi-0.1.0/punkasgi/server.py +322 -0
- punkasgi-0.1.0/pyproject.toml +132 -0
- punkasgi-0.1.0/tests/__init__.py +0 -0
- punkasgi-0.1.0/tests/conftest.py +119 -0
- punkasgi-0.1.0/tests/fakes.py +154 -0
- punkasgi-0.1.0/tests/protocols/__init__.py +0 -0
- punkasgi-0.1.0/tests/protocols/test_http.py +650 -0
- punkasgi-0.1.0/tests/protocols/test_websocket.py +1022 -0
- punkasgi-0.1.0/tests/response.py +70 -0
- punkasgi-0.1.0/tests/test_config.py +65 -0
- punkasgi-0.1.0/tests/test_lifespan.py +224 -0
- punkasgi-0.1.0/tests/test_server.py +160 -0
- punkasgi-0.1.0/tests/utils.py +27 -0
- punkasgi-0.1.0/tests/ws.py +252 -0
punkasgi-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.
|
punkasgi-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: punkasgi
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: An ASGI server for the tonio runtime, built on httpunk
|
|
5
|
+
Project-URL: Homepage, https://github.com/gi0baro/punkasgi
|
|
6
|
+
Project-URL: Funding, https://github.com/sponsors/gi0baro
|
|
7
|
+
Project-URL: Source, https://github.com/gi0baro/punkasgi
|
|
8
|
+
Project-URL: Issues, https://github.com/gi0baro/punkasgi/issues
|
|
9
|
+
Author-email: Giovanni Barillari <g@baro.dev>
|
|
10
|
+
License-Expression: BSD-3-Clause
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: asgi,async,http,httpunk,server,tonio
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Environment :: Web Environment
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
17
|
+
Classifier: Operating System :: MacOS
|
|
18
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
22
|
+
Classifier: Programming Language :: Python :: Free Threading :: 3 - Stable
|
|
23
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
24
|
+
Requires-Python: >=3.14
|
|
25
|
+
Requires-Dist: click>=7.0
|
|
26
|
+
Requires-Dist: httpunk~=0.4.1
|
|
27
|
+
Requires-Dist: tonio~=0.9.17
|
|
28
|
+
Requires-Dist: websockets>=17
|
|
29
|
+
Provides-Extra: standard
|
|
30
|
+
Requires-Dist: python-dotenv>=0.13; extra == 'standard'
|
|
31
|
+
Requires-Dist: pyyaml>=5.1; extra == 'standard'
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# punkasgi
|
|
35
|
+
|
|
36
|
+
punkasgi is an ASGI server for the [TonIO](https://github.com/gi0baro/tonio) runtime,
|
|
37
|
+
built on top of [HTTPunk](https://github.com/gi0baro/httpunk).
|
|
38
|
+
|
|
39
|
+
> **Warning:** punkasgi is in an early stage and still work in progress.
|
|
40
|
+
|
|
41
|
+
> **Note:** punkasgi was built with substantial help from LLMs, under human supervision.
|
|
42
|
+
|
|
43
|
+
## In a nutshell
|
|
44
|
+
|
|
45
|
+
- HTTP/1.0, HTTP/1.1 and HTTP/2 (prior knowledge on plain TCP, ALPN over TLS), with
|
|
46
|
+
every protocol behaviour coming from httpunk: keep-alive, head timeouts, framing,
|
|
47
|
+
`Date` headers, graceful shutdown
|
|
48
|
+
- WebSockets over HTTP/1.1 through the sans-io `websockets` implementation, with
|
|
49
|
+
keepalive pings, per-message deflate and the `websocket.http.response` extension
|
|
50
|
+
- The ASGI 3 protocol handling (scope contents, `receive()` / `send()` semantics, lifespan,
|
|
51
|
+
websocket messages) follows [uvicorn](https://github.com/Kludex/uvicorn)
|
|
52
|
+
- Single process architecture: the TonIO runtime's threads are the workers
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
```shell
|
|
57
|
+
pip install punkasgi
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
punkasgi needs a free-threaded CPython 3.14 or later, as tonio does. The `standard` extra
|
|
61
|
+
adds `python-dotenv` (for `--env-file`) and `PyYAML` (for YAML logging configurations).
|
|
62
|
+
|
|
63
|
+
## Quickstart
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
# main.py
|
|
67
|
+
async def app(scope, receive, send):
|
|
68
|
+
assert scope["type"] == "http"
|
|
69
|
+
await send({"type": "http.response.start", "status": 200, "headers": [(b"content-type", b"text/plain")]})
|
|
70
|
+
await send({"type": "http.response.body", "body": b"Hello, world!"})
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
```shell
|
|
74
|
+
punkasgi main:app
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Or *programmatically*:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
import punkasgi
|
|
81
|
+
|
|
82
|
+
punkasgi.run("main:app", host="0.0.0.0", port=8000, threads=4)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`punkasgi --help` lists every option. The ones worth knowing:
|
|
86
|
+
|
|
87
|
+
| option | note |
|
|
88
|
+
|---|---|
|
|
89
|
+
| `--threads` | size of the tonio runtime, defaults to `$WEB_CONCURRENCY` or the CPU count |
|
|
90
|
+
| `--http auto\|h1\|h2` | protocol selection: `auto` sniffs the HTTP/2 preface on plain TCP and follows ALPN over TLS |
|
|
91
|
+
| `--ws/--no-ws` | WebSocket upgrades on or off; with them off an upgrade request is answered 400 |
|
|
92
|
+
| `--timeout-keep-alive` | hyper's head read timeout: it bounds the idle wait between requests together with the read of the next request head |
|
|
93
|
+
| `--timeout-graceful-shutdown` | how long a shutdown waits for in-flight requests; unset waits without a deadline |
|
|
94
|
+
| `--proxy-headers` | trust `X-Forwarded-For` / `X-Forwarded-Proto` from `--forwarded-allow-ips`; off by default |
|
|
95
|
+
| `--access-log` | one `punkasgi.access` line per response; off by default |
|
|
96
|
+
|
|
97
|
+
A first `SIGINT` or `SIGTERM` starts a graceful shutdown; a second `SIGINT` leaves at once
|
|
98
|
+
with whatever is still in flight. Environment variables use the `PUNKASGI_` prefix (e.g.
|
|
99
|
+
`PUNKASGI_PORT`), loggers are `punkasgi.error`, `punkasgi.access` and `punkasgi.asgi`,
|
|
100
|
+
and the default `server` header is `punkasgi`.
|
|
101
|
+
|
|
102
|
+
## Behaviours worth knowing
|
|
103
|
+
|
|
104
|
+
These follow httpunk, and through it hyper, rather than any other server:
|
|
105
|
+
|
|
106
|
+
- **Unread request bodies.** After a response completes, a request body the application
|
|
107
|
+
left unread is drained only if it is already buffered; otherwise the connection closes
|
|
108
|
+
instead of being reused for the next request.
|
|
109
|
+
- **Response `content-length`.** punkasgi does not check the body against the declared
|
|
110
|
+
length. On HTTP/1 a body shorter than declared fails the response and closes the
|
|
111
|
+
connection, and a longer one is cut at the declared length; on HTTP/2 the frames go out
|
|
112
|
+
as sent.
|
|
113
|
+
- **`Date` header.** Always written, on both protocols.
|
|
114
|
+
|
|
115
|
+
## License
|
|
116
|
+
|
|
117
|
+
punkasgi is released under the BSD-3-Clause license.
|
punkasgi-0.1.0/README.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# punkasgi
|
|
2
|
+
|
|
3
|
+
punkasgi is an ASGI server for the [TonIO](https://github.com/gi0baro/tonio) runtime,
|
|
4
|
+
built on top of [HTTPunk](https://github.com/gi0baro/httpunk).
|
|
5
|
+
|
|
6
|
+
> **Warning:** punkasgi is in an early stage and still work in progress.
|
|
7
|
+
|
|
8
|
+
> **Note:** punkasgi was built with substantial help from LLMs, under human supervision.
|
|
9
|
+
|
|
10
|
+
## In a nutshell
|
|
11
|
+
|
|
12
|
+
- HTTP/1.0, HTTP/1.1 and HTTP/2 (prior knowledge on plain TCP, ALPN over TLS), with
|
|
13
|
+
every protocol behaviour coming from httpunk: keep-alive, head timeouts, framing,
|
|
14
|
+
`Date` headers, graceful shutdown
|
|
15
|
+
- WebSockets over HTTP/1.1 through the sans-io `websockets` implementation, with
|
|
16
|
+
keepalive pings, per-message deflate and the `websocket.http.response` extension
|
|
17
|
+
- The ASGI 3 protocol handling (scope contents, `receive()` / `send()` semantics, lifespan,
|
|
18
|
+
websocket messages) follows [uvicorn](https://github.com/Kludex/uvicorn)
|
|
19
|
+
- Single process architecture: the TonIO runtime's threads are the workers
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```shell
|
|
24
|
+
pip install punkasgi
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
punkasgi needs a free-threaded CPython 3.14 or later, as tonio does. The `standard` extra
|
|
28
|
+
adds `python-dotenv` (for `--env-file`) and `PyYAML` (for YAML logging configurations).
|
|
29
|
+
|
|
30
|
+
## Quickstart
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
# main.py
|
|
34
|
+
async def app(scope, receive, send):
|
|
35
|
+
assert scope["type"] == "http"
|
|
36
|
+
await send({"type": "http.response.start", "status": 200, "headers": [(b"content-type", b"text/plain")]})
|
|
37
|
+
await send({"type": "http.response.body", "body": b"Hello, world!"})
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```shell
|
|
41
|
+
punkasgi main:app
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Or *programmatically*:
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
import punkasgi
|
|
48
|
+
|
|
49
|
+
punkasgi.run("main:app", host="0.0.0.0", port=8000, threads=4)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`punkasgi --help` lists every option. The ones worth knowing:
|
|
53
|
+
|
|
54
|
+
| option | note |
|
|
55
|
+
|---|---|
|
|
56
|
+
| `--threads` | size of the tonio runtime, defaults to `$WEB_CONCURRENCY` or the CPU count |
|
|
57
|
+
| `--http auto\|h1\|h2` | protocol selection: `auto` sniffs the HTTP/2 preface on plain TCP and follows ALPN over TLS |
|
|
58
|
+
| `--ws/--no-ws` | WebSocket upgrades on or off; with them off an upgrade request is answered 400 |
|
|
59
|
+
| `--timeout-keep-alive` | hyper's head read timeout: it bounds the idle wait between requests together with the read of the next request head |
|
|
60
|
+
| `--timeout-graceful-shutdown` | how long a shutdown waits for in-flight requests; unset waits without a deadline |
|
|
61
|
+
| `--proxy-headers` | trust `X-Forwarded-For` / `X-Forwarded-Proto` from `--forwarded-allow-ips`; off by default |
|
|
62
|
+
| `--access-log` | one `punkasgi.access` line per response; off by default |
|
|
63
|
+
|
|
64
|
+
A first `SIGINT` or `SIGTERM` starts a graceful shutdown; a second `SIGINT` leaves at once
|
|
65
|
+
with whatever is still in flight. Environment variables use the `PUNKASGI_` prefix (e.g.
|
|
66
|
+
`PUNKASGI_PORT`), loggers are `punkasgi.error`, `punkasgi.access` and `punkasgi.asgi`,
|
|
67
|
+
and the default `server` header is `punkasgi`.
|
|
68
|
+
|
|
69
|
+
## Behaviours worth knowing
|
|
70
|
+
|
|
71
|
+
These follow httpunk, and through it hyper, rather than any other server:
|
|
72
|
+
|
|
73
|
+
- **Unread request bodies.** After a response completes, a request body the application
|
|
74
|
+
left unread is drained only if it is already buffered; otherwise the connection closes
|
|
75
|
+
instead of being reused for the next request.
|
|
76
|
+
- **Response `content-length`.** punkasgi does not check the body against the declared
|
|
77
|
+
length. On HTTP/1 a body shorter than declared fails the response and closes the
|
|
78
|
+
connection, and a longer one is cut at the declared length; on HTTP/2 the frames go out
|
|
79
|
+
as sent.
|
|
80
|
+
- **`Date` header.** Always written, on both protocols.
|
|
81
|
+
|
|
82
|
+
## License
|
|
83
|
+
|
|
84
|
+
punkasgi is released under the BSD-3-Clause license.
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright (c) Django Software Foundation and individual contributors.
|
|
3
|
+
All rights reserved.
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without modification,
|
|
6
|
+
are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice,
|
|
9
|
+
this list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright
|
|
12
|
+
notice, this list of conditions and the following disclaimer in the
|
|
13
|
+
documentation and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of Django nor the names of its contributors may be used
|
|
16
|
+
to endorse or promote products derived from this software without
|
|
17
|
+
specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
20
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
21
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
23
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
24
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
25
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
26
|
+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
27
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
28
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
|
|
33
|
+
from collections.abc import Awaitable, Callable, Iterable
|
|
34
|
+
from typing import Any, Literal, NotRequired, TypedDict
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ASGIVersions(TypedDict):
|
|
38
|
+
spec_version: str
|
|
39
|
+
version: Literal["2.0"] | Literal["3.0"]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class HTTPScope(TypedDict):
|
|
43
|
+
type: Literal["http"]
|
|
44
|
+
asgi: ASGIVersions
|
|
45
|
+
http_version: str
|
|
46
|
+
method: str
|
|
47
|
+
scheme: str
|
|
48
|
+
path: str
|
|
49
|
+
raw_path: bytes
|
|
50
|
+
query_string: bytes
|
|
51
|
+
root_path: str
|
|
52
|
+
headers: Iterable[tuple[bytes, bytes]]
|
|
53
|
+
client: tuple[str, int] | None
|
|
54
|
+
server: tuple[str, int | None] | None
|
|
55
|
+
state: NotRequired[dict[str, Any]]
|
|
56
|
+
extensions: NotRequired[dict[str, dict[object, object]]]
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class WebSocketScope(TypedDict):
|
|
60
|
+
type: Literal["websocket"]
|
|
61
|
+
asgi: ASGIVersions
|
|
62
|
+
http_version: str
|
|
63
|
+
scheme: str
|
|
64
|
+
path: str
|
|
65
|
+
raw_path: bytes
|
|
66
|
+
query_string: bytes
|
|
67
|
+
root_path: str
|
|
68
|
+
headers: Iterable[tuple[bytes, bytes]]
|
|
69
|
+
client: tuple[str, int] | None
|
|
70
|
+
server: tuple[str, int | None] | None
|
|
71
|
+
subprotocols: Iterable[str]
|
|
72
|
+
state: NotRequired[dict[str, Any]]
|
|
73
|
+
extensions: NotRequired[dict[str, dict[object, object]]]
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class LifespanScope(TypedDict):
|
|
77
|
+
type: Literal["lifespan"]
|
|
78
|
+
asgi: ASGIVersions
|
|
79
|
+
state: NotRequired[dict[str, Any]]
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
WWWScope = HTTPScope | WebSocketScope
|
|
83
|
+
Scope = HTTPScope | WebSocketScope | LifespanScope
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class HTTPRequestEvent(TypedDict):
|
|
87
|
+
type: Literal["http.request"]
|
|
88
|
+
body: bytes
|
|
89
|
+
more_body: bool
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class HTTPResponseDebugEvent(TypedDict):
|
|
93
|
+
type: Literal["http.response.debug"]
|
|
94
|
+
info: dict[str, object]
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class HTTPResponseStartEvent(TypedDict):
|
|
98
|
+
type: Literal["http.response.start"]
|
|
99
|
+
status: int
|
|
100
|
+
headers: NotRequired[Iterable[tuple[bytes, bytes]]]
|
|
101
|
+
trailers: NotRequired[bool]
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class HTTPResponseBodyEvent(TypedDict):
|
|
105
|
+
type: Literal["http.response.body"]
|
|
106
|
+
body: bytes
|
|
107
|
+
more_body: NotRequired[bool]
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class HTTPResponseTrailersEvent(TypedDict):
|
|
111
|
+
type: Literal["http.response.trailers"]
|
|
112
|
+
headers: Iterable[tuple[bytes, bytes]]
|
|
113
|
+
more_trailers: bool
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class HTTPServerPushEvent(TypedDict):
|
|
117
|
+
type: Literal["http.response.push"]
|
|
118
|
+
path: str
|
|
119
|
+
headers: Iterable[tuple[bytes, bytes]]
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class HTTPDisconnectEvent(TypedDict):
|
|
123
|
+
type: Literal["http.disconnect"]
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class WebSocketConnectEvent(TypedDict):
|
|
127
|
+
type: Literal["websocket.connect"]
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class WebSocketAcceptEvent(TypedDict):
|
|
131
|
+
type: Literal["websocket.accept"]
|
|
132
|
+
subprotocol: NotRequired[str | None]
|
|
133
|
+
headers: NotRequired[Iterable[tuple[bytes, bytes]]]
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class _WebSocketReceiveEventBytes(TypedDict):
|
|
137
|
+
type: Literal["websocket.receive"]
|
|
138
|
+
bytes: bytes
|
|
139
|
+
text: NotRequired[None]
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class _WebSocketReceiveEventText(TypedDict):
|
|
143
|
+
type: Literal["websocket.receive"]
|
|
144
|
+
bytes: NotRequired[None]
|
|
145
|
+
text: str
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
WebSocketReceiveEvent = _WebSocketReceiveEventBytes | _WebSocketReceiveEventText
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class _WebSocketSendEventBytes(TypedDict):
|
|
152
|
+
type: Literal["websocket.send"]
|
|
153
|
+
bytes: bytes
|
|
154
|
+
text: NotRequired[None]
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class _WebSocketSendEventText(TypedDict):
|
|
158
|
+
type: Literal["websocket.send"]
|
|
159
|
+
bytes: NotRequired[None]
|
|
160
|
+
text: str
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
WebSocketSendEvent = _WebSocketSendEventBytes | _WebSocketSendEventText
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class WebSocketResponseStartEvent(TypedDict):
|
|
167
|
+
type: Literal["websocket.http.response.start"]
|
|
168
|
+
status: int
|
|
169
|
+
headers: Iterable[tuple[bytes, bytes]]
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class WebSocketResponseBodyEvent(TypedDict):
|
|
173
|
+
type: Literal["websocket.http.response.body"]
|
|
174
|
+
body: bytes
|
|
175
|
+
more_body: NotRequired[bool]
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class WebSocketDisconnectEvent(TypedDict):
|
|
179
|
+
type: Literal["websocket.disconnect"]
|
|
180
|
+
code: int
|
|
181
|
+
reason: NotRequired[str | None]
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class WebSocketCloseEvent(TypedDict):
|
|
185
|
+
type: Literal["websocket.close"]
|
|
186
|
+
code: NotRequired[int]
|
|
187
|
+
reason: NotRequired[str | None]
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class LifespanStartupEvent(TypedDict):
|
|
191
|
+
type: Literal["lifespan.startup"]
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class LifespanShutdownEvent(TypedDict):
|
|
195
|
+
type: Literal["lifespan.shutdown"]
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class LifespanStartupCompleteEvent(TypedDict):
|
|
199
|
+
type: Literal["lifespan.startup.complete"]
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class LifespanStartupFailedEvent(TypedDict):
|
|
203
|
+
type: Literal["lifespan.startup.failed"]
|
|
204
|
+
message: str
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
class LifespanShutdownCompleteEvent(TypedDict):
|
|
208
|
+
type: Literal["lifespan.shutdown.complete"]
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class LifespanShutdownFailedEvent(TypedDict):
|
|
212
|
+
type: Literal["lifespan.shutdown.failed"]
|
|
213
|
+
message: str
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
WebSocketEvent = WebSocketReceiveEvent | WebSocketDisconnectEvent | WebSocketConnectEvent
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
ASGIReceiveEvent = (
|
|
220
|
+
HTTPRequestEvent
|
|
221
|
+
| HTTPDisconnectEvent
|
|
222
|
+
| WebSocketConnectEvent
|
|
223
|
+
| WebSocketReceiveEvent
|
|
224
|
+
| WebSocketDisconnectEvent
|
|
225
|
+
| LifespanStartupEvent
|
|
226
|
+
| LifespanShutdownEvent
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
ASGISendEvent = (
|
|
231
|
+
HTTPResponseStartEvent
|
|
232
|
+
| HTTPResponseBodyEvent
|
|
233
|
+
| HTTPResponseTrailersEvent
|
|
234
|
+
| HTTPServerPushEvent
|
|
235
|
+
| HTTPDisconnectEvent
|
|
236
|
+
| WebSocketAcceptEvent
|
|
237
|
+
| WebSocketSendEvent
|
|
238
|
+
| WebSocketResponseStartEvent
|
|
239
|
+
| WebSocketResponseBodyEvent
|
|
240
|
+
| WebSocketCloseEvent
|
|
241
|
+
| LifespanStartupCompleteEvent
|
|
242
|
+
| LifespanStartupFailedEvent
|
|
243
|
+
| LifespanShutdownCompleteEvent
|
|
244
|
+
| LifespanShutdownFailedEvent
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
ASGIReceiveCallable = Callable[[], Awaitable[ASGIReceiveEvent]]
|
|
249
|
+
ASGISendCallable = Callable[[ASGISendEvent], Awaitable[None]]
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
ASGI3Application = Callable[[Scope, ASGIReceiveCallable, ASGISendCallable], Awaitable[None]]
|
|
253
|
+
ASGIApplication = ASGI3Application
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|