anyquart 0.3.0__tar.gz → 0.3.1__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.
- {anyquart-0.3.0 → anyquart-0.3.1}/PKG-INFO +24 -15
- {anyquart-0.3.0 → anyquart-0.3.1}/README.md +20 -11
- {anyquart-0.3.0 → anyquart-0.3.1}/pyproject.toml +7 -8
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/app.py +14 -6
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/cli.py +32 -15
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/ctx.py +7 -4
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/di.py +13 -16
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/helpers.py +2 -2
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/sessions.py +3 -5
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/templating.py +1 -3
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/testing/__init__.py +11 -14
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/testing/utils.py +10 -5
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/typing.py +35 -39
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/wrappers/response.py +1 -1
- {anyquart-0.3.0 → anyquart-0.3.1}/LICENSE.txt +0 -0
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/__init__.py +0 -0
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/__main__.py +0 -0
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/asgi.py +0 -0
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/blueprints.py +0 -0
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/config.py +0 -0
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/datastructures.py +0 -0
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/debug.py +0 -0
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/formparser.py +0 -0
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/globals.py +0 -0
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/json/__init__.py +0 -0
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/json/provider.py +0 -0
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/json/tag.py +0 -0
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/logging.py +0 -0
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/py.typed +0 -0
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/routing.py +0 -0
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/signals.py +0 -0
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/testing/app.py +0 -0
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/testing/client.py +0 -0
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/testing/connections.py +0 -0
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/utils.py +0 -0
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/views.py +0 -0
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/wrappers/__init__.py +6 -6
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/wrappers/base.py +0 -0
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/wrappers/request.py +0 -0
- {anyquart-0.3.0 → anyquart-0.3.1}/src/anyquart/wrappers/websocket.py +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: anyquart
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: A fork of Quart that uses AnyIO
|
|
5
5
|
Author-email: pgjones <philip.graham.jones@googlemail.com>, NIYONSHUTI Emmanuel <emmanuelniyonshuti13@gmail.com>
|
|
6
6
|
Maintainer-email: NIYONSHUTI Emmanuel <emmanuelniyonshuti13@gmail.com>
|
|
7
|
-
Requires-Python: >=3.
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
8
|
Description-Content-Type: text/markdown
|
|
9
9
|
License-Expression: MIT
|
|
10
10
|
Classifier: Development Status :: 4 - Beta
|
|
@@ -13,12 +13,12 @@ Classifier: Framework :: Flask
|
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
14
14
|
Classifier: Operating System :: OS Independent
|
|
15
15
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.13
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.14
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.15
|
|
21
|
+
Classifier: Programming Language :: Python :: Free Threading :: 2 - Beta
|
|
22
22
|
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
23
23
|
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
24
24
|
Classifier: Typing :: Typed
|
|
@@ -31,7 +31,7 @@ Requires-Dist: importlib-metadata; python_version < '3.13'
|
|
|
31
31
|
Requires-Dist: itsdangerous
|
|
32
32
|
Requires-Dist: jinja2
|
|
33
33
|
Requires-Dist: markupsafe
|
|
34
|
-
Requires-Dist: typing-extensions; python_version < '3.
|
|
34
|
+
Requires-Dist: typing-extensions >= 4.16.0; python_version < '3.15'
|
|
35
35
|
Requires-Dist: werkzeug>=3.0
|
|
36
36
|
Requires-Dist: anycorn>=0.11.2 ; extra == "anycorn"
|
|
37
37
|
Requires-Dist: python-dotenv ; extra == "dotenv"
|
|
@@ -67,32 +67,35 @@ e.g:
|
|
|
67
67
|
```python
|
|
68
68
|
from anyquart import Needs
|
|
69
69
|
|
|
70
|
+
|
|
70
71
|
async def get_db() -> AsyncGenerator[AsyncSession, None]:
|
|
71
72
|
async with async_session() as session:
|
|
72
73
|
yield session
|
|
73
74
|
|
|
75
|
+
|
|
74
76
|
@app.route("/users")
|
|
75
|
-
async def get_users(db: AsyncSession = Needs(get_db)) -> None:
|
|
76
|
-
|
|
77
|
+
async def get_users(db: AsyncSession = Needs(get_db)) -> None: ...
|
|
78
|
+
|
|
77
79
|
|
|
78
80
|
# Or:
|
|
79
81
|
DBSession = Annotated[AsyncSession, Needs(get_db)]
|
|
80
82
|
|
|
83
|
+
|
|
81
84
|
@app.route("/users")
|
|
82
|
-
async def get_users(db: DBSession) -> None:
|
|
83
|
-
|
|
85
|
+
async def get_users(db: DBSession) -> None: ...
|
|
86
|
+
|
|
84
87
|
|
|
85
88
|
# Tests can use `app.dependency_overrides` dictionary to replace route handler's
|
|
86
89
|
# dependency with test dependency
|
|
87
90
|
```
|
|
88
|
-
6. Runs on Python 3.
|
|
91
|
+
6. Runs on Python 3.11+
|
|
89
92
|
|
|
90
93
|
## Usage
|
|
91
94
|
You will have to replace `quart` with `anyquart` and `Quart` with `AnyQuart`.
|
|
92
95
|
|
|
93
|
-
Install from PyPI using an installer such as pip. Requires Python 3.
|
|
96
|
+
Install from PyPI using an installer such as pip. Requires Python 3.11+.
|
|
94
97
|
|
|
95
|
-
```
|
|
98
|
+
```python
|
|
96
99
|
$ pip install anyquart
|
|
97
100
|
```
|
|
98
101
|
|
|
@@ -103,14 +106,17 @@ from anyquart import AnyQuart, websocket, render_template
|
|
|
103
106
|
|
|
104
107
|
app = AnyQuart(__name__)
|
|
105
108
|
|
|
109
|
+
|
|
106
110
|
@app.route("/")
|
|
107
111
|
async def hello():
|
|
108
112
|
return await render_template("index.html")
|
|
109
113
|
|
|
114
|
+
|
|
110
115
|
@app.route("/api")
|
|
111
116
|
async def json():
|
|
112
117
|
return {"hello": "world"}
|
|
113
118
|
|
|
119
|
+
|
|
114
120
|
@app.websocket("/ws")
|
|
115
121
|
async def ws():
|
|
116
122
|
while True:
|
|
@@ -119,14 +125,15 @@ async def ws():
|
|
|
119
125
|
```
|
|
120
126
|
Install an ASGI web server, pick one from [this list](https://asgi.readthedocs.io/en/latest/implementations.html), or install anyquart with Anycorn directly:
|
|
121
127
|
```bash
|
|
122
|
-
$
|
|
128
|
+
$ pip install anyquart[anycorn]
|
|
123
129
|
```
|
|
124
130
|
run the application:
|
|
125
131
|
```bash
|
|
126
132
|
$ anycorn app:app
|
|
127
133
|
* [2026-08-01 11:49:46 +0200] [30809] [INFO] Running on http://127.0.0.1:8000 (CTRL + C to quit)
|
|
128
134
|
```
|
|
129
|
-
|
|
135
|
+
|
|
136
|
+
**Note**: As of 0.3.0, `anyquart run` requires an ASGI web server to be installed. If no ASGI web server is available, running the application in development mode will raise a `RuntimeError`. Install AnyQuart with Anycorn using the command above, or install another ASGI web server from the list above.
|
|
130
137
|
|
|
131
138
|
# Testing
|
|
132
139
|
Pytest requires a plugin to run asynchronous test functions and fixtures.
|
|
@@ -137,20 +144,22 @@ import pytest
|
|
|
137
144
|
|
|
138
145
|
from app import app
|
|
139
146
|
|
|
147
|
+
|
|
140
148
|
@pytest.fixture()
|
|
141
149
|
def anyio_backend():
|
|
142
|
-
return "trio"
|
|
150
|
+
return "trio" # you can replace with "asyncio"
|
|
151
|
+
|
|
143
152
|
|
|
144
153
|
@pytest.fixture()
|
|
145
154
|
def test_client():
|
|
146
155
|
return app.test_client()
|
|
147
156
|
|
|
157
|
+
|
|
148
158
|
@pytest.mark.anyio
|
|
149
159
|
async def test_do_something(test_client) -> None:
|
|
150
160
|
response = await test_client.get("/")
|
|
151
161
|
assert response.status_code == 200
|
|
152
162
|
assert await response.json == {"hello": "world"}
|
|
153
|
-
|
|
154
163
|
```
|
|
155
164
|
|
|
156
165
|
Refer to the [Quart documentation](https://quart.palletsprojects.com) for more details.
|
|
@@ -23,32 +23,35 @@ e.g:
|
|
|
23
23
|
```python
|
|
24
24
|
from anyquart import Needs
|
|
25
25
|
|
|
26
|
+
|
|
26
27
|
async def get_db() -> AsyncGenerator[AsyncSession, None]:
|
|
27
28
|
async with async_session() as session:
|
|
28
29
|
yield session
|
|
29
30
|
|
|
31
|
+
|
|
30
32
|
@app.route("/users")
|
|
31
|
-
async def get_users(db: AsyncSession = Needs(get_db)) -> None:
|
|
32
|
-
|
|
33
|
+
async def get_users(db: AsyncSession = Needs(get_db)) -> None: ...
|
|
34
|
+
|
|
33
35
|
|
|
34
36
|
# Or:
|
|
35
37
|
DBSession = Annotated[AsyncSession, Needs(get_db)]
|
|
36
38
|
|
|
39
|
+
|
|
37
40
|
@app.route("/users")
|
|
38
|
-
async def get_users(db: DBSession) -> None:
|
|
39
|
-
|
|
41
|
+
async def get_users(db: DBSession) -> None: ...
|
|
42
|
+
|
|
40
43
|
|
|
41
44
|
# Tests can use `app.dependency_overrides` dictionary to replace route handler's
|
|
42
45
|
# dependency with test dependency
|
|
43
46
|
```
|
|
44
|
-
6. Runs on Python 3.
|
|
47
|
+
6. Runs on Python 3.11+
|
|
45
48
|
|
|
46
49
|
## Usage
|
|
47
50
|
You will have to replace `quart` with `anyquart` and `Quart` with `AnyQuart`.
|
|
48
51
|
|
|
49
|
-
Install from PyPI using an installer such as pip. Requires Python 3.
|
|
52
|
+
Install from PyPI using an installer such as pip. Requires Python 3.11+.
|
|
50
53
|
|
|
51
|
-
```
|
|
54
|
+
```python
|
|
52
55
|
$ pip install anyquart
|
|
53
56
|
```
|
|
54
57
|
|
|
@@ -59,14 +62,17 @@ from anyquart import AnyQuart, websocket, render_template
|
|
|
59
62
|
|
|
60
63
|
app = AnyQuart(__name__)
|
|
61
64
|
|
|
65
|
+
|
|
62
66
|
@app.route("/")
|
|
63
67
|
async def hello():
|
|
64
68
|
return await render_template("index.html")
|
|
65
69
|
|
|
70
|
+
|
|
66
71
|
@app.route("/api")
|
|
67
72
|
async def json():
|
|
68
73
|
return {"hello": "world"}
|
|
69
74
|
|
|
75
|
+
|
|
70
76
|
@app.websocket("/ws")
|
|
71
77
|
async def ws():
|
|
72
78
|
while True:
|
|
@@ -75,14 +81,15 @@ async def ws():
|
|
|
75
81
|
```
|
|
76
82
|
Install an ASGI web server, pick one from [this list](https://asgi.readthedocs.io/en/latest/implementations.html), or install anyquart with Anycorn directly:
|
|
77
83
|
```bash
|
|
78
|
-
$
|
|
84
|
+
$ pip install anyquart[anycorn]
|
|
79
85
|
```
|
|
80
86
|
run the application:
|
|
81
87
|
```bash
|
|
82
88
|
$ anycorn app:app
|
|
83
89
|
* [2026-08-01 11:49:46 +0200] [30809] [INFO] Running on http://127.0.0.1:8000 (CTRL + C to quit)
|
|
84
90
|
```
|
|
85
|
-
|
|
91
|
+
|
|
92
|
+
**Note**: As of 0.3.0, `anyquart run` requires an ASGI web server to be installed. If no ASGI web server is available, running the application in development mode will raise a `RuntimeError`. Install AnyQuart with Anycorn using the command above, or install another ASGI web server from the list above.
|
|
86
93
|
|
|
87
94
|
# Testing
|
|
88
95
|
Pytest requires a plugin to run asynchronous test functions and fixtures.
|
|
@@ -93,20 +100,22 @@ import pytest
|
|
|
93
100
|
|
|
94
101
|
from app import app
|
|
95
102
|
|
|
103
|
+
|
|
96
104
|
@pytest.fixture()
|
|
97
105
|
def anyio_backend():
|
|
98
|
-
return "trio"
|
|
106
|
+
return "trio" # you can replace with "asyncio"
|
|
107
|
+
|
|
99
108
|
|
|
100
109
|
@pytest.fixture()
|
|
101
110
|
def test_client():
|
|
102
111
|
return app.test_client()
|
|
103
112
|
|
|
113
|
+
|
|
104
114
|
@pytest.mark.anyio
|
|
105
115
|
async def test_do_something(test_client) -> None:
|
|
106
116
|
response = await test_client.get("/")
|
|
107
117
|
assert response.status_code == 200
|
|
108
118
|
assert await response.json == {"hello": "world"}
|
|
109
|
-
|
|
110
119
|
```
|
|
111
120
|
|
|
112
121
|
Refer to the [Quart documentation](https://quart.palletsprojects.com) for more details.
|
|
@@ -4,7 +4,7 @@ build-backend = "flit_core.buildapi"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "anyquart"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.1"
|
|
8
8
|
description = "A fork of Quart that uses AnyIO"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -21,17 +21,17 @@ classifiers = [
|
|
|
21
21
|
"Intended Audience :: Developers",
|
|
22
22
|
"Operating System :: OS Independent",
|
|
23
23
|
"Programming Language :: Python :: 3 :: Only",
|
|
24
|
-
"Programming Language :: Python :: 3.10",
|
|
25
24
|
"Programming Language :: Python :: 3.11",
|
|
26
25
|
"Programming Language :: Python :: 3.12",
|
|
27
26
|
"Programming Language :: Python :: 3.13",
|
|
28
27
|
"Programming Language :: Python :: 3.14",
|
|
29
28
|
"Programming Language :: Python :: 3.15",
|
|
29
|
+
"Programming Language :: Python :: Free Threading :: 2 - Beta",
|
|
30
30
|
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
|
|
31
31
|
"Topic :: Software Development :: Libraries :: Application Frameworks",
|
|
32
32
|
"Typing :: Typed",
|
|
33
33
|
]
|
|
34
|
-
requires-python = ">=3.
|
|
34
|
+
requires-python = ">=3.11"
|
|
35
35
|
dependencies = [
|
|
36
36
|
"anyio>=4.14.0,<5",
|
|
37
37
|
"blinker>=1.6",
|
|
@@ -41,7 +41,7 @@ dependencies = [
|
|
|
41
41
|
"itsdangerous",
|
|
42
42
|
"jinja2",
|
|
43
43
|
"markupsafe",
|
|
44
|
-
"typing-extensions; python_version < '3.
|
|
44
|
+
"typing-extensions >= 4.16.0; python_version < '3.15'",
|
|
45
45
|
"werkzeug>=3.0",
|
|
46
46
|
]
|
|
47
47
|
|
|
@@ -59,7 +59,7 @@ anyquart = "anyquart.cli:main"
|
|
|
59
59
|
|
|
60
60
|
[dependency-groups]
|
|
61
61
|
dev = [
|
|
62
|
-
"trio==0.
|
|
62
|
+
"trio==0.35.0",
|
|
63
63
|
"ruff",
|
|
64
64
|
"tox",
|
|
65
65
|
"tox-uv",
|
|
@@ -123,7 +123,7 @@ warn_return_any = false
|
|
|
123
123
|
files = ["src", "tests"]
|
|
124
124
|
|
|
125
125
|
[tool.pyright]
|
|
126
|
-
pythonVersion = "3.
|
|
126
|
+
pythonVersion = "3.11"
|
|
127
127
|
include = ["src", "tests"]
|
|
128
128
|
typeCheckingMode = "basic"
|
|
129
129
|
|
|
@@ -145,7 +145,6 @@ select = [
|
|
|
145
145
|
"W", # pycodestyle warning
|
|
146
146
|
]
|
|
147
147
|
ignore = [
|
|
148
|
-
"UP038", # keep isinstance tuple
|
|
149
148
|
"UP007", # Union[] is needed for forward-referenced type aliases
|
|
150
149
|
"UP045"
|
|
151
150
|
]
|
|
@@ -156,7 +155,7 @@ order-by-type = false
|
|
|
156
155
|
|
|
157
156
|
[tool.tox]
|
|
158
157
|
env_list = [
|
|
159
|
-
"py3.15", "py3.14", "py3.
|
|
158
|
+
"py3.15", "py3.14", "py3.14t", "py3.13", "py3.12", "py3.11",
|
|
160
159
|
"style",
|
|
161
160
|
"typing",
|
|
162
161
|
"docs",
|
|
@@ -11,7 +11,6 @@ from collections.abc import Callable
|
|
|
11
11
|
from contextlib import AbstractAsyncContextManager
|
|
12
12
|
from datetime import timedelta
|
|
13
13
|
from inspect import isasyncgen
|
|
14
|
-
from inspect import iscoroutinefunction
|
|
15
14
|
from inspect import isgenerator
|
|
16
15
|
from types import TracebackType
|
|
17
16
|
from typing import Any
|
|
@@ -97,8 +96,8 @@ from .testing import make_test_body_with_headers
|
|
|
97
96
|
from .testing import make_test_headers_path_and_query_string
|
|
98
97
|
from .testing import make_test_scope
|
|
99
98
|
from .testing import no_op_push
|
|
100
|
-
from .testing import sentinel
|
|
101
99
|
from .testing import TestApp
|
|
100
|
+
from .testing.utils import sentinel
|
|
102
101
|
from .typing import AfterServingCallable
|
|
103
102
|
from .typing import AfterWebsocketCallable
|
|
104
103
|
from .typing import ASGIHTTPProtocol
|
|
@@ -127,6 +126,13 @@ from .wrappers import Request
|
|
|
127
126
|
from .wrappers import Response
|
|
128
127
|
from .wrappers import Websocket
|
|
129
128
|
|
|
129
|
+
# inspect.iscoroutinefunction behaves differently on python < 3.13
|
|
130
|
+
# See: https://github.com/python/cpython/issues/94924
|
|
131
|
+
if sys.version_info >= (3, 13):
|
|
132
|
+
from inspect import iscoroutinefunction
|
|
133
|
+
else:
|
|
134
|
+
from asyncio import iscoroutinefunction
|
|
135
|
+
|
|
130
136
|
AppOrBlueprintKey = str | None # The App key is None, whereas blueprints are named
|
|
131
137
|
T_after_serving = TypeVar("T_after_serving", bound=AfterServingCallable)
|
|
132
138
|
T_after_websocket = TypeVar("T_after_websocket", bound=AfterWebsocketCallable)
|
|
@@ -833,10 +839,12 @@ class AnyQuart(App):
|
|
|
833
839
|
try:
|
|
834
840
|
from anycorn.config import Config as HyperConfig
|
|
835
841
|
except ImportError:
|
|
836
|
-
raise RuntimeError(
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
842
|
+
raise RuntimeError(
|
|
843
|
+
"Running anyquart requires an ASGI web server to be installed\n"
|
|
844
|
+
"You can install one from this list https://asgi.readthedocs.io/en/latest/implementations.html\n"
|
|
845
|
+
"e.g:\n"
|
|
846
|
+
" $ pip install anycorn\n"
|
|
847
|
+
) from None
|
|
840
848
|
if kwargs:
|
|
841
849
|
warnings.warn(
|
|
842
850
|
f"Additional arguments, {','.join(kwargs.keys())}, are not supported.\n"
|
|
@@ -15,6 +15,7 @@ from importlib.metadata import entry_points
|
|
|
15
15
|
from operator import attrgetter
|
|
16
16
|
from types import ModuleType
|
|
17
17
|
from typing import Any
|
|
18
|
+
from typing import overload
|
|
18
19
|
from typing import TYPE_CHECKING
|
|
19
20
|
|
|
20
21
|
import anyio
|
|
@@ -38,7 +39,7 @@ class NoAppException(click.UsageError):
|
|
|
38
39
|
pass
|
|
39
40
|
|
|
40
41
|
|
|
41
|
-
def _called_with_wrong_args(f: Callable) -> bool:
|
|
42
|
+
def _called_with_wrong_args(f: Callable[..., Any]) -> bool:
|
|
42
43
|
"""Check whether calling a function raised a ``TypeError`` because
|
|
43
44
|
the call failed or because something in the factory raised the
|
|
44
45
|
error.
|
|
@@ -265,7 +266,7 @@ class ScriptInfo:
|
|
|
265
266
|
if not app:
|
|
266
267
|
raise NoAppException(
|
|
267
268
|
"Could not locate a AnyQuart application. Use the"
|
|
268
|
-
" 'AnyQuart --app' option, '
|
|
269
|
+
" 'AnyQuart --app' option, 'ANYQUART_APP' environment"
|
|
269
270
|
" variable, or an 'app.py' file in the"
|
|
270
271
|
" current directory."
|
|
271
272
|
)
|
|
@@ -317,21 +318,41 @@ class AppGroup(click.Group):
|
|
|
317
318
|
Not to be confused with :class:`AnyQuartGroup`.
|
|
318
319
|
"""
|
|
319
320
|
|
|
320
|
-
|
|
321
|
+
@overload
|
|
322
|
+
def command(self, __func: Callable[..., Any]) -> click.Command: ...
|
|
323
|
+
|
|
324
|
+
@overload
|
|
325
|
+
def command(
|
|
326
|
+
self, *args: Any, **kwargs: Any
|
|
327
|
+
) -> Callable[[Callable[..., Any]], click.Command]: ...
|
|
328
|
+
|
|
329
|
+
def command(
|
|
330
|
+
self, *args: Any, **kwargs: Any
|
|
331
|
+
) -> Callable[[Callable[..., Any]], click.Command]:
|
|
321
332
|
"""This works exactly like the method of the same name on a regular
|
|
322
333
|
:class:`click.Group` but it wraps callbacks in :func:`with_appcontext`
|
|
323
334
|
if it's enabled by passing ``with_appcontext=True``.
|
|
324
335
|
"""
|
|
325
336
|
wrap_for_ctx = kwargs.pop("with_appcontext", False)
|
|
326
337
|
|
|
327
|
-
def decorator(f: Callable) ->
|
|
338
|
+
def decorator(f: Callable[..., Any]) -> click.Command:
|
|
328
339
|
if wrap_for_ctx:
|
|
329
340
|
f = with_appcontext(f)
|
|
330
341
|
return click.Group.command(self, *args, **kwargs)(f)
|
|
331
342
|
|
|
332
343
|
return decorator
|
|
333
344
|
|
|
334
|
-
|
|
345
|
+
@overload
|
|
346
|
+
def group(self, __func: Callable[..., Any]) -> click.Group: ...
|
|
347
|
+
|
|
348
|
+
@overload
|
|
349
|
+
def group(
|
|
350
|
+
self, *args: Any, **kwargs: Any
|
|
351
|
+
) -> Callable[[Callable[..., Any]], click.Group]: ...
|
|
352
|
+
|
|
353
|
+
def group(
|
|
354
|
+
self, *args: Any, **kwargs: Any
|
|
355
|
+
) -> Callable[[Callable[..., Any]], click.Group] | click.Group:
|
|
335
356
|
kwargs.setdefault("cls", AppGroup)
|
|
336
357
|
return super().group(*args, **kwargs)
|
|
337
358
|
|
|
@@ -374,7 +395,7 @@ def _set_app(ctx: click.Context, param: click.Option, value: str | None) -> str
|
|
|
374
395
|
# This option is eager so the app will be available if --help is given.
|
|
375
396
|
# --help is also eager, so --app must be before it in the param list.
|
|
376
397
|
# no_args_is_help bypasses eager processing, so this option must be
|
|
377
|
-
# processed manually in that case to ensure
|
|
398
|
+
# processed manually in that case to ensure ANYQUART_APP gets picked up.
|
|
378
399
|
_app_option = click.Option(
|
|
379
400
|
["-A", "--app"],
|
|
380
401
|
metavar="IMPORT",
|
|
@@ -461,7 +482,7 @@ class AnyQuartGroup(AppGroup):
|
|
|
461
482
|
set_debug_flag: bool = True,
|
|
462
483
|
**extra: Any,
|
|
463
484
|
) -> None:
|
|
464
|
-
params = list(extra.pop("params", None) or ())
|
|
485
|
+
params: list[click.Parameter] = list(extra.pop("params", None) or ())
|
|
465
486
|
# Processing is done with option callbacks instead of a group
|
|
466
487
|
# callback. This allows users to make a custom group callback
|
|
467
488
|
# without losing the behavior. --env-file must come first so
|
|
@@ -474,7 +495,7 @@ class AnyQuartGroup(AppGroup):
|
|
|
474
495
|
if "context_settings" not in extra:
|
|
475
496
|
extra["context_settings"] = {}
|
|
476
497
|
|
|
477
|
-
extra["context_settings"].setdefault("auto_envvar_prefix", "
|
|
498
|
+
extra["context_settings"].setdefault("auto_envvar_prefix", "anyquart")
|
|
478
499
|
|
|
479
500
|
super().__init__(params=params, **extra)
|
|
480
501
|
|
|
@@ -558,12 +579,8 @@ class AnyQuartGroup(AppGroup):
|
|
|
558
579
|
return super().make_context(info_name, args, parent=parent, **extra)
|
|
559
580
|
|
|
560
581
|
def parse_args(self, ctx: click.Context, args: list[str]) -> list[str]:
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
# were given as env vars. Otherwise no_args_is_help will not
|
|
564
|
-
# see commands from app.cli.
|
|
565
|
-
_env_file_option.handle_parse_result(ctx, {}, [])
|
|
566
|
-
_app_option.handle_parse_result(ctx, {}, [])
|
|
582
|
+
_env_file_option.handle_parse_result(ctx, {}, [])
|
|
583
|
+
_app_option.handle_parse_result(ctx, {}, [])
|
|
567
584
|
|
|
568
585
|
return super().parse_args(ctx, args)
|
|
569
586
|
|
|
@@ -776,7 +793,7 @@ cli = AnyQuartGroup(
|
|
|
776
793
|
help="""\
|
|
777
794
|
A general utility script for AnyQuart applications.
|
|
778
795
|
An application to load must be given with the '--app' option,
|
|
779
|
-
'
|
|
796
|
+
'ANYQUART_APP' environment variable, or with an 'app.py' file
|
|
780
797
|
in the current directory.
|
|
781
798
|
""",
|
|
782
799
|
)
|
|
@@ -27,7 +27,10 @@ from .wrappers import Websocket
|
|
|
27
27
|
if TYPE_CHECKING:
|
|
28
28
|
from .app import AnyQuart # noqa
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
if sys.version_info < (3, 15):
|
|
31
|
+
from typing_extensions import sentinel
|
|
32
|
+
|
|
33
|
+
_sentinel = sentinel("_sentinel")
|
|
31
34
|
|
|
32
35
|
|
|
33
36
|
class _BaseRequestWebsocketContext:
|
|
@@ -153,7 +156,7 @@ class RequestContext(_BaseRequestWebsocketContext):
|
|
|
153
156
|
await super()._push_appctx(_cv_request.set(self))
|
|
154
157
|
await super()._push()
|
|
155
158
|
|
|
156
|
-
async def pop(self, exc: BaseException |
|
|
159
|
+
async def pop(self, exc: BaseException | _sentinel = _sentinel) -> None:
|
|
157
160
|
try:
|
|
158
161
|
if len(self._cv_tokens) == 1:
|
|
159
162
|
if exc is _sentinel:
|
|
@@ -210,7 +213,7 @@ class WebsocketContext(_BaseRequestWebsocketContext):
|
|
|
210
213
|
await super()._push_appctx(_cv_websocket.set(self))
|
|
211
214
|
await super()._push()
|
|
212
215
|
|
|
213
|
-
async def pop(self, exc: BaseException |
|
|
216
|
+
async def pop(self, exc: BaseException | _sentinel = _sentinel) -> None:
|
|
214
217
|
try:
|
|
215
218
|
if len(self._cv_tokens) == 1:
|
|
216
219
|
if exc is _sentinel:
|
|
@@ -265,7 +268,7 @@ class AppContext:
|
|
|
265
268
|
_sync_wrapper=self.app.ensure_async, # type: ignore[arg-type]
|
|
266
269
|
)
|
|
267
270
|
|
|
268
|
-
async def pop(self, exc: BaseException |
|
|
271
|
+
async def pop(self, exc: BaseException | _sentinel = _sentinel) -> None:
|
|
269
272
|
try:
|
|
270
273
|
if len(self._cv_tokens) == 1:
|
|
271
274
|
if exc is _sentinel:
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import inspect
|
|
4
4
|
import re
|
|
5
|
+
import sys
|
|
5
6
|
import typing
|
|
6
7
|
import weakref
|
|
7
8
|
from collections.abc import AsyncGenerator
|
|
@@ -18,6 +19,8 @@ from anyio import to_thread
|
|
|
18
19
|
if typing.TYPE_CHECKING:
|
|
19
20
|
from .app import AnyQuart
|
|
20
21
|
|
|
22
|
+
if sys.version_info < (3, 15):
|
|
23
|
+
from typing_extensions import sentinel
|
|
21
24
|
|
|
22
25
|
# Matches the converter portions of a URL rule so that we can extract the
|
|
23
26
|
# variable names (e.g. ``<int:user_id>`` yields ``user_id``).
|
|
@@ -25,7 +28,7 @@ _rule_variable_re = re.compile(
|
|
|
25
28
|
r"<(?:(?P<converter>[a-zA-Z_][a-zA-Z0-9_]*)(?:\([^>]*\))?:)?"
|
|
26
29
|
r"(?P<variable>[a-zA-Z_][a-zA-Z0-9_]*)>"
|
|
27
30
|
)
|
|
28
|
-
_NO_VALUE =
|
|
31
|
+
_NO_VALUE = sentinel("_NO_VALUE")
|
|
29
32
|
|
|
30
33
|
_dependency_map_cache: weakref.WeakKeyDictionary[
|
|
31
34
|
Callable[..., Any], dict[str, Callable[..., Any]]
|
|
@@ -34,25 +37,23 @@ _dependency_map_cache: weakref.WeakKeyDictionary[
|
|
|
34
37
|
|
|
35
38
|
@dataclass(frozen=True)
|
|
36
39
|
class _Needs:
|
|
40
|
+
dependency: Callable[..., Any]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def Needs(dependency: Callable[..., Any]) -> Any: # noqa 802
|
|
37
44
|
"""Mark a handler or dependency parameter as a request-scoped dependency.
|
|
38
45
|
|
|
39
46
|
Arguments:
|
|
40
47
|
dependency: The callable that produces the value for the marked
|
|
41
48
|
parameter.
|
|
42
49
|
"""
|
|
43
|
-
|
|
44
|
-
dependency: Callable[..., Any]
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def Needs(dependency: Callable[..., Any]) -> Any: # noqa 802
|
|
48
50
|
return _Needs(dependency=dependency)
|
|
49
51
|
|
|
50
52
|
|
|
51
53
|
def build_route_handler_dependency_map(
|
|
52
54
|
func: Callable[..., Any],
|
|
53
55
|
) -> dict[str, Callable[..., Any]]:
|
|
54
|
-
"""
|
|
55
|
-
Build route handler dependency map(a dictionary)
|
|
56
|
+
"""Build route handler dependency map(a dictionary)
|
|
56
57
|
i.e:``{parameter_name: dependency_func}``
|
|
57
58
|
|
|
58
59
|
Any handler or dependency parameter whose default value is a
|
|
@@ -106,8 +107,7 @@ def build_route_handler_dependency_map(
|
|
|
106
107
|
|
|
107
108
|
|
|
108
109
|
def rule_arguments(rule: str) -> set[str]:
|
|
109
|
-
"""
|
|
110
|
-
Get a set of url converter variables
|
|
110
|
+
"""Get a set of url converter variables
|
|
111
111
|
present in a url path(a Url Rule in flask lingo).
|
|
112
112
|
Arguments:
|
|
113
113
|
rule: a url path
|
|
@@ -120,8 +120,7 @@ def rule_arguments(rule: str) -> set[str]:
|
|
|
120
120
|
def check_name_conflicts(
|
|
121
121
|
view_func: Callable[..., Any], rule: str, defaults: dict[str, Any] | None = None
|
|
122
122
|
) -> None:
|
|
123
|
-
"""
|
|
124
|
-
A handler parameter cannot be both a ``Needs`` dependency injected value
|
|
123
|
+
"""A handler parameter cannot be both a ``Needs`` dependency injected value
|
|
125
124
|
and a URL rule converter (or a rule ``default``) on the same route.
|
|
126
125
|
e.g:
|
|
127
126
|
```
|
|
@@ -155,8 +154,7 @@ def check_name_conflicts(
|
|
|
155
154
|
async def invoke_with_di(
|
|
156
155
|
app: AnyQuart, func: Callable[..., Any], view_args: dict[str, Any] | None
|
|
157
156
|
) -> Any:
|
|
158
|
-
"""
|
|
159
|
-
Invoke the ``func`` resolving any of its dependency injected parameters.
|
|
157
|
+
"""Invoke the ``func`` resolving any of its dependency injected parameters.
|
|
160
158
|
|
|
161
159
|
When ``func`` declares no dependencies this is exactly equivalent to
|
|
162
160
|
``await app.ensure_async(func)(**view_args)`` so non-injected routes carry
|
|
@@ -174,8 +172,7 @@ async def invoke_with_di(
|
|
|
174
172
|
|
|
175
173
|
|
|
176
174
|
class _Resolver:
|
|
177
|
-
"""
|
|
178
|
-
Resolves a dependency tree for a single request.
|
|
175
|
+
"""Resolves a dependency tree for a single request.
|
|
179
176
|
|
|
180
177
|
A resolver instance is created per request and holds that request's cache
|
|
181
178
|
of resolved values plus its generator teardowns, guaranteeing that two
|
|
@@ -6,7 +6,7 @@ from collections.abc import Callable
|
|
|
6
6
|
from collections.abc import Iterable
|
|
7
7
|
from datetime import datetime
|
|
8
8
|
from datetime import timedelta
|
|
9
|
-
from datetime import
|
|
9
|
+
from datetime import UTC
|
|
10
10
|
from functools import cache
|
|
11
11
|
from functools import wraps
|
|
12
12
|
from io import BytesIO
|
|
@@ -339,7 +339,7 @@ async def send_file(
|
|
|
339
339
|
response.cache_control.public = True
|
|
340
340
|
if cache_timeout is not None:
|
|
341
341
|
response.cache_control.max_age = cache_timeout
|
|
342
|
-
response.expires = datetime.now(
|
|
342
|
+
response.expires = datetime.now(UTC) + timedelta(seconds=cache_timeout)
|
|
343
343
|
|
|
344
344
|
if add_etags and etag is not None:
|
|
345
345
|
response.set_etag(etag)
|
|
@@ -2,14 +2,12 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import hashlib
|
|
4
4
|
from datetime import datetime
|
|
5
|
-
from datetime import
|
|
5
|
+
from datetime import UTC
|
|
6
6
|
from typing import TYPE_CHECKING
|
|
7
7
|
|
|
8
8
|
from flask.sessions import NullSession as NullSession # noqa: F401
|
|
9
9
|
from flask.sessions import SecureCookieSession as SecureCookieSession # noqa: F401
|
|
10
|
-
from flask.sessions import
|
|
11
|
-
session_json_serializer as session_json_serializer,
|
|
12
|
-
)
|
|
10
|
+
from flask.sessions import session_json_serializer as session_json_serializer # noqa: F401
|
|
13
11
|
from flask.sessions import SessionMixin as SessionMixin # noqa: F401
|
|
14
12
|
from itsdangerous import BadSignature
|
|
15
13
|
from itsdangerous import URLSafeTimedSerializer
|
|
@@ -84,7 +82,7 @@ class SessionInterface:
|
|
|
84
82
|
the browser stops accessing the app.
|
|
85
83
|
"""
|
|
86
84
|
if session.permanent:
|
|
87
|
-
return datetime.now(
|
|
85
|
+
return datetime.now(UTC) + app.permanent_session_lifetime
|
|
88
86
|
else:
|
|
89
87
|
return None
|
|
90
88
|
|
|
@@ -4,9 +4,7 @@ from collections.abc import AsyncIterator
|
|
|
4
4
|
from typing import Any
|
|
5
5
|
from typing import TYPE_CHECKING
|
|
6
6
|
|
|
7
|
-
from flask.templating import
|
|
8
|
-
DispatchingJinjaLoader as DispatchingJinjaLoader, # noqa: F401
|
|
9
|
-
)
|
|
7
|
+
from flask.templating import DispatchingJinjaLoader as DispatchingJinjaLoader # noqa: F401
|
|
10
8
|
from jinja2 import Environment as BaseEnvironment
|
|
11
9
|
from jinja2 import Template
|
|
12
10
|
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
__all__ = (
|
|
4
|
+
"make_test_body_with_headers",
|
|
5
|
+
"make_test_headers_path_and_query_string",
|
|
6
|
+
"make_test_scope",
|
|
7
|
+
"no_op_push",
|
|
8
|
+
"AnyQuartClient",
|
|
9
|
+
"AnyQuartCliRunner",
|
|
10
|
+
"TestApp",
|
|
11
|
+
"WebsocketResponseError",
|
|
12
|
+
)
|
|
13
|
+
|
|
3
14
|
from typing import Any
|
|
4
15
|
from typing import TYPE_CHECKING
|
|
5
16
|
|
|
@@ -13,7 +24,6 @@ from .utils import make_test_body_with_headers
|
|
|
13
24
|
from .utils import make_test_headers_path_and_query_string
|
|
14
25
|
from .utils import make_test_scope
|
|
15
26
|
from .utils import no_op_push
|
|
16
|
-
from .utils import sentinel
|
|
17
27
|
|
|
18
28
|
if TYPE_CHECKING:
|
|
19
29
|
from ..app import AnyQuart
|
|
@@ -32,16 +42,3 @@ class AnyQuartCliRunner(CliRunner):
|
|
|
32
42
|
kwargs["obj"] = ScriptInfo(create_app=lambda: self.app)
|
|
33
43
|
|
|
34
44
|
return super().invoke(cli, args, **kwargs)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
__all__ = (
|
|
38
|
-
"make_test_body_with_headers",
|
|
39
|
-
"make_test_headers_path_and_query_string",
|
|
40
|
-
"make_test_scope",
|
|
41
|
-
"no_op_push",
|
|
42
|
-
"AnyQuartClient",
|
|
43
|
-
"AnyQuartCliRunner",
|
|
44
|
-
"sentinel",
|
|
45
|
-
"TestApp",
|
|
46
|
-
"WebsocketResponseError",
|
|
47
|
-
)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import sys
|
|
3
4
|
from typing import Any
|
|
4
5
|
from typing import AnyStr
|
|
5
6
|
from typing import cast
|
|
@@ -30,7 +31,11 @@ from ..utils import encode_headers
|
|
|
30
31
|
if TYPE_CHECKING:
|
|
31
32
|
from ..app import AnyQuart # noqa
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
if sys.version_info < (3, 15):
|
|
35
|
+
from typing_extensions import sentinel as sentinel
|
|
36
|
+
|
|
37
|
+
_sentinel = sentinel("_sentinel")
|
|
38
|
+
sentinel = _sentinel # type: ignore
|
|
34
39
|
|
|
35
40
|
|
|
36
41
|
def make_test_headers_path_and_query_string(
|
|
@@ -84,7 +89,7 @@ def make_test_body_with_headers(
|
|
|
84
89
|
data: AnyStr | None = None,
|
|
85
90
|
form: dict | None = None,
|
|
86
91
|
files: dict[str, FileStorage] | None = None,
|
|
87
|
-
json: Any =
|
|
92
|
+
json: Any = _sentinel,
|
|
88
93
|
app: AnyQuart | None = None,
|
|
89
94
|
) -> tuple[bytes, Headers]:
|
|
90
95
|
"""Make the body bytes with associated headers.
|
|
@@ -98,11 +103,11 @@ def make_test_body_with_headers(
|
|
|
98
103
|
json: Data to send json encoded in the request body.
|
|
99
104
|
|
|
100
105
|
"""
|
|
101
|
-
if [json is not
|
|
106
|
+
if [json is not _sentinel, form is not None, data is not None].count(True) > 1:
|
|
102
107
|
raise ValueError(
|
|
103
108
|
"anyquart test args 'json', 'form', and 'data' are mutually exclusive"
|
|
104
109
|
)
|
|
105
|
-
if [json is not
|
|
110
|
+
if [json is not _sentinel, files is not None, data is not None].count(True) > 1:
|
|
106
111
|
raise ValueError(
|
|
107
112
|
"anyquart test args 'files', 'json', and 'data' are mutually exclusive"
|
|
108
113
|
)
|
|
@@ -116,7 +121,7 @@ def make_test_body_with_headers(
|
|
|
116
121
|
elif isinstance(data, bytes):
|
|
117
122
|
request_data = data
|
|
118
123
|
|
|
119
|
-
if json is not
|
|
124
|
+
if json is not _sentinel:
|
|
120
125
|
request_data = dumps(json).encode("utf-8")
|
|
121
126
|
headers["Content-Type"] = "application/json"
|
|
122
127
|
elif files is not None:
|
|
@@ -1,44 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import os
|
|
4
|
-
import sys
|
|
5
|
-
from collections.abc import AsyncGenerator
|
|
6
|
-
from collections.abc import Awaitable
|
|
7
|
-
from collections.abc import Callable
|
|
8
|
-
from collections.abc import Iterable
|
|
9
|
-
from collections.abc import Iterator
|
|
10
|
-
from collections.abc import Mapping
|
|
11
|
-
from collections.abc import Sequence
|
|
12
|
-
from contextlib import AbstractAsyncContextManager
|
|
13
|
-
from datetime import datetime
|
|
14
|
-
from datetime import timedelta
|
|
15
|
-
from http.cookiejar import CookieJar
|
|
16
|
-
from types import TracebackType
|
|
17
|
-
from typing import Any
|
|
18
|
-
from typing import AnyStr
|
|
19
|
-
from typing import Literal
|
|
20
|
-
from typing import Optional
|
|
21
|
-
from typing import Protocol
|
|
22
|
-
from typing import TYPE_CHECKING
|
|
23
|
-
from typing import TypedDict
|
|
24
|
-
from typing import Union
|
|
25
|
-
|
|
26
|
-
from .datastructures import FileStorage
|
|
27
|
-
|
|
28
|
-
if sys.version_info >= (3, 11):
|
|
29
|
-
from typing import NotRequired
|
|
30
|
-
else:
|
|
31
|
-
from typing_extensions import NotRequired
|
|
32
|
-
|
|
33
|
-
if TYPE_CHECKING:
|
|
34
|
-
from werkzeug.datastructures import Authorization # noqa: F401
|
|
35
|
-
from werkzeug.datastructures import Headers # noqa: F401
|
|
36
|
-
from werkzeug.wrappers import Response as WerkzeugResponse
|
|
37
|
-
|
|
38
|
-
from .app import AnyQuart
|
|
39
|
-
from .sessions import SessionMixin
|
|
40
|
-
from .wrappers.response import Response
|
|
41
|
-
|
|
42
3
|
__all__ = (
|
|
43
4
|
"ASGIVersions",
|
|
44
5
|
"HTTPScope",
|
|
@@ -73,6 +34,41 @@ __all__ = (
|
|
|
73
34
|
"ASGISendCallable",
|
|
74
35
|
)
|
|
75
36
|
|
|
37
|
+
import os
|
|
38
|
+
from collections.abc import AsyncGenerator
|
|
39
|
+
from collections.abc import Awaitable
|
|
40
|
+
from collections.abc import Callable
|
|
41
|
+
from collections.abc import Iterable
|
|
42
|
+
from collections.abc import Iterator
|
|
43
|
+
from collections.abc import Mapping
|
|
44
|
+
from collections.abc import Sequence
|
|
45
|
+
from contextlib import AbstractAsyncContextManager
|
|
46
|
+
from datetime import datetime
|
|
47
|
+
from datetime import timedelta
|
|
48
|
+
from http.cookiejar import CookieJar
|
|
49
|
+
from types import TracebackType
|
|
50
|
+
from typing import Any
|
|
51
|
+
from typing import AnyStr
|
|
52
|
+
from typing import Literal
|
|
53
|
+
from typing import NotRequired
|
|
54
|
+
from typing import Optional
|
|
55
|
+
from typing import Protocol
|
|
56
|
+
from typing import TYPE_CHECKING
|
|
57
|
+
from typing import TypedDict
|
|
58
|
+
from typing import Union
|
|
59
|
+
|
|
60
|
+
from .datastructures import FileStorage
|
|
61
|
+
|
|
62
|
+
if TYPE_CHECKING:
|
|
63
|
+
from werkzeug.datastructures import Authorization # noqa: F401
|
|
64
|
+
from werkzeug.datastructures import Headers # noqa: F401
|
|
65
|
+
from werkzeug.wrappers import Response as WerkzeugResponse
|
|
66
|
+
|
|
67
|
+
from .app import AnyQuart
|
|
68
|
+
from .sessions import SessionMixin
|
|
69
|
+
from .wrappers.response import Response
|
|
70
|
+
|
|
71
|
+
|
|
76
72
|
FilePath = Union[bytes, str, os.PathLike]
|
|
77
73
|
|
|
78
74
|
# The possible types that are directly convertible or are a Response object.
|
|
@@ -105,7 +105,7 @@ class IterableBody(ResponseBody):
|
|
|
105
105
|
if isinstance(iterable, Iterable):
|
|
106
106
|
self.iter = run_sync_iterable(iter(iterable))
|
|
107
107
|
else:
|
|
108
|
-
self.iter =
|
|
108
|
+
self.iter = aiter(iterable)
|
|
109
109
|
|
|
110
110
|
async def __aenter__(self) -> IterableBody:
|
|
111
111
|
return self
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from .base import BaseRequestWebsocket
|
|
4
|
-
from .request import Body
|
|
5
|
-
from .request import Request
|
|
6
|
-
from .response import Response
|
|
7
|
-
from .websocket import Websocket
|
|
8
|
-
|
|
9
3
|
__all__ = (
|
|
10
4
|
"BaseRequestWebsocket",
|
|
11
5
|
"Body",
|
|
@@ -13,3 +7,9 @@ __all__ = (
|
|
|
13
7
|
"Response",
|
|
14
8
|
"Websocket",
|
|
15
9
|
)
|
|
10
|
+
|
|
11
|
+
from .base import BaseRequestWebsocket
|
|
12
|
+
from .request import Body
|
|
13
|
+
from .request import Request
|
|
14
|
+
from .response import Response
|
|
15
|
+
from .websocket import Websocket
|
|
File without changes
|
|
File without changes
|
|
File without changes
|