Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a329d98a8 | ||
|
|
93411c6a9f | ||
|
|
5550b20cdd | ||
|
|
d8d2667053 | ||
|
|
3943a69374 | ||
|
|
a2f6985d01 | ||
|
|
4238aa4cd4 | ||
|
|
744548f8dc | ||
|
|
d46d2950c8 | ||
|
|
2e4911d108 | ||
|
|
3eb57d0fcf | ||
|
|
42406cef42 | ||
|
|
e09e9830f4 | ||
|
|
304ca2ef68 | ||
|
|
d99df2c401 | ||
|
|
3554bc91cb | ||
|
|
51f910087a | ||
|
|
e0f0565551 | ||
|
|
2a6e76c685 | ||
|
|
42c88b6b20 | ||
|
|
c07a539435 | ||
|
|
e92310fa55 | ||
|
|
9b9b7aa76d | ||
|
|
696f2e3e18 | ||
|
|
87c47ccefc | ||
|
|
a0dd7c8ab6 | ||
|
|
a80841f464 | ||
|
|
f81de6d958 | ||
|
|
efec9f14be | ||
|
|
239cf4ff37 | ||
|
|
87cd098f66 | ||
|
|
bb75e15b2d | ||
|
|
b7ad02eaf1 |
27
.github/workflows/tests.yml
vendored
27
.github/workflows/tests.yml
vendored
@@ -11,8 +11,8 @@ jobs:
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v3
|
||||
- run: python -m pip install --upgrade pip wheel
|
||||
- run: pip install tox tox-gh-actions
|
||||
- run: tox -eflake8
|
||||
@@ -25,8 +25,8 @@ jobs:
|
||||
fail-fast: false
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
- run: python -m pip install --upgrade pip wheel
|
||||
@@ -36,8 +36,8 @@ jobs:
|
||||
name: tests-micropython
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v3
|
||||
- run: python -m pip install --upgrade pip wheel
|
||||
- run: pip install tox tox-gh-actions
|
||||
- run: tox -eupy
|
||||
@@ -45,18 +45,21 @@ jobs:
|
||||
name: coverage
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v3
|
||||
- run: python -m pip install --upgrade pip wheel
|
||||
- run: pip install tox tox-gh-actions codecov
|
||||
- run: pip install tox tox-gh-actions
|
||||
- run: tox
|
||||
- run: codecov
|
||||
- uses: codecov/codecov-action@v3
|
||||
with:
|
||||
files: ./coverage.xml
|
||||
fail_ci_if_error: true
|
||||
benchmark:
|
||||
name: benchmark
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v3
|
||||
- run: python -m pip install --upgrade pip wheel
|
||||
- run: pip install tox tox-gh-actions
|
||||
- run: tox -ebenchmark
|
||||
|
||||
16
.readthedocs.yaml
Normal file
16
.readthedocs.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
version: 2
|
||||
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
tools:
|
||||
python: "3.11"
|
||||
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
|
||||
python:
|
||||
install:
|
||||
- method: pip
|
||||
path: .
|
||||
extra_requirements:
|
||||
- docs
|
||||
27
CHANGES.md
27
CHANGES.md
@@ -1,5 +1,32 @@
|
||||
# Microdot change log
|
||||
|
||||
**Release 1.3.4** - 2023-11-08
|
||||
|
||||
- Handle change in `wait_closed()` behavior in Python 3.12 [#177](https://github.com/miguelgrinberg/microdot/issues/177) ([commit](https://github.com/miguelgrinberg/microdot/commit/5550b20cdd347d59e2aa68f6ebf9e9abffaff9fc))
|
||||
- Added missing request argument in some documentation examples [#163](https://github.com/miguelgrinberg/microdot/issues/163) ([commit](https://github.com/miguelgrinberg/microdot/commit/744548f8dc33a72512b34c4001ee9c6c1edd22ee))
|
||||
- Fix minor documentation typos [#161](https://github.com/miguelgrinberg/microdot/issues/161) ([commit](https://github.com/miguelgrinberg/microdot/commit/2e4911d10826cbb3914de4a45e495c3be36543fa)) (thanks **Andy Piper**!)
|
||||
|
||||
**Release 1.3.3** - 2023-07-16
|
||||
|
||||
- Handle query string arguments without value [#149](https://github.com/miguelgrinberg/microdot/issues/149) ([commit](https://github.com/miguelgrinberg/microdot/commit/3554bc91cb1523efa5b66fe3ef173f8e86e8c2a0))
|
||||
- Support empty responses with ASGI adapter ([commit](https://github.com/miguelgrinberg/microdot/commit/e09e9830f43af41d38775547637558494151a385))
|
||||
- Added CORS extension to Python package ([commit](https://github.com/miguelgrinberg/microdot/commit/304ca2ef6881fe718126b3e308211e760109d519))
|
||||
- Document access to WSGI and ASGI attributes [#153](https://github.com/miguelgrinberg/microdot/issues/153) ([commit](https://github.com/miguelgrinberg/microdot/commit/d99df2c4010ab70c60b86ab334d656903e04eb26))
|
||||
- Upgrade micropython tests to use v1.20 ([commit](https://github.com/miguelgrinberg/microdot/commit/e0f0565551966ee0238a5a1819c78a13639ad704))
|
||||
|
||||
**Release 1.3.2** - 2023-06-13
|
||||
|
||||
- In ASGI, return headers as strings and not binary [#144](https://github.com/miguelgrinberg/microdot/issues/144) ([commit](https://github.com/miguelgrinberg/microdot/commit/e92310fa55bbffcdcbb33f560e27c3579d7ac451))
|
||||
- Incorrect import in `static_async.py` example ([commit](https://github.com/miguelgrinberg/microdot/commit/c07a53943508e64baea160748e67efc92e75b036))
|
||||
|
||||
**Release 1.3.1** - 2023-05-21
|
||||
|
||||
- Support negative numbers for int path components [#137](https://github.com/miguelgrinberg/microdot/issues/137) ([commit](https://github.com/miguelgrinberg/microdot/commit/a0dd7c8ab6d681932324e56ed101aba861a105a0))
|
||||
- Use a more conservative default for socket timeout [#130](https://github.com/miguelgrinberg/microdot/issues/130) ([commit](https://github.com/miguelgrinberg/microdot/commit/239cf4ff37268a7e2467b93be44fe9f91cee8aee))
|
||||
- More robust check for socket timeout error code [#106](https://github.com/miguelgrinberg/microdot/issues/106) ([commit](https://github.com/miguelgrinberg/microdot/commit/efec9f14be7b6f3451e4d1d0fe7e528ce6ca74dc))
|
||||
- WebSocket error when handling PING packet [#129](https://github.com/miguelgrinberg/microdot/issues/129) ([commit](https://github.com/miguelgrinberg/microdot/commit/87cd098f66e24bed6bbad29b1490a129e355bbb3))
|
||||
- Explicitly set UTF-8 encoding for HTML files in examples [#132](https://github.com/miguelgrinberg/microdot/issues/132) ([commit](https://github.com/miguelgrinberg/microdot/commit/f81de6d9582f4905b9c2735d3c639b92d7e77994))
|
||||
|
||||
**Release 1.3.0** - 2023-04-08
|
||||
|
||||
- Cross-Origin Resource Sharing (CORS) extension [#45](https://github.com/miguelgrinberg/microdot/issues/45) ([commit](https://github.com/miguelgrinberg/microdot/commit/67798f7dbffb30018ab4b62a9aaa297f63bc9e64))
|
||||
|
||||
5
MANIFEST.in
Normal file
5
MANIFEST.in
Normal file
@@ -0,0 +1,5 @@
|
||||
include README.md LICENSE tox.ini
|
||||
recursive-include docs *
|
||||
recursive-exclude docs/_build *
|
||||
recursive-include tests *
|
||||
exclude **/*.pyc
|
||||
BIN
bin/micropython
BIN
bin/micropython
Binary file not shown.
@@ -137,8 +137,8 @@ subdirectory. This location can be changed with the
|
||||
.. note::
|
||||
The Jinja extension is not compatible with MicroPython.
|
||||
|
||||
Maintaing Secure User Sessions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Maintaining Secure User Sessions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. list-table::
|
||||
:align: left
|
||||
@@ -486,6 +486,9 @@ web application using the Gunicorn web server::
|
||||
|
||||
gunicorn test:app
|
||||
|
||||
When using this WSGI adapter, the ``environ`` dictionary provided by the web
|
||||
server is available to request handlers as ``request.environ``.
|
||||
|
||||
Using an ASGI Web Server
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
@@ -529,3 +532,5 @@ web application using the Uvicorn web server::
|
||||
|
||||
uvicorn test:app
|
||||
|
||||
When using this ASGI adapter, the ``scope`` dictionary provided by the web
|
||||
server is available to request handlers as ``request.asgi_scope``.
|
||||
|
||||
@@ -301,7 +301,7 @@ expected to return an updated response object.
|
||||
|
||||
.. note::
|
||||
The :ref:`request.g <The "g" Object>` object is a special object that allows
|
||||
the before and after request handlers, as well sa the route function to
|
||||
the before and after request handlers, as well as the route function to
|
||||
share data during the life of the request.
|
||||
|
||||
Error Handlers
|
||||
@@ -500,7 +500,7 @@ contents as a file-like object.
|
||||
Cookies
|
||||
^^^^^^^
|
||||
|
||||
Cookies that are sent by the client are made available throught the
|
||||
Cookies that are sent by the client are made available through the
|
||||
:attr:`cookies <microdot.Request.cookies>` attribute of the request object in
|
||||
dictionary form.
|
||||
|
||||
@@ -595,7 +595,7 @@ always returned to the client in the response body::
|
||||
In the above example, Microdot issues a standard 200 status code response, and
|
||||
inserts the necessary headers.
|
||||
|
||||
The applicaton can provide its own status code as a second value returned from
|
||||
The application can provide its own status code as a second value returned from
|
||||
the route. The example below returns a 202 status code::
|
||||
|
||||
@app.get('/')
|
||||
@@ -611,7 +611,7 @@ The next example returns an HTML response, instead of a default text response::
|
||||
return '<h1>Hello, World!</h1>', 202, {'Content-Type': 'text/html'}
|
||||
|
||||
If the application needs to return custom headers, but does not need to change
|
||||
the default status code, then it can return two values, omitting the stauts
|
||||
the default status code, then it can return two values, omitting the status
|
||||
code::
|
||||
|
||||
@app.get('/')
|
||||
@@ -753,7 +753,7 @@ Another option is to create a response object directly in the route function::
|
||||
Standard cookies do not offer sufficient privacy and security controls, so
|
||||
never store sensitive information in them unless you are adding additional
|
||||
protection mechanisms such as encryption or cryptographic signing. The
|
||||
:ref:`session <Maintaing Secure User Sessions>` extension implements signed
|
||||
:ref:`session <Maintaining Secure User Sessions>` extension implements signed
|
||||
cookies that prevent tampering by malicious actors.
|
||||
|
||||
Concurrency
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
aiofiles==0.8.0
|
||||
anyio==3.6.1
|
||||
blinker==1.5
|
||||
certifi==2022.12.7
|
||||
certifi==2023.7.22
|
||||
charset-normalizer==2.1.0
|
||||
click==8.1.3
|
||||
fastapi==0.79.0
|
||||
Flask==2.2.1
|
||||
Flask==2.3.2
|
||||
gunicorn==20.1.0
|
||||
h11==0.13.0
|
||||
h2==4.1.0
|
||||
@@ -22,12 +22,12 @@ priority==2.0.0
|
||||
psutil==5.9.1
|
||||
pydantic==1.9.1
|
||||
quart==0.18.0
|
||||
requests==2.28.1
|
||||
requests==2.31.0
|
||||
sniffio==1.2.0
|
||||
starlette==0.25.0
|
||||
starlette==0.27.0
|
||||
toml==0.10.2
|
||||
typing_extensions==4.3.0
|
||||
urllib3==1.26.11
|
||||
urllib3==1.26.18
|
||||
uvicorn==0.18.2
|
||||
Werkzeug==2.2.3
|
||||
wsproto==1.1.0
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Microdot GPIO Example</title>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
||||
<script>
|
||||
function getCookie(name) {
|
||||
|
||||
@@ -6,6 +6,7 @@ htmldoc = '''<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Microdot Example Page</title>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
|
||||
@@ -6,6 +6,7 @@ htmldoc = '''<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Microdot Example Page</title>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
|
||||
@@ -6,6 +6,7 @@ htmldoc = '''<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Microdot Example Page</title>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
|
||||
@@ -6,6 +6,7 @@ htmldoc = '''<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Microdot Example Page</title>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
|
||||
@@ -6,6 +6,7 @@ BASE_TEMPLATE = '''<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Microdot login example</title>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Microdot login example</h1>
|
||||
@@ -17,7 +18,7 @@ LOGGED_OUT = '''<p>You are not logged in.</p>
|
||||
<form method="POST">
|
||||
<p>
|
||||
Username:
|
||||
<input type="text" name="username" autofocus />
|
||||
<input name="username" autofocus />
|
||||
</p>
|
||||
<input type="submit" value="Submit" />
|
||||
</form>'''
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Static File Serving Demo</title>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Static File Serving Demo</h1>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from microdot_asyncio import Microdot
|
||||
from microdot import send_file
|
||||
from microdot_asyncio import Microdot, send_file
|
||||
app = Microdot()
|
||||
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ def index(request):
|
||||
<html>
|
||||
<head>
|
||||
<title>Microdot Video Streaming</title>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Microdot Video Streaming</h1>
|
||||
|
||||
@@ -21,6 +21,7 @@ def index(request):
|
||||
<html>
|
||||
<head>
|
||||
<title>Microdot Video Streaming</title>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Microdot Video Streaming</h1>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Microdot + Jinja example</title>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Microdot + Jinja example</h1>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Microdot + uTemplate example</title>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Microdot + uTemplate example</h1>
|
||||
|
||||
@@ -7,6 +7,7 @@ htmldoc = '''<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Microdot Example Page</title>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
|
||||
@@ -8,6 +8,7 @@ htmldoc = '''<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Microdot Example Page</title>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Microdot TLS WebSocket Demo</title>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Microdot TLS WebSocket Demo</h1>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Microdot Upload Example</title>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Microdot Upload Example</h1>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Microdot WebSocket Demo</title>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Microdot WebSocket Demo</h1>
|
||||
|
||||
@@ -1,6 +1,57 @@
|
||||
[project]
|
||||
name = "microdot"
|
||||
version = "1.3.5.dev0"
|
||||
authors = [
|
||||
{ name = "Miguel Grinberg", email = "miguel.grinberg@gmail.com" },
|
||||
]
|
||||
description = "The impossibly small web framework for MicroPython"
|
||||
classifiers = [
|
||||
"Environment :: Web Environment",
|
||||
"Intended Audience :: Developers",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: Implementation :: MicroPython",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Operating System :: OS Independent",
|
||||
]
|
||||
|
||||
[project.readme]
|
||||
file = "README.md"
|
||||
content-type = "text/markdown"
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/miguelgrinberg/microdot"
|
||||
"Bug Tracker" = "https://github.com/miguelgrinberg/microdot/issues"
|
||||
|
||||
[project.optional-dependencies]
|
||||
docs = [
|
||||
"sphinx",
|
||||
]
|
||||
|
||||
[tool.setuptools]
|
||||
zip-safe = false
|
||||
include-package-data = true
|
||||
py-modules = [
|
||||
"microdot",
|
||||
"microdot_asyncio",
|
||||
"microdot_utemplate",
|
||||
"microdot_jinja",
|
||||
"microdot_session",
|
||||
"microdot_cors",
|
||||
"microdot_websocket",
|
||||
"microdot_websocket_alt",
|
||||
"microdot_asyncio_websocket",
|
||||
"microdot_test_client",
|
||||
"microdot_asyncio_test_client",
|
||||
"microdot_wsgi",
|
||||
"microdot_asgi",
|
||||
"microdot_asgi_websocket",
|
||||
]
|
||||
|
||||
[tool.setuptools.package-dir]
|
||||
"" = "src"
|
||||
|
||||
[build-system]
|
||||
requires = [
|
||||
"setuptools>=42",
|
||||
"wheel"
|
||||
"setuptools>=61.2",
|
||||
]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
38
setup.cfg
38
setup.cfg
@@ -1,38 +0,0 @@
|
||||
[metadata]
|
||||
name = microdot
|
||||
version = 1.3.0
|
||||
author = Miguel Grinberg
|
||||
author_email = miguel.grinberg@gmail.com
|
||||
description = The impossibly small web framework for MicroPython
|
||||
long_description = file: README.md
|
||||
long_description_content_type = text/markdown
|
||||
url = https://github.com/miguelgrinberg/microdot
|
||||
project_urls =
|
||||
Bug Tracker = https://github.com/miguelgrinberg/microdot/issues
|
||||
classifiers =
|
||||
Environment :: Web Environment
|
||||
Intended Audience :: Developers
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: Implementation :: MicroPython
|
||||
License :: OSI Approved :: MIT License
|
||||
Operating System :: OS Independent
|
||||
|
||||
[options]
|
||||
zip_safe = False
|
||||
include_package_data = True
|
||||
package_dir =
|
||||
= src
|
||||
py_modules =
|
||||
microdot
|
||||
microdot_asyncio
|
||||
microdot_utemplate
|
||||
microdot_jinja
|
||||
microdot_session
|
||||
microdot_websocket
|
||||
microdot_websocket_alt
|
||||
microdot_asyncio_websocket
|
||||
microdot_test_client
|
||||
microdot_asyncio_test_client
|
||||
microdot_wsgi
|
||||
microdot_asgi
|
||||
microdot_asgi_websocket
|
||||
@@ -308,8 +308,9 @@ class Request():
|
||||
|
||||
#: Specify a suggested read timeout to use when reading the request. Set to
|
||||
#: 0 to disable the use of a timeout. This timeout should be considered a
|
||||
#: suggestion only, as some platforms may not support it.
|
||||
socket_read_timeout = 0.1
|
||||
#: suggestion only, as some platforms may not support it. The default is
|
||||
#: 1 second.
|
||||
socket_read_timeout = 1
|
||||
|
||||
class G:
|
||||
pass
|
||||
@@ -403,13 +404,15 @@ class Request():
|
||||
data = MultiDict()
|
||||
if len(urlencoded) > 0:
|
||||
if isinstance(urlencoded, str):
|
||||
for k, v in [pair.split('=', 1)
|
||||
for pair in urlencoded.split('&') if pair]:
|
||||
data[urldecode_str(k)] = urldecode_str(v)
|
||||
for kv in [pair.split('=', 1)
|
||||
for pair in urlencoded.split('&') if pair]:
|
||||
data[urldecode_str(kv[0])] = urldecode_str(kv[1]) \
|
||||
if len(kv) > 1 else ''
|
||||
elif isinstance(urlencoded, bytes): # pragma: no branch
|
||||
for k, v in [pair.split(b'=', 1)
|
||||
for pair in urlencoded.split(b'&') if pair]:
|
||||
data[urldecode_bytes(k)] = urldecode_bytes(v)
|
||||
for kv in [pair.split(b'=', 1)
|
||||
for pair in urlencoded.split(b'&') if pair]:
|
||||
data[urldecode_bytes(kv[0])] = urldecode_bytes(kv[1]) \
|
||||
if len(kv) > 1 else b''
|
||||
return data
|
||||
|
||||
@property
|
||||
@@ -735,7 +738,7 @@ class URLPattern():
|
||||
if type_ == 'string':
|
||||
pattern = '[^/]+'
|
||||
elif type_ == 'int':
|
||||
pattern = '\\d+'
|
||||
pattern = '-?\\d+'
|
||||
elif type_ == 'path':
|
||||
pattern = '.+'
|
||||
elif type_.startswith('re:'):
|
||||
@@ -1071,7 +1074,7 @@ class Microdot():
|
||||
app = Microdot()
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
def index(request):
|
||||
return 'Hello, world!'
|
||||
|
||||
app.run(debug=True)
|
||||
@@ -1163,7 +1166,7 @@ class Microdot():
|
||||
req = Request.create(self, stream, addr, sock)
|
||||
res = self.dispatch_request(req)
|
||||
except socket_timeout_error as exc: # pragma: no cover
|
||||
if exc.errno and exc.errno not in [60, 110]:
|
||||
if exc.errno and exc.errno != errno.ETIMEDOUT:
|
||||
print_exception(exc) # not a timeout
|
||||
except Exception as exc: # pragma: no cover
|
||||
print_exception(exc)
|
||||
|
||||
@@ -59,8 +59,9 @@ class Microdot(BaseMicrodot):
|
||||
headers = NoCaseDict()
|
||||
content_length = 0
|
||||
for key, value in scope.get('headers', []):
|
||||
headers[key] = value
|
||||
if key.lower() == 'content-length':
|
||||
key = key.decode().title()
|
||||
headers[key] = value.decode()
|
||||
if key == 'Content-Length':
|
||||
content_length = int(value)
|
||||
|
||||
if content_length and content_length <= Request.max_body_length:
|
||||
@@ -119,17 +120,18 @@ class Microdot(BaseMicrodot):
|
||||
asyncio.ensure_future(cancel_monitor())
|
||||
|
||||
body_iter = res.body_iter().__aiter__()
|
||||
res_body = b''
|
||||
try:
|
||||
body = await body_iter.__anext__()
|
||||
res_body = await body_iter.__anext__()
|
||||
while not cancelled: # pragma: no branch
|
||||
next_body = await body_iter.__anext__()
|
||||
await send({'type': 'http.response.body',
|
||||
'body': body,
|
||||
'body': res_body,
|
||||
'more_body': True})
|
||||
body = next_body
|
||||
res_body = next_body
|
||||
except StopAsyncIteration:
|
||||
await send({'type': 'http.response.body',
|
||||
'body': body,
|
||||
'body': res_body,
|
||||
'more_body': False})
|
||||
|
||||
async def __call__(self, scope, receive, send):
|
||||
|
||||
@@ -241,7 +241,7 @@ class Microdot(BaseMicrodot):
|
||||
app = Microdot()
|
||||
|
||||
@app.route('/')
|
||||
async def index():
|
||||
async def index(request):
|
||||
return 'Hello, world!'
|
||||
|
||||
async def main():
|
||||
@@ -280,6 +280,11 @@ class Microdot(BaseMicrodot):
|
||||
|
||||
while True:
|
||||
try:
|
||||
if hasattr(self.server, 'serve_forever'): # pragma: no cover
|
||||
try:
|
||||
await self.server.serve_forever()
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
await self.server.wait_closed()
|
||||
break
|
||||
except AttributeError: # pragma: no cover
|
||||
@@ -313,7 +318,7 @@ class Microdot(BaseMicrodot):
|
||||
app = Microdot()
|
||||
|
||||
@app.route('/')
|
||||
async def index():
|
||||
async def index(request):
|
||||
return 'Hello, world!'
|
||||
|
||||
app.run(debug=True)
|
||||
|
||||
@@ -17,7 +17,7 @@ class WebSocket(BaseWebSocket):
|
||||
opcode, payload = await self._read_frame()
|
||||
send_opcode, data = self._process_websocket_frame(opcode, payload)
|
||||
if send_opcode: # pragma: no cover
|
||||
await self.send(send_opcode, data)
|
||||
await self.send(data, send_opcode)
|
||||
elif data: # pragma: no branch
|
||||
return data
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ class WebSocket:
|
||||
opcode, payload = self._read_frame()
|
||||
send_opcode, data = self._process_websocket_frame(opcode, payload)
|
||||
if send_opcode: # pragma: no cover
|
||||
self.send(send_opcode, data)
|
||||
self.send(data, send_opcode)
|
||||
elif data: # pragma: no branch
|
||||
return data
|
||||
|
||||
|
||||
@@ -53,9 +53,9 @@ class TestMicrodotASGI(unittest.TestCase):
|
||||
'type': 'http',
|
||||
'path': '/foo/bar',
|
||||
'query_string': b'baz=1',
|
||||
'headers': [('Authorization', 'Bearer 123'),
|
||||
('Cookie', 'session=xyz'),
|
||||
('Content-Length', 4)],
|
||||
'headers': [(b'Authorization', b'Bearer 123'),
|
||||
(b'Cookie', b'session=xyz'),
|
||||
(b'Content-Length', b'4')],
|
||||
'client': ['1.2.3.4', 1234],
|
||||
'method': 'POST',
|
||||
'http_version': '1.1',
|
||||
@@ -114,9 +114,9 @@ class TestMicrodotASGI(unittest.TestCase):
|
||||
scope = {
|
||||
'type': 'http',
|
||||
'path': '/foo/bar',
|
||||
'headers': [('Authorization', 'Bearer 123'),
|
||||
('Cookie', 'session=xyz'),
|
||||
('Content-Length', 4)],
|
||||
'headers': [(b'Authorization', b'Bearer 123'),
|
||||
(b'Cookie', b'session=xyz'),
|
||||
(b'Content-Length', b'4')],
|
||||
'client': ['1.2.3.4', 1234],
|
||||
'method': 'POST',
|
||||
'http_version': '1.1',
|
||||
|
||||
@@ -39,11 +39,12 @@ class TestRequest(unittest.TestCase):
|
||||
self.assertEqual(req.body, b'aaa')
|
||||
|
||||
def test_args(self):
|
||||
fd = get_request_fd('GET', '/?foo=bar&abc=def&x=%2f%%')
|
||||
fd = get_request_fd('GET', '/?foo=bar&abc=def&foo&x=%2f%%')
|
||||
req = Request.create('app', fd, 'addr')
|
||||
self.assertEqual(req.query_string, 'foo=bar&abc=def&x=%2f%%')
|
||||
self.assertEqual(req.args, MultiDict(
|
||||
{'foo': 'bar', 'abc': 'def', 'x': '/%%'}))
|
||||
self.assertEqual(req.query_string, 'foo=bar&abc=def&foo&x=%2f%%')
|
||||
md = MultiDict({'foo': 'bar', 'abc': 'def', 'x': '/%%'})
|
||||
md['foo'] = ''
|
||||
self.assertEqual(req.args, md)
|
||||
|
||||
def test_badly_formatted_args(self):
|
||||
fd = get_request_fd('GET', '/?&foo=bar&abc=def&&&x=%2f%%')
|
||||
|
||||
@@ -49,11 +49,12 @@ class TestRequestAsync(unittest.TestCase):
|
||||
self.assertEqual(req.body, b'aaa')
|
||||
|
||||
def test_args(self):
|
||||
fd = get_async_request_fd('GET', '/?foo=bar&abc=def&x=%2f%%')
|
||||
fd = get_async_request_fd('GET', '/?foo=bar&abc=def&foo&x=%2f%%')
|
||||
req = _run(Request.create('app', fd, 'writer', 'addr'))
|
||||
self.assertEqual(req.query_string, 'foo=bar&abc=def&x=%2f%%')
|
||||
self.assertEqual(req.args, MultiDict(
|
||||
{'foo': 'bar', 'abc': 'def', 'x': '/%%'}))
|
||||
self.assertEqual(req.query_string, 'foo=bar&abc=def&foo&x=%2f%%')
|
||||
md = MultiDict({'foo': 'bar', 'abc': 'def', 'x': '/%%'})
|
||||
md['foo'] = ''
|
||||
self.assertEqual(req.args, md)
|
||||
|
||||
def test_json(self):
|
||||
fd = get_async_request_fd('GET', '/foo', headers={
|
||||
|
||||
@@ -56,10 +56,12 @@ class TestURLPattern(unittest.TestCase):
|
||||
|
||||
p = URLPattern('/users/<int:id>/<int:id2>/')
|
||||
self.assertEqual(p.match('/users/123/456/'), {'id': 123, 'id2': 456})
|
||||
self.assertEqual(p.match('/users/123/-456/'), {'id': 123, 'id2': -456})
|
||||
self.assertIsNone(p.match('/users/'))
|
||||
self.assertIsNone(p.match('/users/123/456'))
|
||||
self.assertIsNone(p.match('/users/123/-456'))
|
||||
self.assertIsNone(p.match('/users/123/abc/'))
|
||||
self.assertIsNone(p.match('/users/123/456/abc'))
|
||||
self.assertIsNone(p.match('/users/123/-456/abc'))
|
||||
self.assertIsNone(p.match('/users/--123/456/'))
|
||||
|
||||
def test_path_argument(self):
|
||||
p = URLPattern('/users/<path:path>')
|
||||
|
||||
2
tox.ini
2
tox.ini
@@ -15,7 +15,7 @@ python =
|
||||
[testenv]
|
||||
commands=
|
||||
pip install -e .
|
||||
pytest -p no:logging --cov=src --cov-config=.coveragerc --cov-branch --cov-report=term-missing
|
||||
pytest -p no:logging --cov=src --cov-config=.coveragerc --cov-branch --cov-report=term-missing --cov-report=xml
|
||||
deps=
|
||||
pytest
|
||||
pytest-cov
|
||||
|
||||
Reference in New Issue
Block a user