microdot 2.6.2__tar.gz → 2.8.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.8.0}/PKG-INFO +32 -12
- {microdot-2.6.2 → microdot-2.8.0}/README.md +24 -8
- {microdot-2.6.2 → microdot-2.8.0}/pyproject.toml +8 -4
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/__init__.py +1 -1
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/asgi.pyi +10 -10
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/auth.pyi +9 -9
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/cors.py +7 -6
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/cors.pyi +4 -4
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/csrf.py +5 -0
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/csrf.pyi +3 -3
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/jinja.pyi +5 -5
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/login.pyi +6 -6
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/microdot.py +35 -8
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/microdot.pyi +78 -72
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/multipart.py +6 -4
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/multipart.pyi +8 -8
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/session.py +3 -0
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/session.pyi +10 -11
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/sse.pyi +2 -2
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/test_client.py +15 -1
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/test_client.pyi +15 -12
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/utemplate.pyi +5 -5
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/websocket.pyi +5 -5
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/wsgi.pyi +4 -4
- {microdot-2.6.2 → microdot-2.8.0/src/microdot.egg-info}/PKG-INFO +32 -12
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot.egg-info/SOURCES.txt +1 -0
- microdot-2.8.0/tests/.test_multipart.py.swp +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/test_cors.py +7 -1
- {microdot-2.6.2 → microdot-2.8.0}/tests/test_csrf.py +6 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/test_microdot.py +70 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/test_multipart.py +69 -11
- {microdot-2.6.2 → microdot-2.8.0}/tests/test_session.py +20 -0
- {microdot-2.6.2 → microdot-2.8.0}/tox.ini +26 -3
- {microdot-2.6.2 → microdot-2.8.0}/LICENSE +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/MANIFEST.in +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/Makefile +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/_static/css/custom.css +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/api/asgi.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/api/auth.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/api/cors.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/api/csrf.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/api/index.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/api/jinja.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/api/login.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/api/microdot.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/api/multipart.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/api/sessions.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/api/sse.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/api/test_client.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/api/utemplate.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/api/websocket.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/api/wsgi.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/conf.py +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/extensions/auth.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/extensions/cors.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/extensions/csrf.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/extensions/index.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/extensions/login.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/extensions/multipart.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/extensions/production.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/extensions/sessions.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/extensions/sse.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/extensions/templates.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/extensions/test_client.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/extensions/websocket.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/implementation/freezing.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/implementation/index.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/implementation/migrating.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/index.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/intro.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/make.bat +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/users-guide/concurrency.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/users-guide/defining-routes.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/users-guide/index.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/users-guide/intro.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/users-guide/request-object.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/docs/users-guide/responses.rst +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/setup.cfg +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/__init__.pyi +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/asgi.py +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/auth.py +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/helpers.py +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/helpers.pyi +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/jinja.py +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/login.py +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/py.typed +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/sse.py +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/utemplate.py +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/websocket.py +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot/wsgi.py +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot.egg-info/dependency_links.txt +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot.egg-info/not-zip-safe +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot.egg-info/requires.txt +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/src/microdot.egg-info/top_level.txt +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/__init__.py +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/files/test.bin +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/files/test.css +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/files/test.gif +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/files/test.gz +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/files/test.html +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/files/test.jpg +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/files/test.js +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/files/test.json +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/files/test.png +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/files/test.txt +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/files/test.txt.gz +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/mock_socket.py +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/templates/hello.jinja.txt +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/templates/hello.utemplate.txt +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/templates/hello_utemplate_txt.py +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/test_asgi.py +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/test_auth.py +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/test_end2end.py +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/test_jinja.py +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/test_login.py +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/test_multidict.py +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/test_request.py +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/test_response.py +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/test_sse.py +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/test_url_pattern.py +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/test_urlencode.py +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/test_utemplate.py +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/test_websocket.py +0 -0
- {microdot-2.6.2 → microdot-2.8.0}/tests/test_wsgi.py +0 -0
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: microdot
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.8.0
|
|
4
4
|
Summary: The impossibly small web framework for MicroPython
|
|
5
5
|
Author-email: Miguel Grinberg <miguel.grinberg@gmail.com>
|
|
6
|
-
|
|
7
|
-
Project-URL:
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://code.miguelgrinberg.com/miguelgrinberg/microdot
|
|
8
|
+
Project-URL: Documentation, https://microdot.readthedocs.io/
|
|
9
|
+
Project-URL: Source Code, https://code.miguelgrinberg.com/miguelgrinberg/microdot
|
|
10
|
+
Project-URL: Issue Tracker, https://code.miguelgrinberg.com/miguelgrinberg/microdot/issues
|
|
11
|
+
Project-URL: Changelog, https://code.miguelgrinberg.com/miguelgrinberg/microdot/src/branch/main/CHANGES.md
|
|
12
|
+
Project-URL: Funding, https://code.miguelgrinberg.com/miguelgrinberg/microdot#sponsor-this-project
|
|
8
13
|
Classifier: Environment :: Web Environment
|
|
9
14
|
Classifier: Intended Audience :: Developers
|
|
10
15
|
Classifier: Programming Language :: Python :: 3
|
|
11
16
|
Classifier: Programming Language :: Python :: Implementation :: MicroPython
|
|
12
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
13
17
|
Classifier: Operating System :: OS Independent
|
|
14
18
|
Requires-Python: >=3.8
|
|
15
19
|
Description-Content-Type: text/markdown
|
|
@@ -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.8.0"
|
|
4
4
|
authors = [
|
|
5
5
|
{ name = "Miguel Grinberg", email = "miguel.grinberg@gmail.com" },
|
|
6
6
|
]
|
|
@@ -10,9 +10,9 @@ classifiers = [
|
|
|
10
10
|
"Intended Audience :: Developers",
|
|
11
11
|
"Programming Language :: Python :: 3",
|
|
12
12
|
"Programming Language :: Python :: Implementation :: MicroPython",
|
|
13
|
-
"License :: OSI Approved :: MIT License",
|
|
14
13
|
"Operating System :: OS Independent",
|
|
15
14
|
]
|
|
15
|
+
license = "MIT"
|
|
16
16
|
requires-python = ">=3.8"
|
|
17
17
|
dependencies = [
|
|
18
18
|
]
|
|
@@ -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
|
...
|
|
@@ -495,8 +495,9 @@ class Request:
|
|
|
495
495
|
|
|
496
496
|
@property
|
|
497
497
|
def files(self):
|
|
498
|
-
"""The files uploaded in the request as a
|
|
499
|
-
|
|
498
|
+
"""The files uploaded in the request as a
|
|
499
|
+
:class:`MultiDict <microdot.MultiDict>` object, or ``None`` if the
|
|
500
|
+
request does not have any files.
|
|
500
501
|
|
|
501
502
|
The :func:`with_form_data <microdot.multipart.with_form_data>`
|
|
502
503
|
decorator must be added to the route that receives file uploads for
|
|
@@ -613,7 +614,7 @@ class Response:
|
|
|
613
614
|
"""
|
|
614
615
|
if ';' in value:
|
|
615
616
|
raise ValueError('invalid cookie value')
|
|
616
|
-
http_cookie = '{cookie}={value}'
|
|
617
|
+
http_cookie = f'{cookie}={value}'
|
|
617
618
|
if path:
|
|
618
619
|
http_cookie += '; Path=' + path
|
|
619
620
|
if domain:
|
|
@@ -819,7 +820,7 @@ class Response:
|
|
|
819
820
|
if max_age is None:
|
|
820
821
|
max_age = cls.default_send_file_max_age
|
|
821
822
|
if max_age is not None:
|
|
822
|
-
headers['Cache-Control'] = 'max-age={}'
|
|
823
|
+
headers['Cache-Control'] = f'max-age={max_age}'
|
|
823
824
|
|
|
824
825
|
if compressed:
|
|
825
826
|
headers['Content-Encoding'] = compressed \
|
|
@@ -862,7 +863,7 @@ class URLPattern():
|
|
|
862
863
|
value of the segment. If omitted, the value is returned
|
|
863
864
|
as a string.
|
|
864
865
|
"""
|
|
865
|
-
cls.segment_patterns[type_name] = '/({})'
|
|
866
|
+
cls.segment_patterns[type_name] = f'/({pattern})'
|
|
866
867
|
cls.segment_parsers[type_name] = parser
|
|
867
868
|
|
|
868
869
|
def __init__(self, url_pattern):
|
|
@@ -889,7 +890,7 @@ class URLPattern():
|
|
|
889
890
|
name = segment
|
|
890
891
|
parser = None
|
|
891
892
|
if type_.startswith('re:'):
|
|
892
|
-
pattern += '/({
|
|
893
|
+
pattern += f'/({type_[3:]})'
|
|
893
894
|
else:
|
|
894
895
|
if type_ not in self.segment_patterns:
|
|
895
896
|
raise ValueError('invalid URL segment type')
|
|
@@ -927,7 +928,7 @@ class URLPattern():
|
|
|
927
928
|
return args
|
|
928
929
|
|
|
929
930
|
def __repr__(self): # pragma: no cover
|
|
930
|
-
return 'URLPattern: {
|
|
931
|
+
return f'URLPattern: {self.url_pattern}'
|
|
931
932
|
|
|
932
933
|
|
|
933
934
|
class HTTPException(Exception):
|
|
@@ -936,7 +937,7 @@ class HTTPException(Exception):
|
|
|
936
937
|
self.reason = reason or str(status_code) + ' error'
|
|
937
938
|
|
|
938
939
|
def __repr__(self): # pragma: no cover
|
|
939
|
-
return 'HTTPException: {
|
|
940
|
+
return f'HTTPException: {self.status_code}'
|
|
940
941
|
|
|
941
942
|
|
|
942
943
|
class Microdot:
|
|
@@ -1091,6 +1092,24 @@ class Microdot:
|
|
|
1091
1092
|
"""
|
|
1092
1093
|
return self.route(url_pattern, methods=['DELETE'])
|
|
1093
1094
|
|
|
1095
|
+
def query(self, url_pattern):
|
|
1096
|
+
"""Decorator that is used to register a function as a ``QUERY``
|
|
1097
|
+
request handler for a given URL.
|
|
1098
|
+
|
|
1099
|
+
:param url_pattern: The URL pattern that will be compared against
|
|
1100
|
+
incoming requests.
|
|
1101
|
+
|
|
1102
|
+
This decorator can be used as an alias to the ``route`` decorator with
|
|
1103
|
+
``methods=['QUERY']``.
|
|
1104
|
+
|
|
1105
|
+
Example::
|
|
1106
|
+
|
|
1107
|
+
@app.query('/users/<int:id>')
|
|
1108
|
+
def query_user(request, id):
|
|
1109
|
+
# ...
|
|
1110
|
+
"""
|
|
1111
|
+
return self.route(url_pattern, methods=['QUERY'])
|
|
1112
|
+
|
|
1094
1113
|
def before_request(self, f):
|
|
1095
1114
|
"""Decorator to register a function to run before each request is
|
|
1096
1115
|
handled. The decorated function must take a single argument, the
|
|
@@ -1560,6 +1579,14 @@ class Microdot:
|
|
|
1560
1579
|
res = await invoke_handler(
|
|
1561
1580
|
handler, req, res) or res
|
|
1562
1581
|
res.is_head = (req and req.method == 'HEAD')
|
|
1582
|
+
if req and hasattr(req.g, '_vary'):
|
|
1583
|
+
if res.headers.get('Vary'):
|
|
1584
|
+
vary = set(
|
|
1585
|
+
[h.strip() for h in res.headers['Vary'].split(',')])
|
|
1586
|
+
vary.update(req.g._vary)
|
|
1587
|
+
else:
|
|
1588
|
+
vary = req.g._vary
|
|
1589
|
+
res.headers['Vary'] = ', '.join(list(vary))
|
|
1563
1590
|
return res
|
|
1564
1591
|
|
|
1565
1592
|
|