microdot 2.6.1__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.1/src/microdot.egg-info → microdot-2.7.0}/PKG-INFO +31 -11
- {microdot-2.6.1 → microdot-2.7.0}/README.md +24 -8
- {microdot-2.6.1 → microdot-2.7.0}/pyproject.toml +7 -3
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/__init__.py +1 -1
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/asgi.pyi +10 -10
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/auth.pyi +9 -9
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/cors.py +7 -6
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/cors.pyi +4 -4
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/csrf.py +5 -0
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/csrf.pyi +3 -3
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/jinja.pyi +5 -5
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/login.pyi +6 -6
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/microdot.py +34 -6
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/microdot.pyi +66 -63
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/multipart.py +2 -1
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/multipart.pyi +8 -8
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/session.py +3 -0
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/session.pyi +10 -11
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/sse.pyi +2 -2
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/test_client.py +15 -1
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/test_client.pyi +15 -12
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/utemplate.pyi +5 -5
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/websocket.pyi +5 -5
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/wsgi.pyi +4 -4
- {microdot-2.6.1 → microdot-2.7.0/src/microdot.egg-info}/PKG-INFO +31 -11
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot.egg-info/SOURCES.txt +0 -1
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot.egg-info/dependency_links.txt +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot.egg-info/not-zip-safe +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot.egg-info/requires.txt +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot.egg-info/top_level.txt +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/test_cors.py +7 -1
- {microdot-2.6.1 → microdot-2.7.0}/tests/test_csrf.py +6 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/test_microdot.py +70 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/test_multipart.py +14 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/test_response.py +2 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/test_session.py +20 -0
- {microdot-2.6.1 → microdot-2.7.0}/tox.ini +26 -3
- microdot-2.6.1/docs/implementation/type_hints.rst +0 -16
- {microdot-2.6.1 → microdot-2.7.0}/LICENSE +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/MANIFEST.in +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/Makefile +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/_static/css/custom.css +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/api/asgi.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/api/auth.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/api/cors.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/api/csrf.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/api/index.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/api/jinja.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/api/login.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/api/microdot.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/api/multipart.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/api/sessions.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/api/sse.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/api/test_client.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/api/utemplate.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/api/websocket.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/api/wsgi.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/conf.py +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/extensions/auth.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/extensions/cors.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/extensions/csrf.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/extensions/index.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/extensions/login.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/extensions/multipart.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/extensions/production.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/extensions/sessions.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/extensions/sse.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/extensions/templates.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/extensions/test_client.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/extensions/websocket.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/implementation/freezing.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/implementation/index.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/implementation/migrating.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/index.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/intro.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/make.bat +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/users-guide/concurrency.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/users-guide/defining-routes.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/users-guide/index.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/users-guide/intro.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/users-guide/request-object.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/docs/users-guide/responses.rst +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/setup.cfg +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/__init__.pyi +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/asgi.py +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/auth.py +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/helpers.py +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/helpers.pyi +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/jinja.py +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/login.py +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/py.typed +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/sse.py +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/utemplate.py +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/websocket.py +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/src/microdot/wsgi.py +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/__init__.py +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/files/test.bin +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/files/test.css +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/files/test.gif +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/files/test.gz +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/files/test.html +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/files/test.jpg +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/files/test.js +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/files/test.json +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/files/test.png +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/files/test.txt +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/files/test.txt.gz +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/mock_socket.py +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/templates/hello.jinja.txt +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/templates/hello.utemplate.txt +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/templates/hello_utemplate_txt.py +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/test_asgi.py +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/test_auth.py +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/test_end2end.py +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/test_jinja.py +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/test_login.py +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/test_multidict.py +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/test_request.py +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/test_sse.py +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/test_url_pattern.py +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/test_urlencode.py +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/test_utemplate.py +0 -0
- {microdot-2.6.1 → microdot-2.7.0}/tests/test_websocket.py +0 -0
- {microdot-2.6.1 → 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
|
...
|
|
@@ -611,7 +611,9 @@ class Response:
|
|
|
611
611
|
:param http_only: The cookie's ``HttpOnly`` flag.
|
|
612
612
|
:param partitioned: Whether the cookie is partitioned.
|
|
613
613
|
"""
|
|
614
|
-
|
|
614
|
+
if ';' in value:
|
|
615
|
+
raise ValueError('invalid cookie value')
|
|
616
|
+
http_cookie = f'{cookie}={value}'
|
|
615
617
|
if path:
|
|
616
618
|
http_cookie += '; Path=' + path
|
|
617
619
|
if domain:
|
|
@@ -817,7 +819,7 @@ class Response:
|
|
|
817
819
|
if max_age is None:
|
|
818
820
|
max_age = cls.default_send_file_max_age
|
|
819
821
|
if max_age is not None:
|
|
820
|
-
headers['Cache-Control'] = 'max-age={}'
|
|
822
|
+
headers['Cache-Control'] = f'max-age={max_age}'
|
|
821
823
|
|
|
822
824
|
if compressed:
|
|
823
825
|
headers['Content-Encoding'] = compressed \
|
|
@@ -860,7 +862,7 @@ class URLPattern():
|
|
|
860
862
|
value of the segment. If omitted, the value is returned
|
|
861
863
|
as a string.
|
|
862
864
|
"""
|
|
863
|
-
cls.segment_patterns[type_name] = '/({})'
|
|
865
|
+
cls.segment_patterns[type_name] = f'/({pattern})'
|
|
864
866
|
cls.segment_parsers[type_name] = parser
|
|
865
867
|
|
|
866
868
|
def __init__(self, url_pattern):
|
|
@@ -887,7 +889,7 @@ class URLPattern():
|
|
|
887
889
|
name = segment
|
|
888
890
|
parser = None
|
|
889
891
|
if type_.startswith('re:'):
|
|
890
|
-
pattern += '/({
|
|
892
|
+
pattern += f'/({type_[3:]})'
|
|
891
893
|
else:
|
|
892
894
|
if type_ not in self.segment_patterns:
|
|
893
895
|
raise ValueError('invalid URL segment type')
|
|
@@ -925,7 +927,7 @@ class URLPattern():
|
|
|
925
927
|
return args
|
|
926
928
|
|
|
927
929
|
def __repr__(self): # pragma: no cover
|
|
928
|
-
return 'URLPattern: {
|
|
930
|
+
return f'URLPattern: {self.url_pattern}'
|
|
929
931
|
|
|
930
932
|
|
|
931
933
|
class HTTPException(Exception):
|
|
@@ -934,7 +936,7 @@ class HTTPException(Exception):
|
|
|
934
936
|
self.reason = reason or str(status_code) + ' error'
|
|
935
937
|
|
|
936
938
|
def __repr__(self): # pragma: no cover
|
|
937
|
-
return 'HTTPException: {
|
|
939
|
+
return f'HTTPException: {self.status_code}'
|
|
938
940
|
|
|
939
941
|
|
|
940
942
|
class Microdot:
|
|
@@ -1089,6 +1091,24 @@ class Microdot:
|
|
|
1089
1091
|
"""
|
|
1090
1092
|
return self.route(url_pattern, methods=['DELETE'])
|
|
1091
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
|
+
|
|
1092
1112
|
def before_request(self, f):
|
|
1093
1113
|
"""Decorator to register a function to run before each request is
|
|
1094
1114
|
handled. The decorated function must take a single argument, the
|
|
@@ -1558,6 +1578,14 @@ class Microdot:
|
|
|
1558
1578
|
res = await invoke_handler(
|
|
1559
1579
|
handler, req, res) or res
|
|
1560
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))
|
|
1561
1589
|
return res
|
|
1562
1590
|
|
|
1563
1591
|
|