microdot 2.6.2__tar.gz → 2.7.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.
- {microdot-2.6.2/src/microdot.egg-info → microdot-2.7.0}/PKG-INFO +31 -11
- {microdot-2.6.2 → microdot-2.7.0}/README.md +24 -8
- {microdot-2.6.2 → microdot-2.7.0}/pyproject.toml +7 -3
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/__init__.py +1 -1
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/asgi.pyi +10 -10
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/auth.pyi +9 -9
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/cors.py +7 -6
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/cors.pyi +4 -4
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/csrf.py +5 -0
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/csrf.pyi +3 -3
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/jinja.pyi +5 -5
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/login.pyi +6 -6
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/microdot.py +32 -6
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/microdot.pyi +66 -63
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/multipart.pyi +8 -8
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/session.py +3 -0
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/session.pyi +10 -11
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/sse.pyi +2 -2
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/test_client.py +15 -1
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/test_client.pyi +15 -12
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/utemplate.pyi +5 -5
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/websocket.pyi +5 -5
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/wsgi.pyi +4 -4
- {microdot-2.6.2 → microdot-2.7.0/src/microdot.egg-info}/PKG-INFO +31 -11
- {microdot-2.6.2 → microdot-2.7.0}/tests/test_cors.py +7 -1
- {microdot-2.6.2 → microdot-2.7.0}/tests/test_csrf.py +6 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/test_microdot.py +70 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/test_session.py +20 -0
- {microdot-2.6.2 → microdot-2.7.0}/tox.ini +26 -3
- {microdot-2.6.2 → microdot-2.7.0}/LICENSE +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/MANIFEST.in +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/Makefile +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/_static/css/custom.css +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/api/asgi.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/api/auth.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/api/cors.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/api/csrf.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/api/index.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/api/jinja.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/api/login.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/api/microdot.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/api/multipart.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/api/sessions.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/api/sse.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/api/test_client.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/api/utemplate.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/api/websocket.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/api/wsgi.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/conf.py +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/extensions/auth.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/extensions/cors.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/extensions/csrf.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/extensions/index.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/extensions/login.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/extensions/multipart.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/extensions/production.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/extensions/sessions.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/extensions/sse.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/extensions/templates.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/extensions/test_client.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/extensions/websocket.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/implementation/freezing.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/implementation/index.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/implementation/migrating.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/index.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/intro.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/make.bat +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/users-guide/concurrency.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/users-guide/defining-routes.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/users-guide/index.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/users-guide/intro.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/users-guide/request-object.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/docs/users-guide/responses.rst +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/setup.cfg +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/__init__.pyi +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/asgi.py +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/auth.py +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/helpers.py +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/helpers.pyi +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/jinja.py +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/login.py +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/multipart.py +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/py.typed +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/sse.py +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/utemplate.py +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/websocket.py +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot/wsgi.py +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot.egg-info/SOURCES.txt +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot.egg-info/dependency_links.txt +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot.egg-info/not-zip-safe +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot.egg-info/requires.txt +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/src/microdot.egg-info/top_level.txt +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/__init__.py +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/files/test.bin +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/files/test.css +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/files/test.gif +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/files/test.gz +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/files/test.html +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/files/test.jpg +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/files/test.js +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/files/test.json +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/files/test.png +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/files/test.txt +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/files/test.txt.gz +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/mock_socket.py +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/templates/hello.jinja.txt +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/templates/hello.utemplate.txt +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/templates/hello_utemplate_txt.py +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/test_asgi.py +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/test_auth.py +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/test_end2end.py +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/test_jinja.py +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/test_login.py +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/test_multidict.py +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/test_multipart.py +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/test_request.py +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/test_response.py +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/test_sse.py +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/test_url_pattern.py +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/test_urlencode.py +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/test_utemplate.py +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/test_websocket.py +0 -0
- {microdot-2.6.2 → microdot-2.7.0}/tests/test_wsgi.py +0 -0
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: microdot
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.7.0
|
|
4
4
|
Summary: The impossibly small web framework for MicroPython
|
|
5
5
|
Author-email: Miguel Grinberg <miguel.grinberg@gmail.com>
|
|
6
|
-
Project-URL: Homepage, https://
|
|
7
|
-
Project-URL:
|
|
6
|
+
Project-URL: Homepage, https://code.miguelgrinberg.com/miguelgrinberg/microdot
|
|
7
|
+
Project-URL: Documentation, https://microdot.readthedocs.io/
|
|
8
|
+
Project-URL: Source Code, https://code.miguelgrinberg.com/miguelgrinberg/microdot
|
|
9
|
+
Project-URL: Issue Tracker, https://code.miguelgrinberg.com/miguelgrinberg/microdot/issues
|
|
10
|
+
Project-URL: Changelog, https://code.miguelgrinberg.com/miguelgrinberg/microdot/src/branch/main/CHANGES.md
|
|
11
|
+
Project-URL: Funding, https://code.miguelgrinberg.com/miguelgrinberg/microdot#sponsor-this-project
|
|
8
12
|
Classifier: Environment :: Web Environment
|
|
9
13
|
Classifier: Intended Audience :: Developers
|
|
10
14
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -22,8 +26,9 @@ Requires-Dist: furo; extra == "docs"
|
|
|
22
26
|
Requires-Dist: pyjwt; extra == "docs"
|
|
23
27
|
Dynamic: license-file
|
|
24
28
|
|
|
25
|
-
#
|
|
26
|
-
|
|
29
|
+
# Microdot
|
|
30
|
+
|
|
31
|
+
[](https://code.miguelgrinberg.com/miguelgrinberg/microdot/actions)
|
|
27
32
|
|
|
28
33
|
*“The impossibly small web framework for Python and MicroPython”*
|
|
29
34
|
|
|
@@ -45,14 +50,16 @@ app.run()
|
|
|
45
50
|
|
|
46
51
|
## Resources
|
|
47
52
|
|
|
48
|
-
- [
|
|
49
|
-
-
|
|
50
|
-
|
|
51
|
-
|
|
53
|
+
- [git](https://code.miguelgrinberg.com/miguelgrinberg/microdot)
|
|
54
|
+
- [Change Log](https://code.miguelgrinberg.com/miguelgrinberg/microdot/src/branch/main/CHANGES.md)
|
|
55
|
+
- [Documentation](https://microdot.readthedocs.io/)
|
|
56
|
+
- [PyPI](https://pypi.python.org/pypi/microdot)
|
|
57
|
+
- [Contributor's guide](CONTRIBUTING.md)
|
|
58
|
+
- [Security policy](SECURITY.md)
|
|
52
59
|
|
|
53
|
-
The following links are for version 1 of Microdot:
|
|
60
|
+
The following links are for legacy version 1 of Microdot:
|
|
54
61
|
|
|
55
|
-
- [
|
|
62
|
+
- [git](https://github.com/miguelgrinberg/microdot/tree/v1)
|
|
56
63
|
- [Documentation](https://microdot.readthedocs.io/en/v1/)
|
|
57
64
|
|
|
58
65
|
Note that version 1 is no longer maintained, so you should consider migrating
|
|
@@ -73,3 +80,16 @@ MicroPython and CPython:
|
|
|
73
80
|
- OpenAPI integration, similar to [APIFairy](https://github.com/miguelgrinberg/apifairy) for Flask
|
|
74
81
|
|
|
75
82
|
Do you have other ideas to propose? Let's [discuss them](https://github.com/:miguelgrinberg/microdot/discussions/new?category=ideas)!
|
|
83
|
+
|
|
84
|
+
Sponsor this project
|
|
85
|
+
--------------------
|
|
86
|
+
|
|
87
|
+
This project relies on contributions from its users. If you benefit from it please consider making a single or ongoing monetary contribution in one of the following platforms:
|
|
88
|
+
|
|
89
|
+
- [Github Sponsors](https://github.com/sponsors/miguelgrinberg)
|
|
90
|
+
- [Patreon](https://patreon.com/miguelgrinberg)
|
|
91
|
+
- [Buy me a Coffee](https://buymeacoffee.com/miguelgrinberg)
|
|
92
|
+
- [thanks.dev](https://thanks.dev/u/gh/miguelgrinberg)
|
|
93
|
+
- [PayPal](https://paypal.me/miguelgrinberg)
|
|
94
|
+
|
|
95
|
+
Thank you!
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
1
|
+
# Microdot
|
|
2
|
+
|
|
3
|
+
[](https://code.miguelgrinberg.com/miguelgrinberg/microdot/actions)
|
|
3
4
|
|
|
4
5
|
*“The impossibly small web framework for Python and MicroPython”*
|
|
5
6
|
|
|
@@ -21,14 +22,16 @@ app.run()
|
|
|
21
22
|
|
|
22
23
|
## Resources
|
|
23
24
|
|
|
24
|
-
- [
|
|
25
|
-
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
- [git](https://code.miguelgrinberg.com/miguelgrinberg/microdot)
|
|
26
|
+
- [Change Log](https://code.miguelgrinberg.com/miguelgrinberg/microdot/src/branch/main/CHANGES.md)
|
|
27
|
+
- [Documentation](https://microdot.readthedocs.io/)
|
|
28
|
+
- [PyPI](https://pypi.python.org/pypi/microdot)
|
|
29
|
+
- [Contributor's guide](CONTRIBUTING.md)
|
|
30
|
+
- [Security policy](SECURITY.md)
|
|
28
31
|
|
|
29
|
-
The following links are for version 1 of Microdot:
|
|
32
|
+
The following links are for legacy version 1 of Microdot:
|
|
30
33
|
|
|
31
|
-
- [
|
|
34
|
+
- [git](https://github.com/miguelgrinberg/microdot/tree/v1)
|
|
32
35
|
- [Documentation](https://microdot.readthedocs.io/en/v1/)
|
|
33
36
|
|
|
34
37
|
Note that version 1 is no longer maintained, so you should consider migrating
|
|
@@ -49,3 +52,16 @@ MicroPython and CPython:
|
|
|
49
52
|
- OpenAPI integration, similar to [APIFairy](https://github.com/miguelgrinberg/apifairy) for Flask
|
|
50
53
|
|
|
51
54
|
Do you have other ideas to propose? Let's [discuss them](https://github.com/:miguelgrinberg/microdot/discussions/new?category=ideas)!
|
|
55
|
+
|
|
56
|
+
Sponsor this project
|
|
57
|
+
--------------------
|
|
58
|
+
|
|
59
|
+
This project relies on contributions from its users. If you benefit from it please consider making a single or ongoing monetary contribution in one of the following platforms:
|
|
60
|
+
|
|
61
|
+
- [Github Sponsors](https://github.com/sponsors/miguelgrinberg)
|
|
62
|
+
- [Patreon](https://patreon.com/miguelgrinberg)
|
|
63
|
+
- [Buy me a Coffee](https://buymeacoffee.com/miguelgrinberg)
|
|
64
|
+
- [thanks.dev](https://thanks.dev/u/gh/miguelgrinberg)
|
|
65
|
+
- [PayPal](https://paypal.me/miguelgrinberg)
|
|
66
|
+
|
|
67
|
+
Thank you!
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "microdot"
|
|
3
|
-
version = "2.
|
|
3
|
+
version = "2.7.0"
|
|
4
4
|
authors = [
|
|
5
5
|
{ name = "Miguel Grinberg", email = "miguel.grinberg@gmail.com" },
|
|
6
6
|
]
|
|
@@ -22,8 +22,12 @@ file = "README.md"
|
|
|
22
22
|
content-type = "text/markdown"
|
|
23
23
|
|
|
24
24
|
[project.urls]
|
|
25
|
-
Homepage = "https://
|
|
26
|
-
|
|
25
|
+
Homepage = "https://code.miguelgrinberg.com/miguelgrinberg/microdot"
|
|
26
|
+
Documentation = "https://microdot.readthedocs.io/"
|
|
27
|
+
"Source Code" = "https://code.miguelgrinberg.com/miguelgrinberg/microdot"
|
|
28
|
+
"Issue Tracker" = "https://code.miguelgrinberg.com/miguelgrinberg/microdot/issues"
|
|
29
|
+
Changelog = "https://code.miguelgrinberg.com/miguelgrinberg/microdot/src/branch/main/CHANGES.md"
|
|
30
|
+
Funding = "https://code.miguelgrinberg.com/miguelgrinberg/microdot#sponsor-this-project"
|
|
27
31
|
|
|
28
32
|
[project.optional-dependencies]
|
|
29
33
|
dev = [
|
|
@@ -9,38 +9,38 @@ class Microdot(BaseMicrodot): # type: ignore[no-redef]
|
|
|
9
9
|
embedded_server: bool
|
|
10
10
|
def __init__(self, lifespan_startup: Callable[[dict[str, Any]], Awaitable[None]] = ..., lifespan_shutdown: Callable[[dict[str, Any]], Awaitable[None]] = ...) -> None:
|
|
11
11
|
...
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
async def handle_lifespan(self, scope: dict[str, Any], receive: Callable[[], Awaitable[dict[str, Any]]], send: Callable[[dict[str, Any]], Awaitable[None]]) -> None:
|
|
14
14
|
...
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
async def asgi_app(self, scope: dict[str, Any], receive: Callable[[], Awaitable[dict[str, Any]]], send: Callable[[dict[str, Any]], Awaitable[None]]):
|
|
17
17
|
...
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
async def __call__(self, scope: dict[str, Any], receive: Callable[[], Awaitable[dict[str, Any]]], send: Callable[[dict[str, Any]], Awaitable[None]]):
|
|
20
20
|
...
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
def shutdown(self) -> None:
|
|
23
23
|
...
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
def run(self, host: str = ..., port: int = ..., debug: bool = ..., **options: Any) -> None: # type: ignore[override]
|
|
26
26
|
...
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
class WebSocket(BaseWebSocket):
|
|
31
31
|
closed: bool
|
|
32
32
|
async def handshake(self) -> None:
|
|
33
33
|
...
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
async def receive(self) -> bytes | str:
|
|
36
36
|
...
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
async def send(self, data: bytes | str) -> None: # type: ignore[override]
|
|
39
39
|
...
|
|
40
|
-
|
|
40
|
+
|
|
41
41
|
async def close(self) -> None:
|
|
42
42
|
...
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
async def websocket_upgrade(request: Request) -> BaseWebSocket:
|
|
@@ -6,13 +6,13 @@ class BaseAuth:
|
|
|
6
6
|
error_callback: Callable[[Request], Any | Awaitable[Any]]
|
|
7
7
|
def __init__(self) -> None:
|
|
8
8
|
...
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
def __call__(self, f: Callable[..., Any]):
|
|
11
11
|
...
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
def optional(self, f: Callable[..., Any]):
|
|
14
14
|
...
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class BasicAuth(BaseAuth):
|
|
@@ -23,14 +23,14 @@ class BasicAuth(BaseAuth):
|
|
|
23
23
|
error_callback: Callable[[Request], Any | Awaitable[Any]]
|
|
24
24
|
def __init__(self, realm: str = ..., charset: str = ..., scheme: str = ..., error_status: int = ...) -> None:
|
|
25
25
|
...
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
async def authentication_error(self, request):
|
|
28
28
|
...
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
auth_callback: Callable[[Request, str, str], Any | Awaitable[Any]]
|
|
31
31
|
def authenticate(self, f: Callable[[Request, str, str], Any | Awaitable[Any]]) -> None:
|
|
32
32
|
...
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
class TokenAuth(BaseAuth):
|
|
@@ -40,13 +40,13 @@ class TokenAuth(BaseAuth):
|
|
|
40
40
|
error_callback: Callable[[Request], Any | Awaitable[Any]]
|
|
41
41
|
def __init__(self, header: str = ..., scheme: str = ..., error_status: int = ...) -> None:
|
|
42
42
|
...
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
auth_callback: Callable[[Request, str], Any | Awaitable[Any]]
|
|
45
45
|
def authenticate(self, f: Callable[..., Any | Awaitable[Any]]) -> None:
|
|
46
46
|
...
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
def errorhandler(self, f: Callable[[Request], Any | Awaitable[Any]]) -> None:
|
|
49
49
|
...
|
|
50
|
-
|
|
50
|
+
|
|
51
51
|
async def authentication_error(self, request) -> None:
|
|
52
52
|
...
|
|
@@ -63,13 +63,18 @@ class CORS:
|
|
|
63
63
|
"""
|
|
64
64
|
cors_headers = {}
|
|
65
65
|
origin = request.headers.get('Origin')
|
|
66
|
+
vary_origin = False
|
|
66
67
|
if self.allowed_origins == '*':
|
|
67
68
|
cors_headers['Access-Control-Allow-Origin'] = origin or '*'
|
|
68
69
|
if origin:
|
|
69
|
-
|
|
70
|
+
vary_origin = True
|
|
70
71
|
elif origin in (self.allowed_origins or []):
|
|
71
72
|
cors_headers['Access-Control-Allow-Origin'] = origin
|
|
72
|
-
|
|
73
|
+
vary_origin = True
|
|
74
|
+
if vary_origin:
|
|
75
|
+
if not hasattr(request.g, '_vary'):
|
|
76
|
+
request.g._vary = set()
|
|
77
|
+
request.g._vary.add('Origin')
|
|
73
78
|
if self.allow_credentials and \
|
|
74
79
|
'Access-Control-Allow-Origin' in cors_headers:
|
|
75
80
|
cors_headers['Access-Control-Allow-Credentials'] = 'true'
|
|
@@ -103,9 +108,5 @@ class CORS:
|
|
|
103
108
|
return cors_headers
|
|
104
109
|
|
|
105
110
|
def after_request(self, request, response):
|
|
106
|
-
saved_vary = response.headers.get('Vary')
|
|
107
111
|
if request: # pragma: no branch
|
|
108
112
|
response.headers.update(self.get_cors_headers(request))
|
|
109
|
-
if saved_vary and saved_vary != response.headers.get('Vary'):
|
|
110
|
-
response.headers['Vary'] = (
|
|
111
|
-
saved_vary + ', ' + response.headers['Vary'])
|
|
@@ -14,16 +14,16 @@ class CORS:
|
|
|
14
14
|
max_age: int | None
|
|
15
15
|
def __init__(self, app: Microdot | None = ..., allowed_origins: list[str] | Literal['*'] | None = ..., allow_credentials: bool = ..., allowed_methods: list[str] | None = ..., expose_headers: list[str] | None = ..., allowed_headers: list[str] | None = ..., max_age: int | None = ..., handle_cors: bool = ...) -> None:
|
|
16
16
|
...
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
default_options_handler: Callable[[Request], dict[str, str]]
|
|
19
19
|
def initialize(self, app: Microdot, handle_cors: bool = ...) -> None:
|
|
20
20
|
...
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
def options_handler(self, request: Request) -> dict[str, str]:
|
|
23
23
|
...
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
def get_cors_headers(self, request: Request) -> dict[str, str]:
|
|
26
26
|
...
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
def after_request(self, request: Request, response: Response) -> None:
|
|
29
29
|
...
|
|
@@ -64,6 +64,11 @@ class CSRF:
|
|
|
64
64
|
allow = True
|
|
65
65
|
elif sfs == 'same-site' and self.allow_subdomains:
|
|
66
66
|
allow = True
|
|
67
|
+
if allow:
|
|
68
|
+
if not hasattr(
|
|
69
|
+
request.g, '_vary'): # pragma: no branch
|
|
70
|
+
request.g._vary = set()
|
|
71
|
+
request.g._vary.add('Sec-Fetch-Site')
|
|
67
72
|
if not allow and origin and self.cors and \
|
|
68
73
|
self.cors.allowed_origins != '*':
|
|
69
74
|
# if we have a list of allowed origins, then we can
|
|
@@ -11,12 +11,12 @@ class CSRF:
|
|
|
11
11
|
protected_routes: list[Callable[..., Any | Awaitable[Any]]]
|
|
12
12
|
def __init__(self, app: Microdot | None = ..., cors: CORS | None = ..., protect_all: bool = ..., allow_subdomains: bool = ...) -> None:
|
|
13
13
|
...
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
def initialize(self, app: Microdot, cors: CORS | None = ...) -> None:
|
|
16
16
|
...
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
def exempt(self, f: Callable[..., Any | Awaitable[Any]]) -> Callable[..., Any | Awaitable[Any]]:
|
|
19
19
|
...
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
def protect(self, f: Callable[..., Any | Awaitable[Any]]) -> Callable[..., Any | Awaitable[Any]]:
|
|
22
22
|
...
|
|
@@ -6,20 +6,20 @@ class Template:
|
|
|
6
6
|
@classmethod
|
|
7
7
|
def initialize(cls, template_dir: str = ..., enable_async: bool = ..., **kwargs) -> None:
|
|
8
8
|
...
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
name: str
|
|
11
11
|
template: JinjaTemplate
|
|
12
12
|
def __init__(self, template: str, **kwargs: Any) -> None:
|
|
13
13
|
...
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
def generate(self, *args: Any, **kwargs: Any) -> Iterator[str]:
|
|
16
16
|
...
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
def render(self, *args: Any, **kwargs: Any) -> str:
|
|
19
19
|
...
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
def generate_async(self, *args: Any, **kwargs: Any) -> AsyncIterator[str]:
|
|
22
22
|
...
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
async def render_async(self, *args: Any, **kwargs: Any) -> Awaitable[str]:
|
|
25
25
|
...
|
|
@@ -6,21 +6,21 @@ class Login:
|
|
|
6
6
|
user_loader_callback: Callable[[str], Any | Awaitable[Any]]
|
|
7
7
|
def __init__(self, login_url: str = ...) -> None:
|
|
8
8
|
...
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
def user_loader(self, f: Callable[[str], Any | Awaitable[Any]]) -> None:
|
|
11
11
|
...
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
async def login_user(self, request: Request, user: Any, remember: bool = ..., redirect_url: str = ...) -> Response:
|
|
14
14
|
...
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
async def logout_user(self, request: Request) -> None:
|
|
17
17
|
...
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
async def get_current_user(self, request: Request):
|
|
20
20
|
...
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
def __call__(self, f: Callable[..., Any | Awaitable[Any]]) -> Callable[..., Any | Awaitable[Any]]:
|
|
23
23
|
...
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
def fresh(self, f: Callable[..., Any | Awaitable[Any]]) -> Callable[..., Any | Awaitable[Any]]:
|
|
26
26
|
...
|
|
@@ -613,7 +613,7 @@ class Response:
|
|
|
613
613
|
"""
|
|
614
614
|
if ';' in value:
|
|
615
615
|
raise ValueError('invalid cookie value')
|
|
616
|
-
http_cookie = '{cookie}={value}'
|
|
616
|
+
http_cookie = f'{cookie}={value}'
|
|
617
617
|
if path:
|
|
618
618
|
http_cookie += '; Path=' + path
|
|
619
619
|
if domain:
|
|
@@ -819,7 +819,7 @@ class Response:
|
|
|
819
819
|
if max_age is None:
|
|
820
820
|
max_age = cls.default_send_file_max_age
|
|
821
821
|
if max_age is not None:
|
|
822
|
-
headers['Cache-Control'] = 'max-age={}'
|
|
822
|
+
headers['Cache-Control'] = f'max-age={max_age}'
|
|
823
823
|
|
|
824
824
|
if compressed:
|
|
825
825
|
headers['Content-Encoding'] = compressed \
|
|
@@ -862,7 +862,7 @@ class URLPattern():
|
|
|
862
862
|
value of the segment. If omitted, the value is returned
|
|
863
863
|
as a string.
|
|
864
864
|
"""
|
|
865
|
-
cls.segment_patterns[type_name] = '/({})'
|
|
865
|
+
cls.segment_patterns[type_name] = f'/({pattern})'
|
|
866
866
|
cls.segment_parsers[type_name] = parser
|
|
867
867
|
|
|
868
868
|
def __init__(self, url_pattern):
|
|
@@ -889,7 +889,7 @@ class URLPattern():
|
|
|
889
889
|
name = segment
|
|
890
890
|
parser = None
|
|
891
891
|
if type_.startswith('re:'):
|
|
892
|
-
pattern += '/({
|
|
892
|
+
pattern += f'/({type_[3:]})'
|
|
893
893
|
else:
|
|
894
894
|
if type_ not in self.segment_patterns:
|
|
895
895
|
raise ValueError('invalid URL segment type')
|
|
@@ -927,7 +927,7 @@ class URLPattern():
|
|
|
927
927
|
return args
|
|
928
928
|
|
|
929
929
|
def __repr__(self): # pragma: no cover
|
|
930
|
-
return 'URLPattern: {
|
|
930
|
+
return f'URLPattern: {self.url_pattern}'
|
|
931
931
|
|
|
932
932
|
|
|
933
933
|
class HTTPException(Exception):
|
|
@@ -936,7 +936,7 @@ class HTTPException(Exception):
|
|
|
936
936
|
self.reason = reason or str(status_code) + ' error'
|
|
937
937
|
|
|
938
938
|
def __repr__(self): # pragma: no cover
|
|
939
|
-
return 'HTTPException: {
|
|
939
|
+
return f'HTTPException: {self.status_code}'
|
|
940
940
|
|
|
941
941
|
|
|
942
942
|
class Microdot:
|
|
@@ -1091,6 +1091,24 @@ class Microdot:
|
|
|
1091
1091
|
"""
|
|
1092
1092
|
return self.route(url_pattern, methods=['DELETE'])
|
|
1093
1093
|
|
|
1094
|
+
def query(self, url_pattern):
|
|
1095
|
+
"""Decorator that is used to register a function as a ``QUERY``
|
|
1096
|
+
request handler for a given URL.
|
|
1097
|
+
|
|
1098
|
+
:param url_pattern: The URL pattern that will be compared against
|
|
1099
|
+
incoming requests.
|
|
1100
|
+
|
|
1101
|
+
This decorator can be used as an alias to the ``route`` decorator with
|
|
1102
|
+
``methods=['QUERY']``.
|
|
1103
|
+
|
|
1104
|
+
Example::
|
|
1105
|
+
|
|
1106
|
+
@app.query('/users/<int:id>')
|
|
1107
|
+
def query_user(request, id):
|
|
1108
|
+
# ...
|
|
1109
|
+
"""
|
|
1110
|
+
return self.route(url_pattern, methods=['QUERY'])
|
|
1111
|
+
|
|
1094
1112
|
def before_request(self, f):
|
|
1095
1113
|
"""Decorator to register a function to run before each request is
|
|
1096
1114
|
handled. The decorated function must take a single argument, the
|
|
@@ -1560,6 +1578,14 @@ class Microdot:
|
|
|
1560
1578
|
res = await invoke_handler(
|
|
1561
1579
|
handler, req, res) or res
|
|
1562
1580
|
res.is_head = (req and req.method == 'HEAD')
|
|
1581
|
+
if req and hasattr(req.g, '_vary'):
|
|
1582
|
+
if res.headers.get('Vary'):
|
|
1583
|
+
vary = set(
|
|
1584
|
+
[h.strip() for h in res.headers['Vary'].split(',')])
|
|
1585
|
+
vary.update(req.g._vary)
|
|
1586
|
+
else:
|
|
1587
|
+
vary = req.g._vary
|
|
1588
|
+
res.headers['Vary'] = ', '.join(list(vary))
|
|
1563
1589
|
return res
|
|
1564
1590
|
|
|
1565
1591
|
|