anyquart 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.
Files changed (39) hide show
  1. anyquart-0.1.0/LICENSE.txt +20 -0
  2. anyquart-0.1.0/PKG-INFO +114 -0
  3. anyquart-0.1.0/README.md +77 -0
  4. anyquart-0.1.0/pyproject.toml +211 -0
  5. anyquart-0.1.0/src/anyquart/__init__.py +55 -0
  6. anyquart-0.1.0/src/anyquart/__main__.py +6 -0
  7. anyquart-0.1.0/src/anyquart/app.py +1809 -0
  8. anyquart-0.1.0/src/anyquart/asgi.py +426 -0
  9. anyquart-0.1.0/src/anyquart/blueprints.py +416 -0
  10. anyquart-0.1.0/src/anyquart/cli.py +775 -0
  11. anyquart-0.1.0/src/anyquart/config.py +38 -0
  12. anyquart-0.1.0/src/anyquart/ctx.py +483 -0
  13. anyquart-0.1.0/src/anyquart/datastructures.py +46 -0
  14. anyquart-0.1.0/src/anyquart/debug.py +114 -0
  15. anyquart-0.1.0/src/anyquart/formparser.py +241 -0
  16. anyquart-0.1.0/src/anyquart/globals.py +59 -0
  17. anyquart-0.1.0/src/anyquart/helpers.py +403 -0
  18. anyquart-0.1.0/src/anyquart/json/__init__.py +47 -0
  19. anyquart-0.1.0/src/anyquart/json/provider.py +2 -0
  20. anyquart-0.1.0/src/anyquart/json/tag.py +10 -0
  21. anyquart-0.1.0/src/anyquart/logging.py +80 -0
  22. anyquart-0.1.0/src/anyquart/py.typed +1 -0
  23. anyquart-0.1.0/src/anyquart/routing.py +92 -0
  24. anyquart-0.1.0/src/anyquart/sessions.py +253 -0
  25. anyquart-0.1.0/src/anyquart/signals.py +80 -0
  26. anyquart-0.1.0/src/anyquart/templating.py +157 -0
  27. anyquart-0.1.0/src/anyquart/testing/__init__.py +47 -0
  28. anyquart-0.1.0/src/anyquart/testing/app.py +92 -0
  29. anyquart-0.1.0/src/anyquart/testing/client.py +457 -0
  30. anyquart-0.1.0/src/anyquart/testing/connections.py +227 -0
  31. anyquart-0.1.0/src/anyquart/testing/utils.py +229 -0
  32. anyquart-0.1.0/src/anyquart/typing.py +328 -0
  33. anyquart-0.1.0/src/anyquart/utils.py +166 -0
  34. anyquart-0.1.0/src/anyquart/views.py +134 -0
  35. anyquart-0.1.0/src/anyquart/wrappers/__init__.py +15 -0
  36. anyquart-0.1.0/src/anyquart/wrappers/base.py +119 -0
  37. anyquart-0.1.0/src/anyquart/wrappers/request.py +415 -0
  38. anyquart-0.1.0/src/anyquart/wrappers/response.py +500 -0
  39. anyquart-0.1.0/src/anyquart/wrappers/websocket.py +104 -0
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright 2017 Pallets
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,114 @@
1
+ Metadata-Version: 2.4
2
+ Name: anyquart
3
+ Version: 0.1.0
4
+ Summary: A fork of Quart that uses AnyIO
5
+ Author-email: pgjones <philip.graham.jones@googlemail.com>, NIYONSHUTI Emmanuel <emmanuelniyonshuti13@gmail.com>
6
+ Maintainer-email: NIYONSHUTI Emmanuel <emmanuelniyonshuti13@gmail.com>
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ License-Expression: MIT
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Environment :: Web Environment
12
+ Classifier: Framework :: Flask
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python
16
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
17
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
18
+ Classifier: Typing :: Typed
19
+ License-File: LICENSE.txt
20
+ Requires-Dist: anyio>=4.14.0,<5
21
+ Requires-Dist: blinker>=1.6
22
+ Requires-Dist: click>=8.0
23
+ Requires-Dist: flask>=3.0
24
+ Requires-Dist: anycorn>=0.11.2
25
+ Requires-Dist: importlib-metadata; python_version < '3.13'
26
+ Requires-Dist: itsdangerous
27
+ Requires-Dist: jinja2
28
+ Requires-Dist: markupsafe
29
+ Requires-Dist: typing-extensions; python_version < '3.13'
30
+ Requires-Dist: werkzeug>=3.0
31
+ Requires-Dist: python-dotenv ; extra == "dotenv"
32
+ Project-URL: Documentation, https://quart.palletsprojects.com
33
+ Project-URL: Donate, https://palletsprojects.com/donate
34
+ Project-URL: Source, https://github.com/EmmanuelNiyonshuti/anyquart
35
+ Provides-Extra: dotenv
36
+
37
+ # AnyQuart
38
+
39
+ Quart runs on Asyncio and when you want to run it on Trio event loop you use [quart-trio](https://github.com/pgjones/quart-trio) extension.
40
+
41
+ AnyQuart is [Quart](https://github.com/pallets/quart) running on [AnyIO](https://github.com/agronholm/anyio). It is a fork of Quart 0.20.1.
42
+
43
+ All credit for Quart goes to the [Pallets](https://palletsprojects.com) team.
44
+
45
+ ## Usage
46
+ You will have to replace `quart` with `anyquart` and `Quart` with `AnyQuart`.
47
+
48
+ Install from PyPI using an installer such as pip. Requires Python 3.10+.
49
+
50
+ ```
51
+ $ pip install anyquart
52
+ ```
53
+
54
+ Save the following as `app.py`.
55
+
56
+ ```python
57
+ from anyquart import AnyQuart, websocket, render_template
58
+
59
+ app = AnyQuart(__name__)
60
+
61
+ @app.route("/")
62
+ async def hello():
63
+ return await render_template("index.html")
64
+
65
+ @app.route("/api")
66
+ async def json():
67
+ return {"hello": "world"}
68
+
69
+ @app.websocket("/ws")
70
+ async def ws():
71
+ while True:
72
+ await websocket.send("hello")
73
+ await websocket.send_json({"hello": "world"})
74
+ ```
75
+
76
+ ```
77
+ $ anyquart run
78
+ * Running on http://127.0.0.1:5000 (CTRL + C to quit)
79
+ ```
80
+
81
+ # Testing
82
+ Pytest does not natively support async test functions hence we need an async framework for async tests and fixtures.
83
+ Quart uses pytest-asyncio, here we use AnyIO's pytest plugin. You will need to specify which backend your tests run on via the `anyio_backend` fixture and decorate your asynchronous tests with `@pytest.mark.anyio`.
84
+
85
+ ```python
86
+ import pytest
87
+
88
+ from app import app
89
+
90
+ @pytest.fixture()
91
+ def anyio_backend():
92
+ return "trio" # you can replace with "asyncio"
93
+
94
+ @pytest.fixture()
95
+ def test_client():
96
+ return app.test_client()
97
+
98
+ @pytest.mark.anyio
99
+ async def test_do_something(test_client) -> None:
100
+ response = await test_client.get("/")
101
+ assert response.status_code == 200
102
+ assert await response.json == {"hello": "world"}
103
+
104
+ ```
105
+
106
+ ## Differences from Quart
107
+ - Uses [Anycorn](https://github.com/davidbrochart/anycorn) instead of Hypercorn as the development server
108
+ - Works with both asyncio and Trio via AnyIO
109
+ - Uses AnyIO's file I/O instead of aiofiles
110
+ - AnyIO primitives work freely in route handlers, giving you structured concurrency out of the box
111
+ - Tests need `@pytest.mark.anyio` and the `anyio_backend` fixture instead of pytest-asyncio
112
+
113
+ Refer to the [Quart documentation](https://quart.palletsprojects.com) for more details.
114
+
@@ -0,0 +1,77 @@
1
+ # AnyQuart
2
+
3
+ Quart runs on Asyncio and when you want to run it on Trio event loop you use [quart-trio](https://github.com/pgjones/quart-trio) extension.
4
+
5
+ AnyQuart is [Quart](https://github.com/pallets/quart) running on [AnyIO](https://github.com/agronholm/anyio). It is a fork of Quart 0.20.1.
6
+
7
+ All credit for Quart goes to the [Pallets](https://palletsprojects.com) team.
8
+
9
+ ## Usage
10
+ You will have to replace `quart` with `anyquart` and `Quart` with `AnyQuart`.
11
+
12
+ Install from PyPI using an installer such as pip. Requires Python 3.10+.
13
+
14
+ ```
15
+ $ pip install anyquart
16
+ ```
17
+
18
+ Save the following as `app.py`.
19
+
20
+ ```python
21
+ from anyquart import AnyQuart, websocket, render_template
22
+
23
+ app = AnyQuart(__name__)
24
+
25
+ @app.route("/")
26
+ async def hello():
27
+ return await render_template("index.html")
28
+
29
+ @app.route("/api")
30
+ async def json():
31
+ return {"hello": "world"}
32
+
33
+ @app.websocket("/ws")
34
+ async def ws():
35
+ while True:
36
+ await websocket.send("hello")
37
+ await websocket.send_json({"hello": "world"})
38
+ ```
39
+
40
+ ```
41
+ $ anyquart run
42
+ * Running on http://127.0.0.1:5000 (CTRL + C to quit)
43
+ ```
44
+
45
+ # Testing
46
+ Pytest does not natively support async test functions hence we need an async framework for async tests and fixtures.
47
+ Quart uses pytest-asyncio, here we use AnyIO's pytest plugin. You will need to specify which backend your tests run on via the `anyio_backend` fixture and decorate your asynchronous tests with `@pytest.mark.anyio`.
48
+
49
+ ```python
50
+ import pytest
51
+
52
+ from app import app
53
+
54
+ @pytest.fixture()
55
+ def anyio_backend():
56
+ return "trio" # you can replace with "asyncio"
57
+
58
+ @pytest.fixture()
59
+ def test_client():
60
+ return app.test_client()
61
+
62
+ @pytest.mark.anyio
63
+ async def test_do_something(test_client) -> None:
64
+ response = await test_client.get("/")
65
+ assert response.status_code == 200
66
+ assert await response.json == {"hello": "world"}
67
+
68
+ ```
69
+
70
+ ## Differences from Quart
71
+ - Uses [Anycorn](https://github.com/davidbrochart/anycorn) instead of Hypercorn as the development server
72
+ - Works with both asyncio and Trio via AnyIO
73
+ - Uses AnyIO's file I/O instead of aiofiles
74
+ - AnyIO primitives work freely in route handlers, giving you structured concurrency out of the box
75
+ - Tests need `@pytest.mark.anyio` and the `anyio_backend` fixture instead of pytest-asyncio
76
+
77
+ Refer to the [Quart documentation](https://quart.palletsprojects.com) for more details.
@@ -0,0 +1,211 @@
1
+ [project]
2
+ name = "anyquart"
3
+ version = "0.1.0"
4
+ description = "A fork of Quart that uses AnyIO"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = ["LICENSE.txt"]
8
+ authors = [
9
+ { name = "pgjones", email = "philip.graham.jones@googlemail.com" },
10
+ { name = "NIYONSHUTI Emmanuel", email = "emmanuelniyonshuti13@gmail.com" }
11
+ ]
12
+ maintainers = [{ name = "NIYONSHUTI Emmanuel", email = "emmanuelniyonshuti13@gmail.com" }]
13
+ classifiers = [
14
+ "Development Status :: 4 - Beta",
15
+ "Environment :: Web Environment",
16
+ "Framework :: Flask",
17
+ "Intended Audience :: Developers",
18
+ "Operating System :: OS Independent",
19
+ "Programming Language :: Python",
20
+ "Topic :: Internet :: WWW/HTTP :: Dynamic Content",
21
+ "Topic :: Software Development :: Libraries :: Application Frameworks",
22
+ "Typing :: Typed",
23
+ ]
24
+ requires-python = ">=3.10"
25
+ dependencies = [
26
+ "anyio>=4.14.0,<5",
27
+ "blinker>=1.6",
28
+ "click>=8.0",
29
+ "flask>=3.0",
30
+ "anycorn>=0.11.2",
31
+ "importlib-metadata; python_version < '3.13'",
32
+ "itsdangerous",
33
+ "jinja2",
34
+ "markupsafe",
35
+ "typing-extensions; python_version < '3.13'",
36
+ "werkzeug>=3.0",
37
+ ]
38
+
39
+ [project.urls]
40
+ Donate = "https://palletsprojects.com/donate"
41
+ Documentation = "https://quart.palletsprojects.com"
42
+ Source = "https://github.com/EmmanuelNiyonshuti/anyquart"
43
+
44
+ [project.optional-dependencies]
45
+ dotenv = ["python-dotenv"]
46
+
47
+ [project.scripts]
48
+ anyquart = "anyquart.cli:main"
49
+
50
+ [dependency-groups]
51
+ dev = [
52
+ "trio==0.33.0",
53
+ "ruff",
54
+ "tox",
55
+ "tox-uv",
56
+ ]
57
+
58
+ gha-update = [
59
+ "gha-update ; python_full_version >= '3.12'",
60
+ ]
61
+ pre-commit = [
62
+ "pre-commit",
63
+ "pre-commit-uv",
64
+ ]
65
+ tests = [
66
+ "hypothesis",
67
+ "pytest",
68
+ "pytest-cov",
69
+ "pytest-sugar",
70
+ "python-dotenv",
71
+ ]
72
+ typing = [
73
+ "mypy",
74
+ "pyright",
75
+ "pytest",
76
+ ]
77
+
78
+ [build-system]
79
+ requires = ["flit-core<4"]
80
+ build-backend = "flit_core.buildapi"
81
+
82
+ [tool.flit.module]
83
+ name = "anyquart"
84
+
85
+ [tool.uv]
86
+ default-groups = ["dev", "pre-commit", "tests", "typing"]
87
+
88
+ [tool.pytest.ini_options]
89
+ addopts = "--no-cov-on-fail --showlocals --strict-markers"
90
+ testpaths = ["tests"]
91
+ filterwarnings = [
92
+ "error",
93
+ ]
94
+
95
+ [tool.coverage.run]
96
+ branch = true
97
+ source = ["anyquart", "tests"]
98
+
99
+ [tool.coverage.paths]
100
+ source = ["src", "*/site-packages"]
101
+
102
+ [tool.coverage.report]
103
+ exclude_also = [
104
+ "if t.TYPE_CHECKING",
105
+ "raise NotImplementedError",
106
+ ": \\.{3}",
107
+ ]
108
+
109
+ [tool.mypy]
110
+ python_version = "3.10"
111
+ files = ["src", "tests"]
112
+ show_error_codes = true
113
+ pretty = true
114
+ strict = true
115
+ # TODO fully satisfy strict mode and remove these customizations
116
+ allow_redefinition = true
117
+ disallow_any_generics = false
118
+ disallow_untyped_calls = false
119
+ implicit_reexport = true
120
+ no_implicit_optional = true
121
+ strict_optional = false
122
+ warn_return_any = false
123
+
124
+ [tool.pyright]
125
+ pythonVersion = "3.10"
126
+ include = ["src", "tests"]
127
+ typeCheckingMode = "basic"
128
+
129
+ [tool.ruff]
130
+ src = ["src"]
131
+ fix = true
132
+ show-fixes = true
133
+ output-format = "full"
134
+
135
+ [tool.ruff.lint]
136
+ select = [
137
+ "B", # flake8-bugbear
138
+ "E", # pycodestyle error
139
+ "F", # pyflakes
140
+ "FA", # flake8-future-annotations
141
+ "I", # isort
142
+ "N", # pep8-naming
143
+ "UP", # pyupgrade
144
+ "W", # pycodestyle warning
145
+ ]
146
+ ignore = [
147
+ "UP038", # keep isinstance tuple
148
+ "UP007", # Union[] is needed for forward-referenced type aliases
149
+ "UP045"
150
+ ]
151
+
152
+ [tool.ruff.lint.isort]
153
+ force-single-line = true
154
+ order-by-type = false
155
+
156
+ [tool.tox]
157
+ env_list = [
158
+ "py3.14", "py3.13", "py3.12", "py3.11", "py3.10",
159
+ "style",
160
+ "typing",
161
+ "docs",
162
+ ]
163
+
164
+ [tool.tox.env_run_base]
165
+ description = "pytest on latest dependency versions"
166
+ runner = "uv-venv-lock-runner"
167
+ package = "wheel"
168
+ wheel_build_env = ".pkg"
169
+ constrain_package_deps = true
170
+ use_frozen_constraints = true
171
+ dependency_groups = ["tests", "dev"]
172
+ commands = [[
173
+ "pytest", "-v", "--tb=short", "--basetemp={env_tmp_dir}", "--cov=anyquart",
174
+ {replace = "posargs", default = [], extend = true},
175
+ ]]
176
+
177
+ [tool.tox.env.style]
178
+ description = "run all pre-commit hooks on all files"
179
+ dependency_groups = ["pre-commit"]
180
+ skip_install = true
181
+ commands = [["pre-commit", "run", "--all-files"]]
182
+
183
+ [tool.tox.env.typing]
184
+ description = "run static type checkers"
185
+ dependency_groups = ["tests", "typing"]
186
+ commands = [
187
+ ["mypy"],
188
+ ]
189
+
190
+
191
+ [tool.tox.env.update-actions]
192
+ description = "update GitHub Actions pins"
193
+ labels = ["update"]
194
+ dependency_groups = ["gha-update"]
195
+ skip_install = true
196
+ commands = [["gha-update"]]
197
+
198
+ [tool.tox.env.update-pre_commit]
199
+ description = "update pre-commit pins"
200
+ labels = ["update"]
201
+ dependency_groups = ["pre-commit"]
202
+ skip_install = true
203
+ commands = [["pre-commit", "autoupdate", "--freeze", "-j4"]]
204
+
205
+ [tool.tox.env.update-requirements]
206
+ description = "update uv lock"
207
+ labels = ["update"]
208
+ dependency_groups = []
209
+ no_default_groups = true
210
+ skip_install = true
211
+ commands = [["uv", "lock", {replace = "posargs", default = ["-U"], extend = true}]]
@@ -0,0 +1,55 @@
1
+ from __future__ import annotations
2
+
3
+ from markupsafe import escape as escape
4
+ from markupsafe import Markup as Markup
5
+
6
+ from .app import AnyQuart as AnyQuart
7
+ from .blueprints import Blueprint as Blueprint
8
+ from .config import Config as Config
9
+ from .ctx import after_this_request as after_this_request
10
+ from .ctx import copy_current_app_context as copy_current_app_context
11
+ from .ctx import copy_current_request_context as copy_current_request_context
12
+ from .ctx import copy_current_websocket_context as copy_current_websocket_context
13
+ from .ctx import has_app_context as has_app_context
14
+ from .ctx import has_request_context as has_request_context
15
+ from .ctx import has_websocket_context as has_websocket_context
16
+ from .globals import current_app as current_app
17
+ from .globals import g as g
18
+ from .globals import request as request
19
+ from .globals import session as session
20
+ from .globals import websocket as websocket
21
+ from .helpers import abort as abort
22
+ from .helpers import flash as flash
23
+ from .helpers import get_flashed_messages as get_flashed_messages
24
+ from .helpers import get_template_attribute as get_template_attribute
25
+ from .helpers import make_push_promise as make_push_promise
26
+ from .helpers import make_response as make_response
27
+ from .helpers import redirect as redirect
28
+ from .helpers import send_file as send_file
29
+ from .helpers import send_from_directory as send_from_directory
30
+ from .helpers import stream_with_context as stream_with_context
31
+ from .helpers import url_for as url_for
32
+ from .json import jsonify as jsonify
33
+ from .signals import appcontext_popped as appcontext_popped
34
+ from .signals import appcontext_pushed as appcontext_pushed
35
+ from .signals import appcontext_tearing_down as appcontext_tearing_down
36
+ from .signals import before_render_template as before_render_template
37
+ from .signals import got_request_exception as got_request_exception
38
+ from .signals import got_websocket_exception as got_websocket_exception
39
+ from .signals import message_flashed as message_flashed
40
+ from .signals import request_finished as request_finished
41
+ from .signals import request_started as request_started
42
+ from .signals import request_tearing_down as request_tearing_down
43
+ from .signals import signals_available as signals_available
44
+ from .signals import template_rendered as template_rendered
45
+ from .signals import websocket_finished as websocket_finished
46
+ from .signals import websocket_started as websocket_started
47
+ from .signals import websocket_tearing_down as websocket_tearing_down
48
+ from .templating import render_template as render_template
49
+ from .templating import render_template_string as render_template_string
50
+ from .templating import stream_template as stream_template
51
+ from .templating import stream_template_string as stream_template_string
52
+ from .typing import ResponseReturnValue as ResponseReturnValue
53
+ from .wrappers import Request as Request
54
+ from .wrappers import Response as Response
55
+ from .wrappers import Websocket as Websocket
@@ -0,0 +1,6 @@
1
+ from __future__ import annotations
2
+
3
+ if __name__ == "__main__":
4
+ from .cli import main
5
+
6
+ main()