Version 22.3

Table of Contents

Introduction#

This is the first release of the version 22 release cycle. All of the standard SCO libraries are now entering the same release cycle and will follow the same versioning pattern. Those packages are:

What to know#

More details in the Changelog. Notable new or breaking features, and what to upgrade...

Application multi-serve#

The Sanic server now has an API to allow you to run multiple applications side-by-side in the same process. This is done by calling app.prepare(...) on one or more application instances, one or many times. Each time it should be bound to a unique host/port combination. Then, you begin serving the applications by calling Sanic.serve().

app = Sanic("One")
app2 = Sanic("Two")

app.prepare(port=9999)
app.prepare(port=9998)
app.prepare(port=9997)
app2.prepare(port=8888)
app2.prepare(port=8887)

Sanic.serve()

In the above snippet, there are two applications that will be run concurrently and bound to multiple ports. This feature is not supported in the CLI.

This pattern is meant to be an alternative to running app.run(...). It should be noted that app.run is now just a shorthand for the above pattern and is still fully supported.

πŸ‘Ά BETA FEATURE - New path parameter type: file extensions#

A very common pattern is to create a route that dynamically generates a file. The endpoint is meant to match on a file with an extension. There is a new path parameter to match files: <foo:ext>.

@app.get("/path/to/<filename:ext>")
async def handler(request, filename, ext):
    ...

This will catch any pattern that ends with a file extension. You may, however want to expand this by specifying which extensions, and also by using other path parameter types for the file name.

For example, if you want to catch a .jpg file that is only numbers:

@app.get("/path/to/<filename=int:ext=jpg>")
async def handler(request, filename, ext):
    ...

Some potential examples:

definition example filename extension
<file:ext> page.txt "page" "txt"
<file:ext=jpg> cat.jpg "cat" "jpg"
<file:ext=jpg|png|gif|svg> cat.jpg "cat" "jpg"
<file=int:ext> 123.txt 123 "txt"
<file=int:ext=jpg|png|gif|svg> 123.svg 123 "svg"
<file=float:ext=tar.gz> 3.14.tar.gz 3.14 "tar.gz"

🚨 BREAKING CHANGE - Path parameter matching of non-empty strings#

A dynamic path parameter will only match on a non-empty string.

Previously a route with a dynamic string parameter (/<foo> or /<foo:str>) would match on any string, including empty strings. It will now only match a non-empty string. To retain the old behavior, you should use the new parameter type: /<foo:strorempty>.

@app.get("/path/to/<foo:strorempty>")
async def handler(request, foo)
    ...

🚨 BREAKING CHANGE - sanic.worker.GunicornWorker has been removed#

Departing from our normal deprecation policy, the GunicornWorker was removed as a part of the process of upgrading the Sanic server to include multi-serve. This decision was made largely in part because even while it existed it was not an optimal strategy for deploying Sanic.

If you want to deploy Sanic using gunicorn, then you are advised to do it using the strategy implemented by uvicorn. This will effectively run Sanic as an ASGI application through uvicorn. You can upgrade to this pattern by installing uvicorn:

pip install uvicorn

Then, you should be able to run it with a pattern like this:

gunicorn path.to.sanic:app -k uvicorn.workers.UvicornWorker

Authorization header parsing#

The Authorization header has been partially parseable for some time now. You have been able to use request.token to gain access to a header that was in one of the following two forms:

Authorization: Token &lt;SOME TOKEN HERE&gt;
Authorization: Bearer &lt;SOME TOKEN HERE&gt;

Sanic can now parse more credential types like BASIC:

Authorization: Basic Z2lsLWJhdGVzOnBhc3N3b3JkMTIz

This can be accessed now as request.credentials:

print(request.credentials)
# Credentials(auth_type='Basic', token='Z2lsLWJhdGVzOnBhc3N3b3JkMTIz', _username='gil-bates', _password='password123')

CLI arguments optionally injected into application factory#

Sanic will now attempt to inject the parsed CLI arguments into your factory if you are using one.

def create_app(args):
    app = Sanic("MyApp")
    print(args)
    return app
$sanic p:create_app --factory
Namespace(module='p:create_app', factory=True, simple=False, host='127.0.0.1', port=8000, unix='', cert=None, key=None, tls=None, tlshost=False, workers=1, fast=False, access_log=False, debug=False, auto_reload=False, path=None, dev=False, motd=True, verbosity=None, noisy_exceptions=False)

If you are running the CLI with --factory, you also have the option of passing arbitrary arguments to the command, which will be injected into the argument Namespace.

sanic p:create_app --factory --foo=bar
Namespace(module='p:create_app', factory=True, simple=False, host='127.0.0.1', port=8000, unix='', cert=None, key=None, tls=None, tlshost=False, workers=1, fast=False, access_log=False, debug=False, auto_reload=False, path=None, dev=False, motd=True, verbosity=None, noisy_exceptions=False, foo='bar')

New reloader process listener events#

When running Sanic server with auto-reload, there are two new events that trigger a listener only on the reloader process:

  • reload_process_start
  • reload_process_stop

These are only triggered if the reloader is running.

@app.reload_process_start
async def reload_start(*_):
    print(">>>>>> reload_start <<<<<<")

@app.reload_process_stop
async def reload_stop(*_):
    print(">>>>>> reload_stop <<<<<<")

The event loop is no longer a required argument of a listener#

You can leave out the loop argument of a listener. Both of these examples work as expected:

@app.before_server_start
async def without(app):
    ...

@app.before_server_start
async def with(app, loop):
    ...

Removal - Debug mode does not automatically start the reloader#

When running with --debug or debug=True, the Sanic server will not automatically start the auto-reloader. This feature of doing both on debug was deprecated in v21 and removed in this release. If you would like to have both debug mode and auto-reload, you can use --dev or dev=True.

dev = debug mode + auto reloader

Deprecation - Loading of lower case environment variables#

Sanic loads prefixed environment variables as configuration values. It has not distinguished between uppercase and lowercase as long as the prefix matches. However, it has always been the convention that the keys should be uppercase. This is deprecated and you will receive a warning if the value is not uppercase. In v22.9 only uppercase and prefixed keys will be loaded.

News#

Packt publishes new book on Sanic web development#

There is a new book on Python Web Development with Sanic by @ahopkins. The book is endorsed by the SCO and part of the proceeds of all sales go directly to the SCO for further development of Sanic.

You can learn more at sanicbook.com

Python Web Development with Sanic

Thank you#

Thank you to everyone that participated in this release: :clap:

@aericson @ahankinson @ahopkins @ariebovenberg @ashleysommer @Bluenix2 @ChihweiLHBird @dotlambda @eric-spitler @howzitcdf @jonra1993 @prryplatypus @raphaelauv @SaidBySolo @SerGeRybakov @Tronic


If you enjoy the project, please consider contributing. Of course we love code contributions, but we also love contributions in any form. Consider writing some documentation, showing off use cases, joining conversations and making your voice known, and if you are able: financial contributions.