Upgrading#
This page lists behavior that changed in 3.x and tends to break working code on an upgrade. The authoritative list is CHANGELOG.md; install a version you tested and read the entries between the two versions before deploying.
Python support#
Python 3.10 is no longer supported; the minimum is 3.11.
Async backend is a choice#
asyncio is the default backend. trio and curio still work, but only when selected
for the process with --aiolib or MUFFIN_AIOLIB. Code that relied on the
first installed library winning has to say which one it wants. See
Async libraries.
Handler methods#
Class-based handlers no longer detect methods from the defined coroutines. A handler now declares what it serves:
class ItemHandler(Handler):
methods = ["get", "post"] # required, or inherited from a parent
async def get(self, request):
...
Declaring a method without implementing it fails when the class is defined, not
on the first request. A handler with no methods accepts every HTTP method at
the routing layer and then fails with a logged 500 for the ones it does not
implement, so listing them is not optional in practice.
Plugin options and environment#
Environment variables now override plugin options, matching the application’s own
precedence: keyword arguments passed to the plugin are defaults. A deployment that
set something like APP_PLUGIN_TIMEOUT and also passed timeout= in code
will get the environment value now, where the code used to win. The full order is
in Configuration.
Disabled plugins#
A plugin disabled by configuration is no longer half-attached: it is not added to
app.plugins and its app property raises
PluginNotInstalledError. Code that guarded with
plugin.app (or ignored the config) needs to check whether the plugin is
registered.
Forms#
request.form() takes max_size only; file_memory_limit is gone. Uploads
stream to disk through upload_to.
Background tasks and CLI#
Failed background tasks are logged instead of tearing down the connection, so
exceptions that used to surface in logs as request errors are now separate
entries. The CLI rejects unknown arguments instead of ignoring them, and the
Manager.command alias is removed in favor of app.manage.
Checking an upgrade#
$ pip install -U muffin
$ pytest -xs # your suite, on every backend you support
$ muffin your.module --help # commands and options still resolve
If the app imports but behaves differently, the usual suspects are handler
methods, plugin option precedence and any code that assumed trio or curio was
the running backend.