microdot 2.5.0__tar.gz → 2.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. {microdot-2.5.0/src/microdot.egg-info → microdot-2.6.0}/PKG-INFO +13 -5
  2. {microdot-2.5.0 → microdot-2.6.0}/README.md +12 -4
  3. {microdot-2.5.0 → microdot-2.6.0}/docs/implementation/migrating.rst +20 -23
  4. {microdot-2.5.0 → microdot-2.6.0}/docs/index.rst +0 -1
  5. {microdot-2.5.0 → microdot-2.6.0}/pyproject.toml +1 -1
  6. {microdot-2.5.0 → microdot-2.6.0}/src/microdot/__init__.py +1 -1
  7. microdot-2.6.0/src/microdot/__init__.pyi +7 -0
  8. {microdot-2.5.0 → microdot-2.6.0}/src/microdot/asgi.py +1 -1
  9. microdot-2.6.0/src/microdot/asgi.pyi +50 -0
  10. microdot-2.6.0/src/microdot/auth.pyi +52 -0
  11. {microdot-2.5.0 → microdot-2.6.0}/src/microdot/cors.py +1 -1
  12. microdot-2.6.0/src/microdot/cors.pyi +29 -0
  13. {microdot-2.5.0 → microdot-2.6.0}/src/microdot/csrf.py +7 -9
  14. microdot-2.6.0/src/microdot/csrf.pyi +22 -0
  15. {microdot-2.5.0 → microdot-2.6.0}/src/microdot/helpers.py +1 -1
  16. microdot-2.6.0/src/microdot/helpers.pyi +4 -0
  17. microdot-2.6.0/src/microdot/jinja.pyi +25 -0
  18. microdot-2.6.0/src/microdot/login.pyi +26 -0
  19. {microdot-2.5.0 → microdot-2.6.0}/src/microdot/microdot.py +24 -9
  20. microdot-2.6.0/src/microdot/microdot.pyi +307 -0
  21. microdot-2.6.0/src/microdot/multipart.pyi +43 -0
  22. microdot-2.6.0/src/microdot/py.typed +0 -0
  23. microdot-2.6.0/src/microdot/session.pyi +45 -0
  24. {microdot-2.5.0 → microdot-2.6.0}/src/microdot/sse.py +1 -1
  25. microdot-2.6.0/src/microdot/sse.pyi +20 -0
  26. {microdot-2.5.0 → microdot-2.6.0}/src/microdot/test_client.py +2 -2
  27. microdot-2.6.0/src/microdot/test_client.pyi +51 -0
  28. {microdot-2.5.0 → microdot-2.6.0}/src/microdot/utemplate.py +1 -1
  29. microdot-2.6.0/src/microdot/utemplate.pyi +23 -0
  30. microdot-2.6.0/src/microdot/websocket.pyi +42 -0
  31. {microdot-2.5.0 → microdot-2.6.0}/src/microdot/wsgi.py +1 -1
  32. microdot-2.6.0/src/microdot/wsgi.pyi +22 -0
  33. {microdot-2.5.0 → microdot-2.6.0/src/microdot.egg-info}/PKG-INFO +13 -5
  34. {microdot-2.5.0 → microdot-2.6.0}/src/microdot.egg-info/SOURCES.txt +17 -1
  35. {microdot-2.5.0 → microdot-2.6.0}/tests/test_csrf.py +20 -8
  36. {microdot-2.5.0 → microdot-2.6.0}/tests/test_end2end.py +63 -7
  37. {microdot-2.5.0 → microdot-2.6.0}/tests/test_login.py +4 -4
  38. {microdot-2.5.0 → microdot-2.6.0}/tests/test_session.py +8 -6
  39. {microdot-2.5.0 → microdot-2.6.0}/tox.ini +12 -13
  40. microdot-2.5.0/docs/contributing.rst +0 -7
  41. {microdot-2.5.0 → microdot-2.6.0}/LICENSE +0 -0
  42. {microdot-2.5.0 → microdot-2.6.0}/MANIFEST.in +0 -0
  43. {microdot-2.5.0 → microdot-2.6.0}/docs/Makefile +0 -0
  44. {microdot-2.5.0 → microdot-2.6.0}/docs/_static/css/custom.css +0 -0
  45. {microdot-2.5.0 → microdot-2.6.0}/docs/api/asgi.rst +0 -0
  46. {microdot-2.5.0 → microdot-2.6.0}/docs/api/auth.rst +0 -0
  47. {microdot-2.5.0 → microdot-2.6.0}/docs/api/cors.rst +0 -0
  48. {microdot-2.5.0 → microdot-2.6.0}/docs/api/csrf.rst +0 -0
  49. {microdot-2.5.0 → microdot-2.6.0}/docs/api/index.rst +0 -0
  50. {microdot-2.5.0 → microdot-2.6.0}/docs/api/jinja.rst +0 -0
  51. {microdot-2.5.0 → microdot-2.6.0}/docs/api/login.rst +0 -0
  52. {microdot-2.5.0 → microdot-2.6.0}/docs/api/microdot.rst +0 -0
  53. {microdot-2.5.0 → microdot-2.6.0}/docs/api/multipart.rst +0 -0
  54. {microdot-2.5.0 → microdot-2.6.0}/docs/api/sessions.rst +0 -0
  55. {microdot-2.5.0 → microdot-2.6.0}/docs/api/sse.rst +0 -0
  56. {microdot-2.5.0 → microdot-2.6.0}/docs/api/test_client.rst +0 -0
  57. {microdot-2.5.0 → microdot-2.6.0}/docs/api/utemplate.rst +0 -0
  58. {microdot-2.5.0 → microdot-2.6.0}/docs/api/websocket.rst +0 -0
  59. {microdot-2.5.0 → microdot-2.6.0}/docs/api/wsgi.rst +0 -0
  60. {microdot-2.5.0 → microdot-2.6.0}/docs/conf.py +0 -0
  61. {microdot-2.5.0 → microdot-2.6.0}/docs/extensions/auth.rst +0 -0
  62. {microdot-2.5.0 → microdot-2.6.0}/docs/extensions/cors.rst +0 -0
  63. {microdot-2.5.0 → microdot-2.6.0}/docs/extensions/csrf.rst +0 -0
  64. {microdot-2.5.0 → microdot-2.6.0}/docs/extensions/index.rst +0 -0
  65. {microdot-2.5.0 → microdot-2.6.0}/docs/extensions/login.rst +0 -0
  66. {microdot-2.5.0 → microdot-2.6.0}/docs/extensions/multipart.rst +0 -0
  67. {microdot-2.5.0 → microdot-2.6.0}/docs/extensions/production.rst +0 -0
  68. {microdot-2.5.0 → microdot-2.6.0}/docs/extensions/sessions.rst +0 -0
  69. {microdot-2.5.0 → microdot-2.6.0}/docs/extensions/sse.rst +0 -0
  70. {microdot-2.5.0 → microdot-2.6.0}/docs/extensions/templates.rst +0 -0
  71. {microdot-2.5.0 → microdot-2.6.0}/docs/extensions/test_client.rst +0 -0
  72. {microdot-2.5.0 → microdot-2.6.0}/docs/extensions/websocket.rst +0 -0
  73. {microdot-2.5.0 → microdot-2.6.0}/docs/implementation/freezing.rst +0 -0
  74. {microdot-2.5.0 → microdot-2.6.0}/docs/implementation/index.rst +0 -0
  75. {microdot-2.5.0 → microdot-2.6.0}/docs/intro.rst +0 -0
  76. {microdot-2.5.0 → microdot-2.6.0}/docs/make.bat +0 -0
  77. {microdot-2.5.0 → microdot-2.6.0}/docs/users-guide/concurrency.rst +0 -0
  78. {microdot-2.5.0 → microdot-2.6.0}/docs/users-guide/defining-routes.rst +0 -0
  79. {microdot-2.5.0 → microdot-2.6.0}/docs/users-guide/index.rst +0 -0
  80. {microdot-2.5.0 → microdot-2.6.0}/docs/users-guide/intro.rst +0 -0
  81. {microdot-2.5.0 → microdot-2.6.0}/docs/users-guide/request-object.rst +0 -0
  82. {microdot-2.5.0 → microdot-2.6.0}/docs/users-guide/responses.rst +0 -0
  83. {microdot-2.5.0 → microdot-2.6.0}/setup.cfg +0 -0
  84. {microdot-2.5.0 → microdot-2.6.0}/src/microdot/auth.py +0 -0
  85. {microdot-2.5.0 → microdot-2.6.0}/src/microdot/jinja.py +0 -0
  86. {microdot-2.5.0 → microdot-2.6.0}/src/microdot/login.py +0 -0
  87. {microdot-2.5.0 → microdot-2.6.0}/src/microdot/multipart.py +0 -0
  88. {microdot-2.5.0 → microdot-2.6.0}/src/microdot/session.py +0 -0
  89. {microdot-2.5.0 → microdot-2.6.0}/src/microdot/websocket.py +0 -0
  90. {microdot-2.5.0 → microdot-2.6.0}/src/microdot.egg-info/dependency_links.txt +0 -0
  91. {microdot-2.5.0 → microdot-2.6.0}/src/microdot.egg-info/not-zip-safe +0 -0
  92. {microdot-2.5.0 → microdot-2.6.0}/src/microdot.egg-info/requires.txt +0 -0
  93. {microdot-2.5.0 → microdot-2.6.0}/src/microdot.egg-info/top_level.txt +0 -0
  94. {microdot-2.5.0 → microdot-2.6.0}/tests/__init__.py +0 -0
  95. {microdot-2.5.0 → microdot-2.6.0}/tests/files/test.bin +0 -0
  96. {microdot-2.5.0 → microdot-2.6.0}/tests/files/test.css +0 -0
  97. {microdot-2.5.0 → microdot-2.6.0}/tests/files/test.gif +0 -0
  98. {microdot-2.5.0 → microdot-2.6.0}/tests/files/test.gz +0 -0
  99. {microdot-2.5.0 → microdot-2.6.0}/tests/files/test.html +0 -0
  100. {microdot-2.5.0 → microdot-2.6.0}/tests/files/test.jpg +0 -0
  101. {microdot-2.5.0 → microdot-2.6.0}/tests/files/test.js +0 -0
  102. {microdot-2.5.0 → microdot-2.6.0}/tests/files/test.json +0 -0
  103. {microdot-2.5.0 → microdot-2.6.0}/tests/files/test.png +0 -0
  104. {microdot-2.5.0 → microdot-2.6.0}/tests/files/test.txt +0 -0
  105. {microdot-2.5.0 → microdot-2.6.0}/tests/files/test.txt.gz +0 -0
  106. {microdot-2.5.0 → microdot-2.6.0}/tests/mock_socket.py +0 -0
  107. {microdot-2.5.0 → microdot-2.6.0}/tests/templates/hello.jinja.txt +0 -0
  108. {microdot-2.5.0 → microdot-2.6.0}/tests/templates/hello.utemplate.txt +0 -0
  109. {microdot-2.5.0 → microdot-2.6.0}/tests/templates/hello_utemplate_txt.py +0 -0
  110. {microdot-2.5.0 → microdot-2.6.0}/tests/test_asgi.py +0 -0
  111. {microdot-2.5.0 → microdot-2.6.0}/tests/test_auth.py +0 -0
  112. {microdot-2.5.0 → microdot-2.6.0}/tests/test_cors.py +0 -0
  113. {microdot-2.5.0 → microdot-2.6.0}/tests/test_jinja.py +0 -0
  114. {microdot-2.5.0 → microdot-2.6.0}/tests/test_microdot.py +0 -0
  115. {microdot-2.5.0 → microdot-2.6.0}/tests/test_multidict.py +0 -0
  116. {microdot-2.5.0 → microdot-2.6.0}/tests/test_multipart.py +0 -0
  117. {microdot-2.5.0 → microdot-2.6.0}/tests/test_request.py +0 -0
  118. {microdot-2.5.0 → microdot-2.6.0}/tests/test_response.py +0 -0
  119. {microdot-2.5.0 → microdot-2.6.0}/tests/test_sse.py +0 -0
  120. {microdot-2.5.0 → microdot-2.6.0}/tests/test_url_pattern.py +0 -0
  121. {microdot-2.5.0 → microdot-2.6.0}/tests/test_urlencode.py +0 -0
  122. {microdot-2.5.0 → microdot-2.6.0}/tests/test_utemplate.py +0 -0
  123. {microdot-2.5.0 → microdot-2.6.0}/tests/test_websocket.py +0 -0
  124. {microdot-2.5.0 → microdot-2.6.0}/tests/test_wsgi.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: microdot
