Version 21.12 (LTS)
Table of Contents
- Version 21.12 (LTS)
- Introduction
- What to know
- Strict application and blueprint names
- Strict application and blueprint properties
- Removals
- Upgrade your streaming responses (if not already)
- CLI overhaul and MOTD (Message of the Day)
- Server running modes and changes coming to debug
- Max allowed workers
- First-class Sanic Extensions support
- Contextual exceptions
- Background task management
- Route context kwargs in definitions
- Blueprints can be registered at any time
- Noisy exceptions (force all exceptions to logs)
- Signal events as Enum
- Custom type casting of environment variables
- Disable uvloop by configuration value
- Run Sanic server with multiple TLS certificates
- News
- Thank you
Introduction#
This is the final release of the version 21 release cycle. Version 21 will now enter long-term support and will be supported for two years until December 2023.
What to know#
More details in the Changelog. Notable new or breaking features, and what to upgrade...
Strict application and blueprint names#
In v21.6 application and blueprint names were required to conform to a new set of restrictions. That change is now being enforced at startup time.
Names must:
- Only use alphanumeric characters (
a-zA-Z0-9
) - May contain a hyphen (
-
) or an underscore (_
) - Must begin with a letter or underscore (
a-zA-Z_
)
Strict application and blueprint properties#
The old leniency to allow directly setting properties of a Sanic
or Blueprint
object was deprecated and no longer allowed. You must use the ctx
object.
app = Sanic("MyApp")
app.ctx.db = Database()
Removals#
The following deprecated features no longer exist:
sanic.exceptions.abort
sanic.views.CompositionView
sanic.response.StreamingHTTPResponse
Upgrade your streaming responses (if not already)#
The sanic.response.stream
response method has been deprecated and will be removed in v22.6. If you are sill using an old school streaming response, please upgrade it.
OLD - Deprecated
async def sample_streaming_fn(response):
await response.write("foo,")
await response.write("bar")
@app.route("/")
async def test(request: Request):
return stream(sample_streaming_fn, content_type="text/csv")
Current
async def sample_streaming_fn(response):
await response.write("foo,")
await response.write("bar")
@app.route("/")
async def test(request: Request):
response = await request.respond(content_type="text/csv")
await response.send("foo,")
await response.send("bar")
CLI overhaul and MOTD (Message of the Day)#
The Sanic CLI has received a fairly extensive upgrade. It adds a bunch of new features to make it on par with app.run()
. It also includes a new MOTD display to provide quick, at-a-glance highlights about your running environment. The MOTD is TTY-aware, and therefore will be less verbose in server logs. It is mainly intended as a convenience during application development.
$ sanic --help
usage: sanic [-h] [--version] [--factory] [-s] [-H HOST] [-p PORT] [-u UNIX] [--cert CERT] [--key KEY] [--tls DIR] [--tls-strict-host]
[-w WORKERS | --fast] [--access-logs | --no-access-logs] [--debug] [-d] [-r] [-R PATH] [--motd | --no-motd] [-v]
[--noisy-exceptions | --no-noisy-exceptions]
module
▄███ █████ ██ ▄█▄ ██ █ █ ▄██████████
██ █ █ █ ██ █ █ ██
▀███████ ███▄ ▀ █ █ ██ ▄ █ ██
██ █████████ █ ██ █ █ ▄▄
████ ████████▀ █ █ █ ██ █ ▀██ ███████
To start running a Sanic application, provide a path to the module, where
app is a Sanic() instance:
$ sanic path.to.server:app
Or, a path to a callable that returns a Sanic() instance:
$ sanic path.to.factory:create_app --factory
Or, a path to a directory to run as a simple HTTP server:
$ sanic ./path/to/static --simple
Required
========
Positional:
module Path to your Sanic app. Example: path.to.server:app
If running a Simple Server, path to directory to serve. Example: ./
Optional
========
General:
-h, --help show this help message and exit
--version show program's version number and exit
Application:
--factory Treat app as an application factory, i.e. a () -> <Sanic app> callable
-s, --simple Run Sanic as a Simple Server, and serve the contents of a directory
(module arg should be a path)
Socket binding:
-H HOST, --host HOST Host address [default 127.0.0.1]
-p PORT, --port PORT Port to serve on [default 8000]
-u UNIX, --unix UNIX location of unix socket
TLS certificate:
--cert CERT Location of fullchain.pem, bundle.crt or equivalent
--key KEY Location of privkey.pem or equivalent .key file
--tls DIR TLS certificate folder with fullchain.pem and privkey.pem
May be specified multiple times to choose multiple certificates
--tls-strict-host Only allow clients that send an SNI matching server certs
Worker:
-w WORKERS, --workers WORKERS Number of worker processes [default 1]
--fast Set the number of workers to max allowed
--access-logs Display access logs
--no-access-logs No display access logs
Development:
--debug Run the server in debug mode
-d, --dev Currently is an alias for --debug. But starting in v22.3,
--debug will no longer automatically trigger auto_restart.
However, --dev will continue, effectively making it the
same as debug + auto_reload.
-r, --reload, --auto-reload Watch source directory for file changes and reload on changes
-R PATH, --reload-dir PATH Extra directories to watch and reload on changes
Output:
--motd Show the startup display
--no-motd No show the startup display
-v, --verbosity Control logging noise, eg. -vv or --verbosity=2 [default 0]
--noisy-exceptions Output stack traces for all exceptions
--no-noisy-exceptions No output stack traces for all exceptions
Server running modes and changes coming to debug
#
There are now two running modes: DEV
and PRODUCTION
. By default, Sanic server will run under PRODUCTION
mode. This is intended for deployments.
Currently, DEV
mode will operate very similarly to how debug=True
does in older Sanic versions. However, in v22.3. debug=True
will no longer enable auto-reload. If you would like to have debugging and auto-reload, you should enable DEV
mode.
DEVELOPMENT
$ sanic server:app --dev
app.run(debug=True, auto_reload=True)
PRODUCTION
$ sanic server:app
app.run()
Beginning in v22.3, PRODUCTION
mode will no longer enable access logs by default.
A summary of the changes are as follows:
Flag | Mode | Tracebacks | Logging | Access logs | Reload | Max workers |
---|---|---|---|---|---|---|
--debug | DEBUG | yes | DEBUG | yes | ^1 | |
PROD | no | INFO ^2 | ^3 | |||
--dev | DEBUG | yes | DEBUG | yes | yes | |
--fast | yes |
- ^1
--debug
to deprecate auto-reloading and remove in 22.3 - ^2 After 22.3 this moves to WARNING
- ^3 After 22.3: no
Max allowed workers#
You can easily spin up the maximum number of allowed workers using --fast
.
$ sanic server:app --fast
app.run(fast=True)
First-class Sanic Extensions support#
Sanic Extensions provides a number of additional features specifically intended for API developers. You can now easily implement all of the functionality it has to offer without additional setup as long as the package is in the environment. These features include:
- Auto create
HEAD
,OPTIONS
, andTRACE
endpoints - CORS protection
- Predefined, endpoint-specific response serializers
- Dependency injection into route handlers
- OpenAPI documentation with Redoc and/or Swagger
- Request query arguments and body input validation
The preferred method is to install it along with Sanic, but you can also install the packages on their own.
$ pip install sanic[ext]
$ pip install sanic sanic-ext
After that, no additional configuration is required. Sanic Extensions will be attached to your application and provide all of the additional functionality with no further configuration.
If you want to change how it works, or provide additional configuration, you can change Sanic extensions using app.extend
. The following examples are equivalent. The Config
object is to provide helpful type annotations for IDE development.
# This is optional, not required
app = Sanic("MyApp")
app.extend(config={"oas_url_prefix": "/apidocs"})
# This is optional, not required
app = Sanic("MyApp")
app.config.OAS_URL_PREFIX = "/apidocs"
# This is optional, not required
from sanic_ext import Config
app = Sanic("MyApp")
app.extend(config=Config(oas_url_prefix="/apidocs"))
Contextual exceptions#
In v21.9 we added default messages to exceptions that simplify the ability to consistently raise exceptions throughout your application.
class TeapotError(SanicException):
status_code = 418
message = "Sorry, I cannot brew coffee"
raise TeapotError
But this lacked two things:
- A dynamic and predictable message format
- The ability to add additional context to an error message (more on this in a moment)
The current release allows any Sanic exception to have additional information to when raised to provide context when writing an error message:
class TeapotError(SanicException):
status_code = 418
@property
def message(self):
return f"Sorry {self.extra['name']}, I cannot make you coffee"
raise TeapotError(extra={"name": "Adam"})
The new feature allows the passing of extra
meta to the exception instance. This extra
info object will be suppressed when in PRODUCTION
mode, but displayed in DEVELOPMENT
mode.
PRODUCTION
DEVELOPMENT
Getting back to item 2 from above: The ability to add additional context to an error message
This is particularly useful when creating microservices or an API that you intend to pass error messages back in JSON format. In this use case, we want to have some context around the exception beyond just a parseable error message to return details to the client.
raise TeapotError(context={"foo": "bar"})
This is information that we want to always be passed in the error (when it is available). Here is what it should look like:
PRODUCTION
{
"description": "I'm a teapot",
"status": 418,
"message": "Sorry Adam, I cannot make you coffee",
"context": {
"foo": "bar"
}
}
DEVELOPMENT
{
"description": "I'm a teapot",
"status": 418,
"message": "Sorry Adam, I cannot make you coffee",
"context": {
"foo": "bar"
},
"extra": {
"name": "Adam",
"more": "lines",
"complex": {
"one": "two"
}
},
"path": "/",
"args": {},
"exceptions": [
{
"type": "TeapotError",
"exception": "Sorry Adam, I cannot make you coffee",
"frames": [
{
"file": "handle_request",
"line": 83,
"name": "handle_request",
"src": ""
},
{
"file": "/tmp/p.py",
"line": 17,
"name": "handler",
"src": "raise TeapotError("
}
]
}
]
}
Background task management#
When using the app.add_task
method to create a background task, there now is the option to pass an optional name
keyword argument that allows it to be fetched, or cancelled.
app.add_task(dummy, name="dummy_task")
task = app.get_task("dummy_task")
app.cancel_task("dummy_task")
Route context kwargs in definitions#
When a route is defined, you can add any number of keyword arguments with a ctx_
prefix. These values will be injected into the route ctx
object.
@app.get("/1", ctx_label="something")
async def handler1(request):
...
@app.get("/2", ctx_label="something")
async def handler2(request):
...
@app.get("/99")
async def handler99(request):
...
@app.on_request
async def do_something(request):
if request.route.ctx.label == "something":
...
Blueprints can be registered at any time#
In previous versions of Sanic, there was a strict ordering of when a Blueprint could be attached to an application. If you ran app.blueprint(bp)
before attaching all objects to the Blueprint instance, they would be missed.
Now, you can attach a Blueprint at anytime and everything attached to it will be included at startup.
Noisy exceptions (force all exceptions to logs)#
There is a new NOISY_EXCEPTIONS
config value. When it is False
(which is the default), Sanic will respect the quiet
property of any SanicException
. This means that an exception with quiet=True
will not be displayed to the log output.
However, when setting NOISY_EXCEPTIONS=True
, all exceptions will be logged regardless of the quiet
value.
This can be helpful when debugging.
app.config.NOISY_EXCEPTIONS = True
Signal events as Enum
#
There is an Enum
with all of the built-in signal values for convenience.
from sanic.signals import Event
@app.signal(Event.HTTP_LIFECYCLE_BEGIN)
async def connection_opened(conn_info):
...
Custom type casting of environment variables#
By default, Sanic will convert an int
, float
, or a bool
value when applying environment variables to the config
instance. You can extend this with your own converter:
app = Sanic(..., config=Config(converters=[UUID]))
Disable uvloop
by configuration value#
The usage of uvloop
can be controlled by configuration value:
app.config.USE_UVLOOP = False
Run Sanic server with multiple TLS certificates#
Sanic can be run with multiple TLS certificates:
app.run(
ssl=[
"/etc/letsencrypt/live/example.com/",
"/etc/letsencrypt/live/mysite.example/",
]
)
News#
Coming Soon: Python Web Development with Sanic#
A book about Sanic is coming soon by one of the core developers, @ahopkins.
Learn more at sanicbook.com.
Get equipped with the practical knowledge of working with Sanic to increase the performance and scalability of your web applications. While doing that, we will level-up your development skills as you learn to customize your application to meet the changing business needs without having to significantly over-engineer the app.
A portion of book proceeds goes into the Sanic Community Organization to help fund the development and operation of Sanic. So, buying the book is another way you can support Sanic.
Dark mode for the docs#
If you have not already noticed, this Sanic website is now available in a native dark mode. You can toggle the theme at the top right of the page.
Thank you#
Thank you to everyone that participated in this release: :clap:
@adarsharegmi @ahopkins @ashleysommer @ChihweiLHBird @cnicodeme @kianmeng @meysam81 @nuxion @prryplatypus @realDragonium @SaidBySolo @sjsadowski @Tronic @Varriount @vltr @whos4n3
And, a special thank you to @miss85246 and @ZinkLu for their tremendous work keeping the documentation synced and translated into Chinese.
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.