microdot 2.6.0__tar.gz → 2.6.2__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.0/src/microdot.egg-info → microdot-2.6.2}/PKG-INFO +1 -1
- {microdot-2.6.0 → microdot-2.6.2}/docs/index.rst +2 -2
- {microdot-2.6.0 → microdot-2.6.2}/docs/users-guide/concurrency.rst +4 -4
- {microdot-2.6.0 → microdot-2.6.2}/docs/users-guide/responses.rst +1 -1
- {microdot-2.6.0 → microdot-2.6.2}/pyproject.toml +1 -1
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/__init__.py +1 -1
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/microdot.py +5 -1
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/multipart.py +2 -1
- {microdot-2.6.0 → microdot-2.6.2/src/microdot.egg-info}/PKG-INFO +1 -1
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot.egg-info/SOURCES.txt +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot.egg-info/dependency_links.txt +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot.egg-info/not-zip-safe +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot.egg-info/requires.txt +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot.egg-info/top_level.txt +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/test_multipart.py +14 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/test_response.py +9 -0
- {microdot-2.6.0 → microdot-2.6.2}/LICENSE +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/MANIFEST.in +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/README.md +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/Makefile +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/_static/css/custom.css +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/api/asgi.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/api/auth.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/api/cors.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/api/csrf.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/api/index.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/api/jinja.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/api/login.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/api/microdot.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/api/multipart.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/api/sessions.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/api/sse.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/api/test_client.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/api/utemplate.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/api/websocket.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/api/wsgi.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/conf.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/extensions/auth.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/extensions/cors.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/extensions/csrf.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/extensions/index.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/extensions/login.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/extensions/multipart.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/extensions/production.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/extensions/sessions.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/extensions/sse.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/extensions/templates.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/extensions/test_client.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/extensions/websocket.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/implementation/freezing.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/implementation/index.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/implementation/migrating.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/intro.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/make.bat +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/users-guide/defining-routes.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/users-guide/index.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/users-guide/intro.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/docs/users-guide/request-object.rst +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/setup.cfg +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/__init__.pyi +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/asgi.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/asgi.pyi +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/auth.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/auth.pyi +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/cors.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/cors.pyi +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/csrf.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/csrf.pyi +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/helpers.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/helpers.pyi +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/jinja.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/jinja.pyi +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/login.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/login.pyi +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/microdot.pyi +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/multipart.pyi +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/py.typed +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/session.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/session.pyi +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/sse.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/sse.pyi +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/test_client.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/test_client.pyi +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/utemplate.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/utemplate.pyi +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/websocket.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/websocket.pyi +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/wsgi.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/src/microdot/wsgi.pyi +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/__init__.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/files/test.bin +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/files/test.css +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/files/test.gif +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/files/test.gz +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/files/test.html +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/files/test.jpg +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/files/test.js +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/files/test.json +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/files/test.png +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/files/test.txt +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/files/test.txt.gz +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/mock_socket.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/templates/hello.jinja.txt +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/templates/hello.utemplate.txt +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/templates/hello_utemplate_txt.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/test_asgi.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/test_auth.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/test_cors.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/test_csrf.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/test_end2end.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/test_jinja.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/test_login.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/test_microdot.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/test_multidict.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/test_request.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/test_session.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/test_sse.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/test_url_pattern.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/test_urlencode.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/test_utemplate.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/test_websocket.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tests/test_wsgi.py +0 -0
- {microdot-2.6.0 → microdot-2.6.2}/tox.ini +0 -0
|
@@ -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::
|
|
@@ -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.
|
|
@@ -611,6 +611,8 @@ class Response:
|
|
|
611
611
|
:param http_only: The cookie's ``HttpOnly`` flag.
|
|
612
612
|
:param partitioned: Whether the cookie is partitioned.
|
|
613
613
|
"""
|
|
614
|
+
if ';' in value:
|
|
615
|
+
raise ValueError('invalid cookie value')
|
|
614
616
|
http_cookie = '{cookie}={value}'.format(cookie=cookie, value=value)
|
|
615
617
|
if path:
|
|
616
618
|
http_cookie += '; Path=' + path
|
|
@@ -630,6 +632,8 @@ class Response:
|
|
|
630
632
|
http_cookie += '; HttpOnly'
|
|
631
633
|
if partitioned:
|
|
632
634
|
http_cookie += '; Partitioned'
|
|
635
|
+
if '\r' in http_cookie or '\n' in http_cookie:
|
|
636
|
+
raise ValueError('invalid cookie')
|
|
633
637
|
if 'Set-Cookie' in self.headers:
|
|
634
638
|
self.headers['Set-Cookie'].append(http_cookie)
|
|
635
639
|
else:
|
|
@@ -762,7 +766,7 @@ class Response:
|
|
|
762
766
|
:param status_code: The 3xx status code to use for the redirect. The
|
|
763
767
|
default is 302.
|
|
764
768
|
"""
|
|
765
|
-
if '\
|
|
769
|
+
if '\r' in location or '\n' in location:
|
|
766
770
|
raise ValueError('invalid redirect URL')
|
|
767
771
|
return cls(status_code=status_code, headers={'Location': location})
|
|
768
772
|
|
|
@@ -40,8 +40,9 @@ class FormDataIter:
|
|
|
40
40
|
def __init__(self, request):
|
|
41
41
|
self.request = request
|
|
42
42
|
self.buffer = None
|
|
43
|
+
content_type = request.content_type or ''
|
|
43
44
|
try:
|
|
44
|
-
mimetype, boundary =
|
|
45
|
+
mimetype, boundary = content_type.rsplit('; boundary=', 1)
|
|
45
46
|
except ValueError:
|
|
46
47
|
return # not a multipart request
|
|
47
48
|
if mimetype.split(';', 1)[0] == \
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -172,6 +172,20 @@ class TestMultipart(unittest.TestCase):
|
|
|
172
172
|
self.assertEqual(res.status_code, 200)
|
|
173
173
|
self.assertEqual(res.text, 'None')
|
|
174
174
|
|
|
175
|
+
def test_no_content_type(self):
|
|
176
|
+
app = Microdot()
|
|
177
|
+
|
|
178
|
+
@app.route('/async', methods=['GET', 'POST'])
|
|
179
|
+
@with_form_data
|
|
180
|
+
async def async_route(req):
|
|
181
|
+
return str(req.form)
|
|
182
|
+
|
|
183
|
+
client = TestClient(app)
|
|
184
|
+
|
|
185
|
+
res = self._run(client.get('/async'))
|
|
186
|
+
self.assertEqual(res.status_code, 200)
|
|
187
|
+
self.assertEqual(res.text, 'None')
|
|
188
|
+
|
|
175
189
|
def test_upload_iterator(self):
|
|
176
190
|
app = Microdot()
|
|
177
191
|
|
|
@@ -194,6 +194,15 @@ class TestResponse(unittest.TestCase):
|
|
|
194
194
|
secure=True, http_only=True)
|
|
195
195
|
res.delete_cookie('foo8', http_only=True)
|
|
196
196
|
res.delete_cookie('foo9', path='/s')
|
|
197
|
+
with self.assertRaises(ValueError):
|
|
198
|
+
res.set_cookie('foo1', 'bar1\r\nSet-Cookie: foo2=bar2')
|
|
199
|
+
with self.assertRaises(ValueError):
|
|
200
|
+
res.set_cookie('foo1', 'bar1',
|
|
201
|
+
domain='example.com\r\nSet-Cookie: foo2=bar2')
|
|
202
|
+
with self.assertRaises(ValueError):
|
|
203
|
+
res.set_cookie('foo1', 'bar1', path='/\r\nSet-Cookie: foo2=bar2')
|
|
204
|
+
with self.assertRaises(ValueError):
|
|
205
|
+
res.set_cookie('foo10', 'x;y', path='/')
|
|
197
206
|
self.assertEqual(res.headers, {'Set-Cookie': [
|
|
198
207
|
'foo1=bar1',
|
|
199
208
|
'foo2=bar2; Path=/; Partitioned',
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|