sanic/guide/content/en/release-notes/2021/v21.12.md

511 lines
17 KiB
Markdown
Raw Permalink Normal View History

# Version 21.12 (LTS)
.. toc::
## Introduction
This is the final release of the version 21 [release cycle](../../org/policies.md#release-schedule). 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](https://sanic.readthedocs.io/en/stable/sanic/changelog.html). Notable new or breaking features, and what to upgrade...
### Strict application and blueprint names
In [v21.6](./v21.6.md#stricter-application-and-blueprint-names-and-deprecation) 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**:
1. Only use alphanumeric characters (`a-zA-Z0-9`)
2. May contain a hyphen (`-`) or an underscore (`_`)
3. 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.
```python
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**
```python
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**
```python
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
```
```python
app.run(debug=True, auto_reload=True)
```
**PRODUCTION**
```
$ sanic server:app
```
```python
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
```
```python
app.run(fast=True)
```
### First-class Sanic Extensions support
[Sanic Extensions](../../plugins/sanic-ext/getting-started.md) 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`, and `TRACE` 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.
.. column::
```
$ pip install sanic[ext]
```
.. column::
```
$ 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.
.. column::
```python
# This is optional, not required
app = Sanic("MyApp")
app.extend(config={"oas_url_prefix": "/apidocs"})
```
.. column::
```python
# This is optional, not required
app = Sanic("MyApp")
app.config.OAS_URL_PREFIX = "/apidocs"
```
.. column::
```python
# This is optional, not required
from sanic_ext import Config
app = Sanic("MyApp")
app.extend(config=Config(oas_url_prefix="/apidocs"))
```
.. column::
### Contextual exceptions
In [v21.9](./v21.9.md#default-exception-messages) we added default messages to exceptions that simplify the ability to consistently raise exceptions throughout your application.
```python
class TeapotError(SanicException):
status_code = 418
message = "Sorry, I cannot brew coffee"
raise TeapotError
```
But this lacked two things:
1. A dynamic and predictable message format
2. 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:
```python
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.
.. column::
**PRODUCTION**
![image](https://user-images.githubusercontent.com/166269/139014161-cda67cd1-843f-4ad2-9fa1-acb94a59fc4d.png)
.. column::
**DEVELOPMENT**
![image](https://user-images.githubusercontent.com/166269/139014121-0596b084-b3c5-4adb-994e-31ba6eba6dad.png)
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.
```python
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:
.. column::
**PRODUCTION**
```json
{
"description": "I'm a teapot",
"status": 418,
"message": "Sorry Adam, I cannot make you coffee",
"context": {
"foo": "bar"
}
}
```
.. column::
**DEVELOPMENT**
```json
{
"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.
```python
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.
```python
@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.
```python
app.config.NOISY_EXCEPTIONS = True
```
### Signal events as `Enum`
There is an `Enum` with all of the built-in signal values for convenience.
```python
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:
```python
app = Sanic(..., config=Config(converters=[UUID]))
```
### Disable `uvloop` by configuration value
The usage of `uvloop` can be controlled by configuration value:
```python
app.config.USE_UVLOOP = False
```
### Run Sanic server with multiple TLS certificates
Sanic can be run with multiple TLS certificates:
```python
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](https://github.com/ahopkins).
Learn more at [sanicbook.com](https://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](https://github.com/adarsharegmi)
[@ahopkins](https://github.com/ahopkins)
[@ashleysommer](https://github.com/ashleysommer)
[@ChihweiLHBird](https://github.com/ChihweiLHBird)
[@cnicodeme](https://github.com/cnicodeme)
[@kianmeng](https://github.com/kianmeng)
[@meysam81](https://github.com/meysam81)
[@nuxion](https://github.com/nuxion)
[@prryplatypus](https://github.com/prryplatypus)
[@realDragonium](https://github.com/realDragonium)
[@SaidBySolo](https://github.com/SaidBySolo)
[@sjsadowski](https://github.com/sjsadowski)
[@Tronic](https://github.com/tronic)
[@Varriount](https://github.com/Varriount)
[@vltr](https://github.com/vltr)
[@whos4n3](https://github.com/whos4n3)
And, a special thank you to [@miss85246](https://github.com/miss85246) and [@ZinkLu](https://github.com/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](https://opencollective.com/sanic-org/).