3
- Version: 2.5.0
3
+ Version: 2.6.0
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
- - Legacy (v1)
53
- - [Code](https://github.com/miguelgrinberg/microdot/tree/v1)
54
- - [Documentation](https://microdot.readthedocs.io/en/v1/)
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
 
@@ -60,7 +67,8 @@ MicroPython and CPython:
60
67
 
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
- - CSRF protection extension
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
- - Legacy (v1)
29
- - [Code](https://github.com/miguelgrinberg/microdot/tree/v1)
30
- - [Documentation](https://microdot.readthedocs.io/en/v1/)
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
 
@@ -36,7 +43,8 @@ MicroPython and CPython:
36
43
 
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
- - CSRF protection extension
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 incorporates feedback received from users of earlier
5
- releases, and attempts to improve and correct some design decisions that have
6
- proven to be problematic.
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, eliminating the need for an asyncio
37
- extension.
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 their
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 work asynchronously. Review the
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 instead of returned
91
- as a single string::
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.
@@ -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`
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "microdot"
3
- version = "2.5.0"
3
+ version = "2.6.0"
4
4
  authors = [
5
5
  { name = "Miguel Grinberg", email = "miguel.grinberg@gmail.com" },
6
6
  ]
@@ -1,4 +1,4 @@
1
1
  from microdot.microdot import Microdot, Request, Response, abort, redirect, \
2
2
  send_file, URLPattern, AsyncBytesIO, iscoroutine # noqa: F401
3
3
 
4
- __version__ = '2.5.0'
4
+ __version__ = '2.6.0'
@@ -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
- exposed.
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
+ ...
@@ -56,6 +56,7 @@ class CSRF:
56
56
  ) or request.route in self.protected_routes:
57
57
  allow = False
58
58
  sfs = request.headers.get('Sec-Fetch-Site')
59
+ origin = request.headers.get('Origin')
59
60
  if sfs:
60
61
  # if the Sec-Fetch-Site header was given, ensure it is not
61
62
  # cross-site
@@ -63,14 +64,11 @@ class CSRF:
63
64
  allow = True
64
65
  elif sfs == 'same-site' and self.allow_subdomains:
65
66
  allow = True
66
- elif self.cors and self.cors.allowed_origins != '*':
67
- # if there is no Sec-Fetch-Site header but we have a list
68
- # of allowed origins, then we can validate the origin
69
- origin = request.headers.get('Origin')
70
- if origin is None:
71
- # origin wasn't given so this isn't a browser
72
- allow = True
73
- elif not self.allow_subdomains:
67
+ if not allow and origin and self.cors and \
68
+ self.cors.allowed_origins != '*':
69
+ # if we have a list of allowed origins, then we can
70
+ # validate the origin
71
+ if not self.allow_subdomains:
74
72
  allow = origin in self.cors.allowed_origins
75
73
  else:
76
74
  origin_scheme, origin_host = origin.split('://', 1)
@@ -83,7 +81,7 @@ class CSRF:
83
81
  ):
84
82
  allow = True
85
83
  break
86
- else:
84
+ if not allow and not sfs and not origin:
87
85
  allow = True # no headers to check
88
86
 
89
87
  if not allow:
@@ -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
+ ...
@@ -2,7 +2,7 @@ try:
2
2
  from functools import wraps
3
3
  except ImportError: # pragma: no cover
4
4
  # MicroPython does not currently implement functools.wraps
5
- def wraps(wrapped):
5
+ def wraps(wrapped): # type: ignore[misc]
6
6
  def _(wrapper):
7
7
  return wrapper
8
8
  return _
@@ -0,0 +1,4 @@
1
+ """
2
+ This type stub file was generated by pyright.
3
+ """
4
+
@@ -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
@@ -1212,7 +1212,7 @@ class Microdot:
1212
1212
  raise HTTPException(status_code, reason)
1213
1213
 
1214
1214
  async def start_server(self, host='0.0.0.0', port=5000, debug=False,
1215
- ssl=None):
1215
+ ssl=None, start_serving=True):
1216
1216
  """Start the Microdot web server as a coroutine. This coroutine does
1217
1217
  not normally return, as the server enters an endless listening loop.
1218
1218
  The :func:`shutdown` function provides a method for terminating the
@@ -1231,6 +1231,13 @@ class Microdot:
1231
1231
  default is ``False``.
1232
1232
  :param ssl: An ``SSLContext`` instance or ``None`` if the server should
1233
1233
  not use TLS. The default is ``None``.
1234
+ :param start_serving: If ``True``, the server starts accepting
1235
+ connections immediately. When set to ``False``,
1236
+ this method returns a ``Server`` object. To
1237
+ accept connections, the
1238
+ ``Server.serve_forever()`` method should be
1239
+ called. The default is ``True``. A value of
1240
+ ``False`` is only supported in CPython.
1234
1241
 
1235
1242
  This method is a coroutine.
1236
1243
 
@@ -1275,10 +1282,18 @@ class Microdot:
1275
1282
  host=host, port=port))
1276
1283
 
1277
1284
  try:
1278
- self.server = await asyncio.start_server(serve, host, port,
1279
- ssl=ssl)
1285
+ self.server = await asyncio.start_server(
1286
+ serve, host, port, ssl=ssl, start_serving=start_serving)
1287
+ if not start_serving:
1288
+ return self.server
1280
1289
  except TypeError: # pragma: no cover
1281
- self.server = await asyncio.start_server(serve, host, port)
1290
+ if not start_serving:
1291
+ raise ValueError('start_serving must be True')
1292
+ try:
1293
+ self.server = await asyncio.start_server(serve, host, port,
1294
+ ssl=ssl)
1295
+ except TypeError: # pragma: no cover
1296
+ self.server = await asyncio.start_server(serve, host, port)
1282
1297
 
1283
1298
  while True:
1284
1299
  try:
@@ -1544,7 +1559,7 @@ class Microdot:
1544
1559
  return res
1545
1560
 
1546
1561
 
1547
- Response.already_handled = Response()
1562
+ Response.already_handled = Response() # type: ignore[assignment]
1548
1563
 
1549
1564
  abort = Microdot.abort
1550
1565
  redirect = Response.redirect