Documentation improvements

This commit is contained in:
Miguel Grinberg
2023-12-28 12:30:35 +00:00
parent 28007ea583
commit b80b6b64d0
9 changed files with 85 additions and 42 deletions

View File

@@ -16,6 +16,7 @@ jobs:
- run: python -m pip install --upgrade pip wheel - run: python -m pip install --upgrade pip wheel
- run: pip install tox tox-gh-actions - run: pip install tox tox-gh-actions
- run: tox -eflake8 - run: tox -eflake8
- run: tox -edocs
tests: tests:
name: tests name: tests
strategy: strategy:

View File

@@ -1,8 +1,8 @@
API Reference API Reference
============= =============
``microdot`` module Core API
------------------- --------
.. autoclass:: microdot.Microdot .. autoclass:: microdot.Microdot
:members: :members:
@@ -14,51 +14,57 @@ API Reference
:members: :members:
``websocket`` extension WebSocket
----------------------- ---------
.. automodule:: microdot.websocket .. automodule:: microdot.websocket
:members: :members:
``utemplate`` templating extension Server-Sent Events (SSE)
---------------------------------- ------------------------
.. automodule:: microdot.sse
:members:
Templates (uTemplate)
---------------------
.. automodule:: microdot.utemplate .. automodule:: microdot.utemplate
:members: :members:
``jinja`` templating extension Templates (Jinja)
------------------------------ -----------------
.. automodule:: microdot.jinja .. automodule:: microdot.jinja
:members: :members:
``session`` extension User Sessions
--------------------- -------------
.. automodule:: microdot.session .. automodule:: microdot.session
:members: :members:
``cors`` extension Cross-Origin Resource Sharing (CORS)
------------------ ------------------------------------
.. automodule:: microdot.cors .. automodule:: microdot.cors
:members: :members:
``test_client`` extension Test Client
------------------------- -----------
.. automodule:: microdot.test_client .. automodule:: microdot.test_client
:members: :members:
``asgi`` extension ASGI
------------------ ----
.. autoclass:: microdot.asgi.Microdot .. autoclass:: microdot.asgi.Microdot
:members: :members:
:exclude-members: shutdown, run :exclude-members: shutdown, run
``wsgi`` extension WSGI
------------------- ----
.. autoclass:: microdot.wsgi.Microdot .. autoclass:: microdot.wsgi.Microdot
:members: :members:

View File

@@ -25,14 +25,15 @@ and incorporated into a custom MicroPython firmware.
Use the following guidelines to know what files to copy: Use the following guidelines to know what files to copy:
- For a minimal setup with only the base web server functionality, copy * For a minimal setup with only the base web server functionality, copy
`microdot.py <https://github.com/miguelgrinberg/microdot/blob/main/src/microdot/microdot.py>`_ `microdot.py <https://github.com/miguelgrinberg/microdot/blob/main/src/microdot/microdot.py>`_
into your project. into your project.
- For a configuration that includes one or more optional extensions, create a * For a configuration that includes one or more optional extensions, create a
*microdot* directory in your device and copy the following files: *microdot* directory in your device and copy the following files:
- `__init__.py <https://github.com/miguelgrinberg/microdot/blob/main/src/microdot/__init__.py>`_
- `microdot.py <https://github.com/miguelgrinberg/microdot/blob/main/src/microdot/microdot.py>`_ * `__init__.py <https://github.com/miguelgrinberg/microdot/blob/main/src/microdot/__init__.py>`_
- any needed `extensions <https://github.com/miguelgrinberg/microdot/tree/main/src/microdot>`_. * `microdot.py <https://github.com/miguelgrinberg/microdot/blob/main/src/microdot/microdot.py>`_
* any needed `extensions <https://github.com/miguelgrinberg/microdot/tree/main/src/microdot>`_.
Getting Started Getting Started

View File

@@ -45,6 +45,12 @@ class _BodyStream: # pragma: no cover
class Microdot(BaseMicrodot): class Microdot(BaseMicrodot):
"""A subclass of the core :class:`Microdot <microdot.Microdot>` class that
implements the ASGI protocol.
This class must be used as the application instance when running under an
ASGI web server.
"""
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.embedded_server = False self.embedded_server = False

View File

