microdot 2.5.1__tar.gz → 2.6.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {microdot-2.5.1/src/microdot.egg-info → microdot-2.6.1}/PKG-INFO +12 -4
- {microdot-2.5.1 → microdot-2.6.1}/README.md +11 -3
- {microdot-2.5.1 → microdot-2.6.1}/docs/implementation/migrating.rst +20 -23
- microdot-2.6.1/docs/implementation/type_hints.rst +16 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/index.rst +2 -3
- {microdot-2.5.1 → microdot-2.6.1}/docs/users-guide/concurrency.rst +4 -4
- {microdot-2.5.1 → microdot-2.6.1}/docs/users-guide/responses.rst +1 -1
- {microdot-2.5.1 → microdot-2.6.1}/pyproject.toml +1 -1
- {microdot-2.5.1 → microdot-2.6.1}/src/microdot/__init__.py +1 -1
- microdot-2.6.1/src/microdot/__init__.pyi +7 -0
- {microdot-2.5.1 → microdot-2.6.1}/src/microdot/asgi.py +1 -1
- microdot-2.6.1/src/microdot/asgi.pyi +50 -0
- microdot-2.6.1/src/microdot/auth.pyi +52 -0
- {microdot-2.5.1 → microdot-2.6.1}/src/microdot/cors.py +1 -1
- microdot-2.6.1/src/microdot/cors.pyi +29 -0
- microdot-2.6.1/src/microdot/csrf.pyi +22 -0
- {microdot-2.5.1 → microdot-2.6.1}/src/microdot/helpers.py +1 -1
- microdot-2.6.1/src/microdot/helpers.pyi +4 -0
- microdot-2.6.1/src/microdot/jinja.pyi +25 -0
- microdot-2.6.1/src/microdot/login.pyi +26 -0
- {microdot-2.5.1 → microdot-2.6.1}/src/microdot/microdot.py +27 -10
- microdot-2.6.1/src/microdot/microdot.pyi +307 -0
- microdot-2.6.1/src/microdot/multipart.pyi +43 -0
- microdot-2.6.1/src/microdot/py.typed +0 -0
- microdot-2.6.1/src/microdot/session.pyi +45 -0
- {microdot-2.5.1 → microdot-2.6.1}/src/microdot/sse.py +1 -1
- microdot-2.6.1/src/microdot/sse.pyi +20 -0
- {microdot-2.5.1 → microdot-2.6.1}/src/microdot/test_client.py +2 -2
- microdot-2.6.1/src/microdot/test_client.pyi +51 -0
- {microdot-2.5.1 → microdot-2.6.1}/src/microdot/utemplate.py +1 -1
- microdot-2.6.1/src/microdot/utemplate.pyi +23 -0
- microdot-2.6.1/src/microdot/websocket.pyi +42 -0
- {microdot-2.5.1 → microdot-2.6.1}/src/microdot/wsgi.py +1 -1
- microdot-2.6.1/src/microdot/wsgi.pyi +22 -0
- {microdot-2.5.1 → microdot-2.6.1/src/microdot.egg-info}/PKG-INFO +12 -4
- {microdot-2.5.1 → microdot-2.6.1}/src/microdot.egg-info/SOURCES.txt +18 -1
- {microdot-2.5.1 → microdot-2.6.1}/tests/test_end2end.py +63 -7
- {microdot-2.5.1 → microdot-2.6.1}/tests/test_login.py +4 -4
- {microdot-2.5.1 → microdot-2.6.1}/tests/test_response.py +7 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/test_session.py +8 -6
- {microdot-2.5.1 → microdot-2.6.1}/tox.ini +12 -13
- microdot-2.5.1/docs/contributing.rst +0 -7
- {microdot-2.5.1 → microdot-2.6.1}/LICENSE +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/MANIFEST.in +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/Makefile +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/_static/css/custom.css +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/api/asgi.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/api/auth.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/api/cors.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/api/csrf.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/api/index.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/api/jinja.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/api/login.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/api/microdot.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/api/multipart.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/api/sessions.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/api/sse.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/api/test_client.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/api/utemplate.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/api/websocket.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/api/wsgi.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/conf.py +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/extensions/auth.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/extensions/cors.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/extensions/csrf.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/extensions/index.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/extensions/login.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/extensions/multipart.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/extensions/production.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/extensions/sessions.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/extensions/sse.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/extensions/templates.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/extensions/test_client.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/extensions/websocket.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/implementation/freezing.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/implementation/index.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/intro.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/make.bat +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/users-guide/defining-routes.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/users-guide/index.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/users-guide/intro.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/docs/users-guide/request-object.rst +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/setup.cfg +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/src/microdot/auth.py +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/src/microdot/csrf.py +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/src/microdot/jinja.py +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/src/microdot/login.py +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/src/microdot/multipart.py +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/src/microdot/session.py +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/src/microdot/websocket.py +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/src/microdot.egg-info/dependency_links.txt +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/src/microdot.egg-info/not-zip-safe +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/src/microdot.egg-info/requires.txt +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/src/microdot.egg-info/top_level.txt +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/__init__.py +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/files/test.bin +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/files/test.css +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/files/test.gif +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/files/test.gz +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/files/test.html +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/files/test.jpg +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/files/test.js +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/files/test.json +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/files/test.png +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/files/test.txt +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/files/test.txt.gz +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/mock_socket.py +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/templates/hello.jinja.txt +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/templates/hello.utemplate.txt +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/templates/hello_utemplate_txt.py +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/test_asgi.py +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/test_auth.py +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/test_cors.py +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/test_csrf.py +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/test_jinja.py +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/test_microdot.py +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/test_multidict.py +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/test_multipart.py +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/test_request.py +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/test_sse.py +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/test_url_pattern.py +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/test_urlencode.py +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/test_utemplate.py +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/test_websocket.py +0 -0
- {microdot-2.5.1 → microdot-2.6.1}/tests/test_wsgi.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: microdot
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.6.1
|
|
4
4
|
Summary: The impossibly small web framework for MicroPython
|
|
5
5
|
Author-email: Miguel Grinberg <miguel.grinberg@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/miguelgrinberg/microdot
|
|
@@ -49,9 +49,16 @@ app.run()
|
|
|
49
49
|
- Documentation
|
|
50
50
|
- [Latest](https://microdot.readthedocs.io/en/latest/)
|
|
51
51
|
- [Stable (v2)](https://microdot.readthedocs.io/en/stable/)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
|
|
53
|
+
The following links are for version 1 of Microdot:
|
|
54
|
+
|
|
55
|
+
- [Code](https://github.com/miguelgrinberg/microdot/tree/v1)
|
|
56
|
+
- [Documentation](https://microdot.readthedocs.io/en/v1/)
|
|
57
|
+
|
|
58
|
+
Note that version 1 is no longer maintained, so you should consider migrating
|
|
59
|
+
to version 2. See the
|
|
60
|
+
[migration notes](https://microdot.readthedocs.io/en/latest/implementation/migrating.html)
|
|
61
|
+
for help.
|
|
55
62
|
|
|
56
63
|
## Roadmap
|
|
57
64
|
|
|
@@ -61,6 +68,7 @@ MicroPython and CPython:
|
|
|
61
68
|
- Authentication support, similar to [Flask-Login](https://github.com/maxcountryman/flask-login) for Flask (**Added in version 2.1**)
|
|
62
69
|
- Support for forms encoded in `multipart/form-data` format (**Added in version 2.2**)
|
|
63
70
|
- CSRF protection extension (**Added in version 2.5**)
|
|
71
|
+
- Type hints (**Added in version 2.6**)
|
|
64
72
|
- Pub/sub mini-framework for WebSocket and SSE
|
|
65
73
|
- OpenAPI integration, similar to [APIFairy](https://github.com/miguelgrinberg/apifairy) for Flask
|
|
66
74
|
|
|
@@ -25,9 +25,16 @@ app.run()
|
|
|
25
25
|
- Documentation
|
|
26
26
|
- [Latest](https://microdot.readthedocs.io/en/latest/)
|
|
27
27
|
- [Stable (v2)](https://microdot.readthedocs.io/en/stable/)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
|
|
29
|
+
The following links are for version 1 of Microdot:
|
|
30
|
+
|
|
31
|
+
- [Code](https://github.com/miguelgrinberg/microdot/tree/v1)
|
|
32
|
+
- [Documentation](https://microdot.readthedocs.io/en/v1/)
|
|
33
|
+
|
|
34
|
+
Note that version 1 is no longer maintained, so you should consider migrating
|
|
35
|
+
to version 2. See the
|
|
36
|
+
[migration notes](https://microdot.readthedocs.io/en/latest/implementation/migrating.html)
|
|
37
|
+
for help.
|
|
31
38
|
|
|
32
39
|
## Roadmap
|
|
33
40
|
|
|
@@ -37,6 +44,7 @@ MicroPython and CPython:
|
|
|
37
44
|
- Authentication support, similar to [Flask-Login](https://github.com/maxcountryman/flask-login) for Flask (**Added in version 2.1**)
|
|
38
45
|
- Support for forms encoded in `multipart/form-data` format (**Added in version 2.2**)
|
|
39
46
|
- CSRF protection extension (**Added in version 2.5**)
|
|
47
|
+
- Type hints (**Added in version 2.6**)
|
|
40
48
|
- Pub/sub mini-framework for WebSocket and SSE
|
|
41
49
|
- OpenAPI integration, similar to [APIFairy](https://github.com/miguelgrinberg/apifairy) for Flask
|
|
42
50
|
|
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
Migrating to Microdot 2.x from Older Releases
|
|
2
2
|
---------------------------------------------
|
|
3
3
|
|
|
4
|
-
Version 2 of Microdot
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
For this reason most applications built for earlier versions will need to be
|
|
9
|
-
updated to work correctly with Microdot 2. This section describes the backwards
|
|
10
|
-
incompatible changes that were made.
|
|
4
|
+
Version 2 of Microdot introduces backwards incompatible changes, so most
|
|
5
|
+
applications built for earlier versions will need to be updated to work
|
|
6
|
+
correctly with Microdot 2. This section describes the changes that were made.
|
|
11
7
|
|
|
12
8
|
Code reorganization
|
|
13
9
|
~~~~~~~~~~~~~~~~~~~
|
|
14
10
|
|
|
15
|
-
The Microdot source code has been moved into a ``microdot`` package,
|
|
11
|
+
The Microdot source code has been moved into a ``microdot`` package in v2,
|
|
16
12
|
eliminating the need for each extension to be named with a *microdot_* prefix.
|
|
17
13
|
|
|
18
14
|
As a result of this change, all extensions have been renamed to shorter names.
|
|
@@ -33,11 +29,11 @@ In earlier releases of Microdot the core web server was built on synchronous
|
|
|
33
29
|
Python, and asynchronous support was enabled with the asyncio extension.
|
|
34
30
|
|
|
35
31
|
Microdot 2 eliminates the synchronous web server, and implements the core
|
|
36
|
-
server logic directly with asyncio
|
|
37
|
-
|
|
32
|
+
server logic directly with asyncio. The asyncio extension does not exist
|
|
33
|
+
anymore.
|
|
38
34
|
|
|
39
|
-
Any applications built using the asyncio extension will need to update
|
|
40
|
-
imports from this::
|
|
35
|
+
Any applications built using the legacy asyncio extension will need to update
|
|
36
|
+
their imports from this::
|
|
41
37
|
|
|
42
38
|
from microdot_asyncio import Microdot
|
|
43
39
|
|
|
@@ -46,7 +42,7 @@ to this::
|
|
|
46
42
|
from microdot import Microdot
|
|
47
43
|
|
|
48
44
|
Applications that were built using the synchronous web server do not need to
|
|
49
|
-
change their imports, but will now
|
|
45
|
+
change their imports, but will now use an asynchronous web server. Review the
|
|
50
46
|
:ref:`Concurrency` section to learn about the potential issues when using
|
|
51
47
|
``def`` function handlers, and the benefits of transitioning to ``async def``
|
|
52
48
|
handlers.
|
|
@@ -64,9 +60,9 @@ extensions:
|
|
|
64
60
|
- *microdot_asgi_websocket.py*: the functionality in this extension is now
|
|
65
61
|
available in the ASGI extension.
|
|
66
62
|
- *microdot_ssl.py*: this extension was only used with the synchronous web
|
|
67
|
-
server, so it is not needed anymore.
|
|
63
|
+
server, so it is not needed anymore and has been removed.
|
|
68
64
|
- *microdot_websocket_alt.py*: this extension was only used with the
|
|
69
|
-
synchronous web server, so it is not needed anymore.
|
|
65
|
+
synchronous web server, so it is not needed anymore and has been removed.
|
|
70
66
|
|
|
71
67
|
No more ``render_template()`` function
|
|
72
68
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
@@ -87,8 +83,8 @@ As a result of this change, it is now possible to use asynchronous rendering::
|
|
|
87
83
|
|
|
88
84
|
return await Template('index.html').render_async(title='Home')
|
|
89
85
|
|
|
90
|
-
Also thanks to this redesign, the template can be streamed
|
|
91
|
-
|
|
86
|
+
Also thanks to this redesign, the template can be streamed, which saves memory
|
|
87
|
+
for larger templates::
|
|
92
88
|
|
|
93
89
|
return Template('index.html').generate(title='Home')
|
|
94
90
|
|
|
@@ -96,6 +92,8 @@ Streamed templates also have an asynchronous version::
|
|
|
96
92
|
|
|
97
93
|
return Template('index.html').generate_async(title='Home')
|
|
98
94
|
|
|
95
|
+
Note that templates that are streamed asynchronously do not use an ``await``.
|
|
96
|
+
|
|
99
97
|
Class-based user sessions
|
|
100
98
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
101
99
|
|
|
@@ -136,10 +134,9 @@ Applications using the WSGI extension continue to run under an asynchronous
|
|
|
136
134
|
loop and should try to use the recommended ``async def`` handlers, but can be
|
|
137
135
|
deployed with standard WSGI servers such as Gunicorn.
|
|
138
136
|
|
|
139
|
-
WebSocket support when using the WSGI extension is enabled when using a
|
|
140
|
-
compatible web server. At this time only Gunicorn is supported for WebSocket.
|
|
141
|
-
Given that WebSocket support is asynchronous, it would be better to switch to
|
|
142
|
-
the ASGI extension, which has full support for WebSocket as defined in the ASGI
|
|
143
|
-
specification.
|
|
144
|
-
|
|
145
137
|
As before, the WSGI extension is not available under MicroPython.
|
|
138
|
+
|
|
139
|
+
Given that Microdot version 2 is asynchronous, the WSGI extension should be
|
|
140
|
+
considered deprecated. Users of this extension are encouraged to migrate to the
|
|
141
|
+
ASGI extension, which works with compatible ASGI web servers such as Uvicorn
|
|
142
|
+
and Gunicorn.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Type Hints
|
|
2
|
+
~~~~~~~~~~
|
|
3
|
+
|
|
4
|
+
Since version 2.6.0, Microdot includes standard Python type hints for all
|
|
5
|
+
public classes, methods, attributes and functions. This allows IDEs such as
|
|
6
|
+
Visual Studio Code, PyCharm and Vim/Neovim to provide type information and
|
|
7
|
+
autocompletion functions as you type.
|
|
8
|
+
|
|
9
|
+
Type hints are provided in standard stub files with the *.pyi* extension.
|
|
10
|
+
Install Microdot with ``pip``, ``uv`` or your favorite installer to have these
|
|
11
|
+
added to your virtual environment.
|
|
12
|
+
|
|
13
|
+
.. note::
|
|
14
|
+
The *.pyi* stub files are only meant to be used on your computer, to help
|
|
15
|
+
your IDE provide better coding assistance while you type. It is not
|
|
16
|
+
necessary to copy these files to your MicroPython device.
|
|
@@ -9,8 +9,8 @@ Microdot
|
|
|
9
9
|
*"The impossibly small web framework for Python and MicroPython"*
|
|
10
10
|
|
|
11
11
|
Microdot is a minimalistic Python web framework inspired by
|
|
12
|
-
`Flask <https://flask.palletsprojects.com/>`_. Given its size, it can run
|
|
13
|
-
systems with limited resources such as microcontrollers. Both standard Python
|
|
12
|
+
`Flask <https://flask.palletsprojects.com/>`_. Given its small size, it can run
|
|
13
|
+
on systems with limited resources such as microcontrollers. Both standard Python
|
|
14
14
|
(CPython) and `MicroPython <https://micropython.org>`_ are supported.
|
|
15
15
|
|
|
16
16
|
.. toctree::
|
|
@@ -21,7 +21,6 @@ systems with limited resources such as microcontrollers. Both standard Python
|
|
|
21
21
|
extensions/index
|
|
22
22
|
implementation/index
|
|
23
23
|
api/index
|
|
24
|
-
contributing
|
|
25
24
|
|
|
26
25
|
* :ref:`genindex`
|
|
27
26
|
* :ref:`search`
|
|
@@ -8,7 +8,7 @@ that applications must be careful to prevent blocking in their handlers.
|
|
|
8
8
|
^^^^^^^^^^^^^^^^^^^^
|
|
9
9
|
|
|
10
10
|
The recommendation for route handlers in Microdot is to use asynchronous
|
|
11
|
-
functions, declared
|
|
11
|
+
functions, declared with ``async def``. Microdot executes these handler
|
|
12
12
|
functions as native asynchronous tasks. The standard considerations for writing
|
|
13
13
|
asynchronous code apply, and in particular blocking calls should be avoided to
|
|
14
14
|
ensure the application runs smoothly and is always responsive.
|
|
@@ -24,9 +24,9 @@ When running on CPython, Microdot executes synchronous handlers in a
|
|
|
24
24
|
`thread executor <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_in_executor>`_,
|
|
25
25
|
which uses a thread pool. The use of blocking or CPU intensive code in these
|
|
26
26
|
handlers does not have such a negative effect on the application, because
|
|
27
|
-
handlers
|
|
28
|
-
hand, the application will be affected by threading issues
|
|
29
|
-
by the Global Interpreter Lock.
|
|
27
|
+
the handlers run on a different threads than the one running the asynchronous
|
|
28
|
+
loop. On the other hand, the application will be affected by threading issues
|
|
29
|
+
such as those caused by the Global Interpreter Lock.
|
|
30
30
|
|
|
31
31
|
Under MicroPython the situation is different. Most microcontroller boards
|
|
32
32
|
do not have or have very limited threading support, so Microdot executes
|
|
@@ -196,5 +196,5 @@ Another option is to create a response object directly in the route function::
|
|
|
196
196
|
Standard cookies do not offer sufficient privacy and security controls, so
|
|
197
197
|
never store sensitive information in them unless you are adding additional
|
|
198
198
|
protection mechanisms such as encryption or cryptographic signing. The
|
|
199
|
-
:ref:`session <
|
|
199
|
+
:ref:`session <Secure User Sessions>` extension implements signed
|
|
200
200
|
cookies that prevent tampering by malicious actors.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This type stub file was generated by pyright.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from microdot.microdot import AsyncBytesIO as AsyncBytesIO, Microdot as Microdot, Request as Request, Response as Response, URLPattern as URLPattern, abort as abort, redirect as redirect, send_file as send_file
|
|
6
|
+
|
|
7
|
+
__version__: str
|
|
@@ -44,7 +44,7 @@ class _BodyStream: # pragma: no cover
|
|
|
44
44
|
return data
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
class Microdot(BaseMicrodot):
|
|
47
|
+
class Microdot(BaseMicrodot): # type: ignore[no-redef]
|
|
48
48
|
"""A subclass of the core :class:`Microdot <microdot.Microdot>` class that
|
|
49
49
|
implements the ASGI protocol.
|
|
50
50
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from typing import Any, Awaitable, Callable
|
|
2
|
+
from microdot import * # type: ignore
|
|
3
|
+
from microdot.microdot import Microdot as BaseMicrodot, Request
|
|
4
|
+
from microdot.websocket import WebSocket as BaseWebSocket
|
|
5
|
+
|
|
6
|
+
class Microdot(BaseMicrodot): # type: ignore[no-redef]
|
|
7
|
+
lifespan_startup: Callable[[dict[str, Any]], Awaitable[None]]
|
|
8
|
+
lifespan_shutdown: Callable[[dict[str, Any]], Awaitable[None]]
|
|
9
|
+
embedded_server: bool
|
|
10
|
+
def __init__(self, lifespan_startup: Callable[[dict[str, Any]], Awaitable[None]] = ..., lifespan_shutdown: Callable[[dict[str, Any]], Awaitable[None]] = ...) -> None:
|
|
11
|
+
...
|
|
12
|
+
|
|
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
|
+
...
|
|
15
|
+
|
|
16
|
+
async def asgi_app(self, scope: dict[str, Any], receive: Callable[[], Awaitable[dict[str, Any]]], send: Callable[[dict[str, Any]], Awaitable[None]]):
|
|
17
|
+
...
|
|
18
|
+
|
|
19
|
+
async def __call__(self, scope: dict[str, Any], receive: Callable[[], Awaitable[dict[str, Any]]], send: Callable[[dict[str, Any]], Awaitable[None]]):
|
|
20
|
+
...
|
|
21
|
+
|
|
22
|
+
def shutdown(self) -> None:
|
|
23
|
+
...
|
|
24
|
+
|
|
25
|
+
def run(self, host: str = ..., port: int = ..., debug: bool = ..., **options: Any) -> None: # type: ignore[override]
|
|
26
|
+
...
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class WebSocket(BaseWebSocket):
|
|
31
|
+
closed: bool
|
|
32
|
+
async def handshake(self) -> None:
|
|
33
|
+
...
|
|
34
|
+
|
|
35
|
+
async def receive(self) -> bytes | str:
|
|
36
|
+
...
|
|
37
|
+
|
|
38
|
+
async def send(self, data: bytes | str) -> None: # type: ignore[override]
|
|
39
|
+
...
|
|
40
|
+
|
|
41
|
+
async def close(self) -> None:
|
|
42
|
+
...
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
async def websocket_upgrade(request: Request) -> BaseWebSocket:
|
|
47
|
+
...
|
|
48
|
+
|
|
49
|
+
def with_websocket(f: Callable[[Request, BaseWebSocket], Awaitable[None]]) -> None:
|
|
50
|
+
...
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from typing import Any, Awaitable, Callable
|
|
2
|
+
from microdot import Request
|
|
3
|
+
|
|
4
|
+
class BaseAuth:
|
|
5
|
+
auth_callback: Callable[..., Any | Awaitable[Any]]
|
|
6
|
+
error_callback: Callable[[Request], Any | Awaitable[Any]]
|
|
7
|
+
def __init__(self) -> None:
|
|
8
|
+
...
|
|
9
|
+
|
|
10
|
+
def __call__(self, f: Callable[..., Any]):
|
|
11
|
+
...
|
|
12
|
+
|
|
13
|
+
def optional(self, f: Callable[..., Any]):
|
|
14
|
+
...
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class BasicAuth(BaseAuth):
|
|
19
|
+
realm: str
|
|
20
|
+
charset: str
|
|
21
|
+
scheme: str
|
|
22
|
+
error_status: int
|
|
23
|
+
error_callback: Callable[[Request], Any | Awaitable[Any]]
|
|
24
|
+
def __init__(self, realm: str = ..., charset: str = ..., scheme: str = ..., error_status: int = ...) -> None:
|
|
25
|
+
...
|
|
26
|
+
|
|
27
|
+
async def authentication_error(self, request):
|
|
28
|
+
...
|
|
29
|
+
|
|
30
|
+
auth_callback: Callable[[Request, str, str], Any | Awaitable[Any]]
|
|
31
|
+
def authenticate(self, f: Callable[[Request, str, str], Any | Awaitable[Any]]) -> None:
|
|
32
|
+
...
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class TokenAuth(BaseAuth):
|
|
37
|
+
header: str
|
|
38
|
+
scheme: str
|
|
39
|
+
error_status: int
|
|
40
|
+
error_callback: Callable[[Request], Any | Awaitable[Any]]
|
|
41
|
+
def __init__(self, header: str = ..., scheme: str = ..., error_status: int = ...) -> None:
|
|
42
|
+
...
|
|
43
|
+
|
|
44
|
+
auth_callback: Callable[[Request, str], Any | Awaitable[Any]]
|
|
45
|
+
def authenticate(self, f: Callable[..., Any | Awaitable[Any]]) -> None:
|
|
46
|
+
...
|
|
47
|
+
|
|
48
|
+
def errorhandler(self, f: Callable[[Request], Any | Awaitable[Any]]) -> None:
|
|
49
|
+
...
|
|
50
|
+
|
|
51
|
+
async def authentication_error(self, request) -> None:
|
|
52
|
+
...
|
|
@@ -14,7 +14,7 @@ class CORS:
|
|
|
14
14
|
making cross-site requests. If not set, all methods
|
|
15
15
|
are allowed.
|
|
16
16
|
:param expose_headers: A list of headers that the browser is allowed to
|
|
17
|
-
|
|
17
|
+
expose.
|
|
18
18
|
:param allowed_headers: A list of headers that are allowed to be used when
|
|
19
19
|
making cross-site requests. If not set, all headers
|
|
20
20
|
are allowed.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This type stub file was generated by pyright.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Callable, Literal
|
|
6
|
+
from microdot import Microdot, Request, Response
|
|
7
|
+
|
|
8
|
+
class CORS:
|
|
9
|
+
allowed_origins: list[str] | Literal['*'] | None
|
|
10
|
+
allow_credentials: bool
|
|
11
|
+
allowed_methods: list[str] | None
|
|
12
|
+
expose_headers: list[str] | None
|
|
13
|
+
allowed_headers: list[str] | None
|
|
14
|
+
max_age: int | None
|
|
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
|
+
...
|
|
17
|
+
|
|
18
|
+
default_options_handler: Callable[[Request], dict[str, str]]
|
|
19
|
+
def initialize(self, app: Microdot, handle_cors: bool = ...) -> None:
|
|
20
|
+
...
|
|
21
|
+
|
|
22
|
+
def options_handler(self, request: Request) -> dict[str, str]:
|
|
23
|
+
...
|
|
24
|
+
|
|
25
|
+
def get_cors_headers(self, request: Request) -> dict[str, str]:
|
|
26
|
+
...
|
|
27
|
+
|
|
28
|
+
def after_request(self, request: Request, response: Response) -> None:
|
|
29
|
+
...
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from typing import Any, Awaitable, Callable
|
|
2
|
+
from microdot import Microdot
|
|
3
|
+
from microdot.cors import CORS
|
|
4
|
+
|
|
5
|
+
class CSRF:
|
|
6
|
+
SAFE_METHODS: list[str]
|
|
7
|
+
cors: CORS | None
|
|
8
|
+
protect_all: bool
|
|
9
|
+
allow_subdomains: bool
|
|
10
|
+
exempt_routes: list[Callable[..., Any | Awaitable[Any]]]
|
|
11
|
+
protected_routes: list[Callable[..., Any | Awaitable[Any]]]
|
|
12
|
+
def __init__(self, app: Microdot | None = ..., cors: CORS | None = ..., protect_all: bool = ..., allow_subdomains: bool = ...) -> None:
|
|
13
|
+
...
|
|
14
|
+
|
|
15
|
+
def initialize(self, app: Microdot, cors: CORS | None = ...) -> None:
|
|
16
|
+
...
|
|
17
|
+
|
|
18
|
+
def exempt(self, f: Callable[..., Any | Awaitable[Any]]) -> Callable[..., Any | Awaitable[Any]]:
|
|
19
|
+
...
|
|
20
|
+
|
|
21
|
+
def protect(self, f: Callable[..., Any | Awaitable[Any]]) -> Callable[..., Any | Awaitable[Any]]:
|
|
22
|
+
...
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from typing import Any, AsyncIterator, Awaitable, Iterator
|
|
2
|
+
from jinja2 import Environment, Template as JinjaTemplate
|
|
3
|
+
|
|
4
|
+
class Template:
|
|
5
|
+
jinja_env: Environment
|
|
6
|
+
@classmethod
|
|
7
|
+
def initialize(cls, template_dir: str = ..., enable_async: bool = ..., **kwargs) -> None:
|
|
8
|
+
...
|
|
9
|
+
|
|
10
|
+
name: str
|
|
11
|
+
template: JinjaTemplate
|
|
12
|
+
def __init__(self, template: str, **kwargs: Any) -> None:
|
|
13
|
+
...
|
|
14
|
+
|
|
15
|
+
def generate(self, *args: Any, **kwargs: Any) -> Iterator[str]:
|
|
16
|
+
...
|
|
17
|
+
|
|
18
|
+
def render(self, *args: Any, **kwargs: Any) -> str:
|
|
19
|
+
...
|
|
20
|
+
|
|
21
|
+
def generate_async(self, *args: Any, **kwargs: Any) -> AsyncIterator[str]:
|
|
22
|
+
...
|
|
23
|
+
|
|
24
|
+
async def render_async(self, *args: Any, **kwargs: Any) -> Awaitable[str]:
|
|
25
|
+
...
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from typing import Any, Awaitable, Callable
|
|
2
|
+
from microdot import Request, Response
|
|
3
|
+
|
|
4
|
+
class Login:
|
|
5
|
+
login_url: str
|
|
6
|
+
user_loader_callback: Callable[[str], Any | Awaitable[Any]]
|
|
7
|
+
def __init__(self, login_url: str = ...) -> None:
|
|
8
|
+
...
|
|
9
|
+
|
|
10
|
+
def user_loader(self, f: Callable[[str], Any | Awaitable[Any]]) -> None:
|
|
11
|
+
...
|
|
12
|
+
|
|
13
|
+
async def login_user(self, request: Request, user: Any, remember: bool = ..., redirect_url: str = ...) -> Response:
|
|
14
|
+
...
|
|
15
|
+
|
|
16
|
+
async def logout_user(self, request: Request) -> None:
|
|
17
|
+
...
|
|
18
|
+
|
|
19
|
+
async def get_current_user(self, request: Request):
|
|
20
|
+
...
|
|
21
|
+
|
|
22
|
+
def __call__(self, f: Callable[..., Any | Awaitable[Any]]) -> Callable[..., Any | Awaitable[Any]]:
|
|
23
|
+
...
|
|
24
|
+
|
|
25
|
+
def fresh(self, f: Callable[..., Any | Awaitable[Any]]) -> Callable[..., Any | Awaitable[Any]]:
|
|
26
|
+
...
|
|
@@ -11,7 +11,7 @@ import re
|
|
|
11
11
|
import time
|
|
12
12
|
|
|
13
13
|
try:
|
|
14
|
-
import orjson as json
|
|
14
|
+
import orjson as json # type: ignore[import-not-found]
|
|
15
15
|
except ImportError:
|
|
16
16
|
import json
|
|
17
17
|
|
|
@@ -31,7 +31,7 @@ try:
|
|
|
31
31
|
None, partial(handler, *args, **kwargs))
|
|
32
32
|
return ret
|
|
33
33
|
except ImportError: # pragma: no cover
|
|
34
|
-
def iscoroutine(coro):
|
|
34
|
+
def iscoroutine(coro): # type: ignore[misc]
|
|
35
35
|
return hasattr(coro, 'send') and hasattr(coro, 'throw')
|
|
36
36
|
|
|
37
37
|
async def invoke_handler(handler, *args, **kwargs):
|
|
@@ -46,7 +46,7 @@ except ImportError: # pragma: no cover
|
|
|
46
46
|
return ret
|
|
47
47
|
|
|
48
48
|
try:
|
|
49
|
-
from sys import print_exception
|
|
49
|
+
from sys import print_exception # type: ignore[attr-defined]
|
|
50
50
|
except ImportError: # pragma: no cover
|
|
51
51
|
import traceback
|
|
52
52
|
|
|
@@ -578,7 +578,7 @@ class Response:
|
|
|
578
578
|
#: written to the client. Used to exit WebSocket connections cleanly.
|
|
579
579
|
already_handled = None
|
|
580
580
|
|
|
581
|
-
def __init__(self, body='', status_code=200, headers=None, reason=None):
|
|
581
|
+
def __init__(self, body=b'', status_code=200, headers=None, reason=None):
|
|
582
582
|
if body is None and status_code == 200:
|
|
583
583
|
body = ''
|
|
584
584
|
status_code = 204
|
|
@@ -630,6 +630,8 @@ class Response:
|
|
|
630
630
|
http_cookie += '; HttpOnly'
|
|
631
631
|
if partitioned:
|
|
632
632
|
http_cookie += '; Partitioned'
|
|
633
|
+
if '\r' in http_cookie or '\n' in http_cookie:
|
|
634
|
+
raise ValueError('invalid cookie')
|
|
633
635
|
if 'Set-Cookie' in self.headers:
|
|
634
636
|
self.headers['Set-Cookie'].append(http_cookie)
|
|
635
637
|
else:
|
|
@@ -762,7 +764,7 @@ class Response:
|
|
|
762
764
|
:param status_code: The 3xx status code to use for the redirect. The
|
|
763
765
|
default is 302.
|
|
764
766
|
"""
|
|
765
|
-
if '\
|
|
767
|
+
if '\r' in location or '\n' in location:
|
|
766
768
|
raise ValueError('invalid redirect URL')
|
|
767
769
|
return cls(status_code=status_code, headers={'Location': location})
|
|
768
770
|
|
|
@@ -1212,7 +1214,7 @@ class Microdot:
|
|
|
1212
1214
|
raise HTTPException(status_code, reason)
|
|
1213
1215
|
|
|
1214
1216
|
async def start_server(self, host='0.0.0.0', port=5000, debug=False,
|
|
1215
|
-
ssl=None):
|
|
1217
|
+
ssl=None, start_serving=True):
|
|
1216
1218
|
"""Start the Microdot web server as a coroutine. This coroutine does
|
|
1217
1219
|
not normally return, as the server enters an endless listening loop.
|
|
1218
1220
|
The :func:`shutdown` function provides a method for terminating the
|
|
@@ -1231,6 +1233,13 @@ class Microdot:
|
|
|
1231
1233
|
default is ``False``.
|
|
1232
1234
|
:param ssl: An ``SSLContext`` instance or ``None`` if the server should
|
|
1233
1235
|
not use TLS. The default is ``None``.
|
|
1236
|
+
:param start_serving: If ``True``, the server starts accepting
|
|
1237
|
+
connections immediately. When set to ``False``,
|
|
1238
|
+
this method returns a ``Server`` object. To
|
|
1239
|
+
accept connections, the
|
|
1240
|
+
``Server.serve_forever()`` method should be
|
|
1241
|
+
called. The default is ``True``. A value of
|
|
1242
|
+
``False`` is only supported in CPython.
|
|
1234
1243
|
|
|
1235
1244
|
This method is a coroutine.
|
|
1236
1245
|
|
|
@@ -1275,10 +1284,18 @@ class Microdot:
|
|
|
1275
1284
|
host=host, port=port))
|
|
1276
1285
|
|
|
1277
1286
|
try:
|
|
1278
|
-
self.server = await asyncio.start_server(
|
|
1279
|
-
|
|
1287
|
+
self.server = await asyncio.start_server(
|
|
1288
|
+
serve, host, port, ssl=ssl, start_serving=start_serving)
|
|
1289
|
+
if not start_serving:
|
|
1290
|
+
return self.server
|
|
1280
1291
|
except TypeError: # pragma: no cover
|
|
1281
|
-
|
|
1292
|
+
if not start_serving:
|
|
1293
|
+
raise ValueError('start_serving must be True')
|
|
1294
|
+
try:
|
|
1295
|
+
self.server = await asyncio.start_server(serve, host, port,
|
|
1296
|
+
ssl=ssl)
|
|
1297
|
+
except TypeError: # pragma: no cover
|
|
1298
|
+
self.server = await asyncio.start_server(serve, host, port)
|
|
1282
1299
|
|
|
1283
1300
|
while True:
|
|
1284
1301
|
try:
|
|
@@ -1544,7 +1561,7 @@ class Microdot:
|
|
|
1544
1561
|
return res
|
|
1545
1562
|
|
|
1546
1563
|
|
|
1547
|
-
Response.already_handled = Response()
|
|
1564
|
+
Response.already_handled = Response() # type: ignore[assignment]
|
|
1548
1565
|
|
|
1549
1566
|
abort = Microdot.abort
|
|
1550
1567
|
redirect = Response.redirect
|