diff --git a/docs/freezing.rst b/docs/freezing.rst index 0a85bc2..ea75eb5 100644 --- a/docs/freezing.rst +++ b/docs/freezing.rst @@ -1,5 +1,8 @@ -Cross-Compiling and Freezing Microdot (MicroPython Only) --------------------------------------------------------- +Cross-Compiling and Freezing Microdot +------------------------------------- + +.. note:: + This section only applies when using Microdot on MicroPython. Microdot is a fairly small framework, so its size is not something you need to be concerned about unless you are working with MicroPython on hardware with a @@ -36,7 +39,7 @@ Cross-Compiling An issue that is common with low-end microcontroller boards is that they do not have enough RAM for the MicroPython compiler to compile the source files, but -once the code is compiled they are able to run it without problems. +once the code is compiled they are able to run it just fine. To address this, MicroPython allows you to cross-compile source files on your desktop or laptop computer and then upload their compiled versions to the @@ -82,8 +85,8 @@ imported directly from the device's ROM, leaving more RAM available for application use. The process to create a custom firmware is unfortunately non-trivial and -different depending on the device, so you will need to consult the MicroPython -documentation that applies to your device to learn how to do this. +different for each microcontroller platform, so you will need to consult the +MicroPython documentation that applies to your device to learn how to do this. The part of the process that is common to all devices is the creation of a `manifest file `_ diff --git a/docs/intro.rst b/docs/intro.rst index b381d93..3ce4b49 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -339,10 +339,11 @@ of letters or numbers:: return 'User: ' + username The ``re`` type returns the URL component as a string, which sometimes may not -be the most convenient. In such cases, the application can register a custom -URL component type and provide a parser function. In the following example, -a ``hex`` custom type is registered to automatically convert the arguments to -numbers:: +be the most convenient. To convert a path component to something more +meaningful than a string, the application can register a custom URL component +type and provide a parser function that performs the conversion. In the +following example, a ``hex`` custom type is registered to automatically +convert hex numbers given in the path to numbers:: from microdot import URLPattern @@ -353,18 +354,18 @@ numbers:: user = get_user_by_id(user_id) # ... -In addition to the parser, the custom URL component can include a valid pattern -as a regular expression. When a pattern is provided, the URL component will -only match if the regular expression matches the value passed in the URL. The -``hex`` example above can be expanded with a pattern as follows:: +In addition to the parser, the custom URL component can include a pattern, +given as a regular expression. When a pattern is provided, the URL component +will only match if the regular expression matches the value passed in the URL. +The ``hex`` example above can be expanded with a pattern as follows:: URLPattern.register_type('hex', pattern='[0-9a-fA-F]+', parser=lambda value: int(value, 16)) In cases where a pattern isn't provided, or when the pattern is unable to -filter all invalid values, the parser function can return ``None`` to indicate -a failed match. The next example shows how the ``hex`` type can be expanded to -do that:: +filter out all invalid values, the parser function can return ``None`` to +indicate a failed match. The next example shows how the parser for the ``hex`` +type can be expanded to do that:: def hex_parser(value): try: @@ -372,7 +373,7 @@ do that:: except ValueError: return None - URLPattern.register_type('hex', pattern='[0-9a-fA-F]+', parser=hex_parser) + URLPattern.register_type('hex', parser=hex_parser) .. note:: Dynamic path components are passed to route functions as keyword arguments, @@ -931,18 +932,36 @@ Another option is to create a response object directly in the route function:: Concurrency ~~~~~~~~~~~ -Microdot implements concurrency through the ``asyncio`` package. Applications -must ensure their handlers do not block, as this will prevent other concurrent -requests from being handled. +Microdot implements concurrency through the ``asyncio`` package, which means +that applications must be careful to prevent blocking in their handlers. -When running under CPython, ``async def`` handler functions run as native -asyncio tasks, while ``def`` handler functions are executed in a -`thread executor `_ -to prevent them from blocking the asynchronous loop. +"async def" handlers +^^^^^^^^^^^^^^^^^^^^ + +The recommendation for route handlers in Microdot is to use asynchronous +functions, declared as ``async def``. Microdot executes these handler +functions as native asynchronous tasks. The standard considerations for writing +asynchronous code apply, and in particular blocking calls should be avoided to +ensure the application runs smoothly and is always responsive. + +"def" handlers +^^^^^^^^^^^^^^ + +Microdot also supports the use of synchronous route handlers, declared as +standard ``def`` functions. These handlers are handled differently under +CPython and MicroPython. + +When running on CPython, Microdot executes synchronous handlers in a +`thread executor `_, +which uses a thread pool. The use of blocking or CPU intensive code in these +handlers does not have such a negative effect on the application, because +handlers do not run on the same thread as the asynchronous loop. On the other +hand, the application will be affected by threading issues such as those caused +by the Global Interpreter Lock. Under MicroPython the situation is different. Most microcontroller boards -implementing MicroPython do not have threading support or executors, so ``def`` -handler functions in this platform can only run in the main and only thread. -These functions will block the asynchronous loop when they take too long to -complete so ``async def`` handlers properly written to allow other handlers to -run in parallel should be preferred. +do not have or have very limited threading support, so Microdot executes +synchronous handlers in the main and often only thread available. This means +that these functions will block the asynchronous loop when they take too long +to complete. The use of properly written asynchronous handlers should be +preferred. diff --git a/src/microdot/jinja.py b/src/microdot/jinja.py index 46cd9fe..0c6ac61 100644 --- a/src/microdot/jinja.py +++ b/src/microdot/jinja.py @@ -9,7 +9,8 @@ class Template: :param kwargs: any additional options to be passed to the Jinja environment's ``get_template()`` method. """ - #: The Jinja environment. + #: The Jinja environment. The ``initialize()`` method must be called before + #: this attribute is accessed. jinja_env = None @classmethod