@@ -134,7 +134,7 @@ def with_session(f):
return 'Hello, World!' return 'Hello, World!'
Note that the decorator does not save the session. To update the session, Note that the decorator does not save the session. To update the session,
call the :func:`update_session <microdot.session.update_session>` function. call the :func:`session.save() <microdot.session.SessionDict.save>` method.
""" """
async def wrapper(request, *args, **kwargs): async def wrapper(request, *args, **kwargs):
return await invoke_handler( return await invoke_handler(

View File

@@ -3,6 +3,11 @@ import json
class SSE: class SSE:
"""Server-Sent Events object.
An object of this class is sent to handler functions to manage the SSE
connection.
"""
def __init__(self): def __init__(self):
self.event = asyncio.Event() self.event = asyncio.Event()
self.queue = [] self.queue = []
@@ -40,19 +45,9 @@ def sse_response(request, event_function, *args, **kwargs):
:param args: additional positional arguments to be passed to the response. :param args: additional positional arguments to be passed to the response.
:param kwargs: additional keyword arguments to be passed to the response. :param kwargs: additional keyword arguments to be passed to the response.
Example:: This is a low-level function that can be used to implement a custom SSE
endpoint. In general the :func:`microdot.sse.with_sse` decorator should be
@app.route('/events') used instead.
async def events_route(request):
async def events(request, sse):
# send an unnamed event with string data
await sse.send('hello')
# send an unnamed event with JSON data
await sse.send({'foo': 'bar'})
# send a named event
await sse.send('hello', event='greeting')
return sse_response(request, events)
""" """
sse = SSE() sse = SSE()
@@ -95,9 +90,14 @@ def with_sse(f):
@app.route('/events') @app.route('/events')
@with_sse @with_sse
async def events(request, sse): async def events(request, sse):
for i in range(10): # send an unnamed event with string data
await asyncio.sleep(1) await sse.send('hello')
await sse.send(f'{i}')
# send an unnamed event with JSON data
await sse.send({'foo': 'bar'})
# send a named event
await sse.send('hello', event='greeting')
""" """
async def sse_handler(request, *args, **kwargs): async def sse_handler(request, *args, **kwargs):
return sse_response(request, f, *args, **kwargs) return sse_response(request, f, *args, **kwargs)

View File

@@ -5,6 +5,11 @@ from microdot.microdot import MUTED_SOCKET_ERRORS
class WebSocket: class WebSocket:
"""A WebSocket connection object.
An instance of this class is sent to handler functions to manage the
WebSocket connection.
"""
CONT = 0 CONT = 0
TEXT = 1 TEXT = 1
BINARY = 2 BINARY = 2
@@ -26,6 +31,7 @@ class WebSocket:
b'Sec-WebSocket-Accept: ' + response + b'\r\n\r\n') b'Sec-WebSocket-Accept: ' + response + b'\r\n\r\n')
async def receive(self): async def receive(self):
"""Receive a message from the client."""
while True: while True:
opcode, payload = await self._read_frame() opcode, payload = await self._read_frame()
send_opcode, data = self._process_websocket_frame(opcode, payload) send_opcode, data = self._process_websocket_frame(opcode, payload)
@@ -35,12 +41,20 @@ class WebSocket:
return data return data
async def send(self, data, opcode=None): async def send(self, data, opcode=None):
"""Send a message to the client.
:param data: the data to send, given as a string or bytes.
:param opcode: a custom frame opcode to use. If not given, the opcode
is ``TEXT`` or ``BINARY`` depending on the type of the
data.
"""
frame = self._encode_websocket_frame( frame = self._encode_websocket_frame(
opcode or (self.TEXT if isinstance(data, str) else self.BINARY), opcode or (self.TEXT if isinstance(data, str) else self.BINARY),
data) data)
await self.request.sock[1].awrite(frame) await self.request.sock[1].awrite(frame)
async def close(self): async def close(self):
"""Close the websocket connection."""
if not self.closed: # pragma: no cover if not self.closed: # pragma: no cover
self.closed = True self.closed = True
await self.send(b'', self.CLOSE) await self.send(b'', self.CLOSE)

View File

@@ -9,6 +9,12 @@ from microdot.websocket import WebSocket, websocket_upgrade, \
class Microdot(BaseMicrodot): class Microdot(BaseMicrodot):
"""A subclass of the core :class:`Microdot <microdot.Microdot>` class that
implements the WSGI protocol.
This class must be used as the application instance when running under a
WSGI web server.
"""
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.loop = asyncio.new_event_loop() self.loop = asyncio.new_event_loop()

13
tox.ini
View File

@@ -1,5 +1,5 @@
[tox] [tox]
envlist=flake8,py38,py39,py310,py311,py312,upy,cpy,benchmark envlist=flake8,py38,py39,py310,py311,py312,upy,cpy,benchmark,docs
skipsdist=True skipsdist=True
skip_missing_interpreters=True skip_missing_interpreters=True
@@ -36,7 +36,6 @@ commands=sh -c "bin/circuitpython run_tests.py"
[testenv:upy-mac] [testenv:upy-mac]
allowlist_externals=micropython allowlist_externals=micropython
commands=micropython run_tests.py commands=micropython run_tests.py
deps=
[testenv:benchmark] [testenv:benchmark]
deps= deps=
@@ -59,3 +58,13 @@ deps=
flake8 flake8
commands= commands=
flake8 --ignore=W503 --exclude examples/templates/utemplate/templates src tests examples flake8 --ignore=W503 --exclude examples/templates/utemplate/templates src tests examples
[testenv:docs]
changedir=docs
deps=
sphinx
pyjwt
allowlist_externals=
make
commands=
make html