235 lines
9.6 KiB
Markdown
235 lines
9.6 KiB
Markdown
|
# Version 21.9
|
||
|
|
||
|
.. toc::
|
||
|
|
||
|
## Introduction
|
||
|
|
||
|
This is the third release of the version 21 [release cycle](../../org/policies.md#release-schedule). Version 21 will be "finalized" in the December long-term support version release.
|
||
|
|
||
|
## 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...
|
||
|
|
||
|
### Removal of config values: `WEBSOCKET_READ_LIMIT`, `WEBSOCKET_WRITE_LIMIT` and `WEBSOCKET_MAX_QUEUE`
|
||
|
|
||
|
With the complete overhaul of the websocket implementation, these configuration values were removed. There currently is not a plan to replace them.
|
||
|
|
||
|
### Deprecation of default value of `FALLBACK_ERROR_FORMAT`
|
||
|
|
||
|
When no error handler is attached, Sanic has used `html` as the fallback format-type. This has been deprecated and will change to `text` starting in v22.3. While the value of this has changed to `auto`, it will still continue to use HTML as the last resort thru v21.12LTS before changing.
|
||
|
|
||
|
### `ErrorHandler.lookup` signature deprecation
|
||
|
|
||
|
The `ErrorHandler.lookup` now **requires** two positional arguments:
|
||
|
|
||
|
```python
|
||
|
def lookup(self, exception, route_name: Optional[str]):
|
||
|
```
|
||
|
|
||
|
A non-conforming method will cause Blueprint-specific exception handlers to not properly attach.
|
||
|
|
||
|
### Reminder of upcoming removals
|
||
|
|
||
|
As a reminder, the following items have already been deprecated, and will be removed in version 21.12LTS
|
||
|
|
||
|
- `CompositionView`
|
||
|
- `load_env` (use `env_prefix` instead)
|
||
|
- Sanic objects (application instances, blueprints, and routes) must by alphanumeric conforming to: `^[a-zA-Z][a-zA-Z0-9_\-]*$`
|
||
|
- Arbitrary assignment of objects to application and blueprint instances (use `ctx` instead; removal of this has been bumped from 21.9 to 21.12)
|
||
|
|
||
|
### Overhaul of websockets
|
||
|
|
||
|
There has been a huge overhaul to the handling of websocket connections. Thanks to [@aaugustin](https://github.com/aaugustin) the [`websockets`](https://websockets.readthedocs.io/en/stable/index.html) now has a new implementation that allows Sanic to handle the I/O of websocket connections on its own. Therefore, Sanic has bumped the minimum version to `websockets>=10.0`.
|
||
|
|
||
|
The change should mostly be unnoticeable to developers, except that some of the oddities around websocket handlers in Sanic have been corrected. For example, you now should be able to catch the `CancellError` yourself when someone disconnects:
|
||
|
|
||
|
```python
|
||
|
@app.websocket("/")
|
||
|
async def handler(request, ws):
|
||
|
try:
|
||
|
while True:
|
||
|
await asyncio.sleep(0.25)
|
||
|
except asyncio.CancelledError:
|
||
|
print("User closed connection")
|
||
|
```
|
||
|
|
||
|
### Built-in signals
|
||
|
|
||
|
Version [21.3](./v21.3.md) introduced [signals](../advanced/signals.md). Now, Sanic dispatches signal events **from within the codebase** itself. This means that developers now have the ability to hook into the request/response cycle at a much closer level than before.
|
||
|
|
||
|
Previously, if you wanted to inject some logic you were limited to middleware. Think of integrated signals as _super_-middleware. The events that are dispatched now include:
|
||
|
|
||
|
- `http.lifecycle.begin`
|
||
|
- `http.lifecycle.complete`
|
||
|
- `http.lifecycle.exception`
|
||
|
- `http.lifecycle.handle`
|
||
|
- `http.lifecycle.read_body`
|
||
|
- `http.lifecycle.read_head`
|
||
|
- `http.lifecycle.request`
|
||
|
- `http.lifecycle.response`
|
||
|
- `http.lifecycle.send`
|
||
|
- `http.middleware.after`
|
||
|
- `http.middleware.before`
|
||
|
- `http.routing.after`
|
||
|
- `http.routing.before`
|
||
|
- `server.init.after`
|
||
|
- `server.init.before`
|
||
|
- `server.shutdown.after`
|
||
|
- `server.shutdown.before`
|
||
|
|
||
|
|
||
|
.. note::
|
||
|
|
||
|
The `server` signals are the same as the four (4) main server listener events. In fact, those listeners themselves are now just convenience wrappers to signal implementations.
|
||
|
|
||
|
|
||
|
### Smarter `auto` exception formatting
|
||
|
|
||
|
Sanic will now try to respond with an appropriate exception format based upon the endpoint and the client. For example, if your endpoint always returns a `sanic.response.json` object, then any exceptions will automatically be formatted in JSON. The same is true for `text` and `html` responses.
|
||
|
|
||
|
Furthermore, you now can _explicitly_ control which formatter to use on a route-by-route basis using the route definition:
|
||
|
|
||
|
```python
|
||
|
@app.route("/", error_format="json")
|
||
|
async def handler(request):
|
||
|
pass
|
||
|
```
|
||
|
|
||
|
### Blueprint copying
|
||
|
|
||
|
Blueprints can be copied to new instances. This will carry forward everything attached to it, like routes, middleware, etc.
|
||
|
|
||
|
```python
|
||
|
v1 = Blueprint("Version1", version=1)
|
||
|
|
||
|
@v1.route("/something")
|
||
|
def something(request):
|
||
|
pass
|
||
|
|
||
|
v2 = v1.copy("Version2", version=2)
|
||
|
|
||
|
app.blueprint(v1)
|
||
|
app.blueprint(v2)
|
||
|
```
|
||
|
|
||
|
```
|
||
|
/v1/something
|
||
|
/v2/something
|
||
|
```
|
||
|
### Blueprint group convenience methods
|
||
|
|
||
|
Blueprint groups should now have all of the same methods available to them as regular Blueprints. With this, along with Blueprint copying, Blueprints should now be very composable and flexible.
|
||
|
|
||
|
### Accept header parsing
|
||
|
|
||
|
Sanic `Request` objects can parse an `Accept` header to provide an ordered list of the client's content-type preference. You can simply access it as an accessor:
|
||
|
|
||
|
```python
|
||
|
print(request.accept)
|
||
|
# ["*/*"]
|
||
|
```
|
||
|
|
||
|
It also is capable of handling wildcard matching. For example, assuming the incoming request included:
|
||
|
|
||
|
```
|
||
|
Accept: */*
|
||
|
```
|
||
|
|
||
|
Then, the following is `True`:
|
||
|
|
||
|
```python
|
||
|
"text/plain" in request.accept
|
||
|
```
|
||
|
|
||
|
### Default exception messages
|
||
|
|
||
|
Any exception that derives from `SanicException` can now define a default exception message. This makes it more convenient and maintainable to reuse the same exception in multiple places without running into DRY issues with the message that the exception provides.
|
||
|
|
||
|
```python
|
||
|
class TeaError(SanicException):
|
||
|
message = "Tempest in a teapot"
|
||
|
|
||
|
raise TeaError
|
||
|
```
|
||
|
|
||
|
### Type annotation conveniences
|
||
|
|
||
|
It is now possible to control the path parameter types using Python's type annotations. Instead of doing this:
|
||
|
|
||
|
```python
|
||
|
@app.route("/<one:int>/<two:float>/<three:uuid>")
|
||
|
def handler(request: Request, one: int, two: float, three: UUID):
|
||
|
...
|
||
|
```
|
||
|
|
||
|
You can now simply do this:
|
||
|
|
||
|
```python
|
||
|
@app.route("/<one>/<two>/<three>")
|
||
|
def handler(request: Request, one: int, two: float, three: UUID):
|
||
|
...
|
||
|
```
|
||
|
|
||
|
Both of these examples will result in the same routing principles to be applied.
|
||
|
|
||
|
### Explicit static resource type
|
||
|
|
||
|
You can now explicitly tell a `static` endpoint whether it is supposed to treat the resource as a file or a directory:
|
||
|
|
||
|
```python
|
||
|
static("/", "/path/to/some/file", resource_type="file"))
|
||
|
```
|
||
|
|
||
|
## News
|
||
|
|
||
|
### Release of `sanic-ext` and deprecation of `sanic-openapi`
|
||
|
|
||
|
One of the core principles of Sanic is that it is meant to be a tool, not a dictator. As the frontpage of this website states:
|
||
|
|
||
|
> Build the way you want to build without letting your tooling constrain you.
|
||
|
|
||
|
This means that a lot of common features used (specifically by Web API developers) do not exist in the `sanic` repository. This is for good reason. Being unopinionated provides the developer freedom and flexibility.
|
||
|
|
||
|
But, sometimes you do not want to have to build and rebuild the same things. Sanic has until now really relied upon the awesome support of the community to fill in the gaps with plugins.
|
||
|
|
||
|
From the early days, there has been an official `sanic-openapi` package that offered the ability to create OpenAPI documentation based upon your application. But, that project has been plagued over the years and has not been given as much priority as the main project.
|
||
|
|
||
|
Starting with the release of v21.9, the SCO is deprecating the `sanic-openapi` package and moving it to maintenance mode. This means that it will continue to get updates as needed to maintain it for the current future, but it will not receive any new feature enhancements.
|
||
|
|
||
|
A new project called `sanic-ext` is taking its place. This package provides not only the ability to build OAS3 documentation, but fills in many of the gaps that API developers may want in their applications. For example, out of the box it will setup CORS, and auto enable `HEAD` and `OPTIONS` responses where needed. It also has the ability validate incoming data using either standard library Dataclasses or Pydantic models.
|
||
|
|
||
|
The list of goodies includes:
|
||
|
- CORS protection
|
||
|
- incoming request validation
|
||
|
- auto OAS3 documentation using Redoc and/or Swagger UI
|
||
|
- auto `HEAD`, `OPTIONS`, and `TRACE` responses
|
||
|
- dependency injection
|
||
|
- response serialization
|
||
|
|
||
|
This project is still in `alpha` mode for now and is subject to change. While it is considered to be production capable, there may be some need to change the API as we continue to add features.
|
||
|
|
||
|
Checkout the [documentation](../../plugins/sanic-ext/getting-started.md) for more details.
|
||
|
|
||
|
## Thank you
|
||
|
|
||
|
Thank you to everyone that participated in this release: :clap:
|
||
|
|
||
|
[@aaugustin](https://github.com/aaugustin)
|
||
|
[@ahopkins](https://github.com/ahopkins)
|
||
|
[@ashleysommer](https://github.com/ashleysommer)
|
||
|
[@cansarigol3megawatt](https://github.com/cansarigol3megawatt)
|
||
|
[@ChihweiLHBird](https://github.com/ChihweiLHBird)
|
||
|
[@gluhar2006](https://github.com/gluhar2006)
|
||
|
[@komar007](https://github.com/komar007)
|
||
|
[@ombe1229](https://github.com/ombe1229)
|
||
|
[@prryplatypus](https://github.com/prryplatypus)
|
||
|
[@SaidBySolo](https://github.com/SaidBySolo)
|
||
|
[@Tronic](https://github.com/tronic)
|
||
|
[@vltr](https://github.com/vltr)
|
||
|
|
||
|
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/).
|