347 lines
12 KiB
Markdown
347 lines
12 KiB
Markdown
# Version 21.6
|
|
|
|
.. toc::
|
|
|
|
## Introduction
|
|
|
|
This is the second release of the version 21 [release cycle](../project/policies.md#release-schedule). There will be one more release in September before version 21 is "finalized" in the December long-term support version. One thing users may have noticed starting in 21.3, the router was moved to its own package: [`sanic-routing`](https://pypi.org/project/sanic-routing). This change is likely to stay for now. Starting with this release, the minimum required version is 0.7.0.
|
|
|
|
## 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...
|
|
|
|
### Deprecation of `StreamingHTTPResponse`
|
|
|
|
The use of `StreamingHTTPResponse` has been deprecated and will be removed in the 21.12 release. This impacts both `sanic.response.stream` and `sanic.response.file_stream`, which both under the hood instantiate `StreamingHTTPResponse`.
|
|
|
|
Although the exact migration path has yet to be determined, `sanic.response.stream` and `sanic.response.file_stream` will continue to exist in v21.12 in some form as convenience operators. Look for more details throughout this Summer as we hope to have this finalized by the September release.
|
|
|
|
### Deprecation of `CompositionView`
|
|
|
|
Usage of `CompositionView` has been deprecated and will be removed in 21.12.
|
|
|
|
### Deprecation of path parameter types: `string` and `number`
|
|
|
|
Going forward, you should use `str` and `float` for path param types instead of `string` and `number`.
|
|
|
|
```python
|
|
@app.get("/<foo:str>/<bar:float>")
|
|
async def handler(request, foo: str, bar: float):
|
|
...
|
|
```
|
|
|
|
Existing `string` and `number` types are aliased and will continue to work, but will be removed in v21.12.
|
|
|
|
### Version 0.7 router upgrades
|
|
|
|
This includes a number of bug fixes and more gracefully handles a wider array of edge cases than v0.6. If you experience any patterns that are not supported, [please report them](https://github.com/sanic-org/sanic-routing/issues). You can see some of the issues resolved on the `sanic-routing` [release notes](https://github.com/sanic-org/sanic-routing/releases).
|
|
|
|
### Inline streaming with `eof()`
|
|
|
|
Version 21.3 included [big changes in how streaming is handled](https://sanic.dev/en/guide/release-notes/v21.3.html#what-to-know). The pattern introduced will become the default (see below). As a convenience, a new `response.eof()` method has been included. It should be called once the final data has been pushed to the client:
|
|
|
|
```python
|
|
@app.route("/")
|
|
async def test(request):
|
|
response = await request.respond(content_type="text/csv")
|
|
await response.send("foo,")
|
|
await response.send("bar")
|
|
await response.eof()
|
|
return response
|
|
```
|
|
|
|
### New path parameter type: `slug`
|
|
|
|
You can now specify a dynamic path segment as a `slug` with appropriate matching:
|
|
|
|
```python
|
|
@app.get("/articles/<article_slug:slug>")
|
|
async def article(request, article_slug: str):
|
|
...
|
|
```
|
|
|
|
Slugs must consist of lowercase letters or digits. They may contain a hyphen (`-`), but it cannot be the first character.
|
|
|
|
```
|
|
this-is-a-slug
|
|
with-123-is-also-a-slug
|
|
111-at-start-is-a-slug
|
|
NOT-a-slug
|
|
-NOT-a-slug
|
|
```
|
|
|
|
### Stricter application and blueprint names, and deprecation
|
|
|
|
Your application and `Blueprint` instances must conform to a stricter set of requirements:
|
|
|
|
1. Only consisting of alphanumeric characters
|
|
2. May contain a hyphen (`-`) or an underscore (`_`)
|
|
3. Must begin with a letter (uppercase or lowercase)
|
|
|
|
The naming convention is similar to Python variable naming conventions, with the addition of allowing hyphens (`-`).
|
|
|
|
The looser standard has been deprecatated. Beginning in 21.12, non-conformance will be a startup time error.
|
|
|
|
### A new access on `Route` object: `route.uri`
|
|
|
|
The `Route` object in v21.3 no longer had a `uri` attribute. Instead, the closes you could get was `route.path`. However, because of how `sanic-routing` works, the `path` property does *not* have a leading `/`. This has been corrected so that now there is a `route.uri` with a leading slash:
|
|
|
|
```python
|
|
route.uri == f"/{route.path}"
|
|
```
|
|
|
|
### A new accessor on `Request` object impacting IPs
|
|
|
|
To access the IP address of the incoming request, Sanic has had a convenience accessor on the request object: `request.ip`. That is not new, and comes from an underlying object that provides details about the open HTTP connection: `request.conn_info`.
|
|
|
|
The current version adds a new `client_ip` accessor to that `conn_info` object. For IPv4, you will not notice a difference. However, for IPv6 applications, the new accessor will provide an "unwrapped" version of the address. Consider the following example:
|
|
|
|
```python
|
|
@app.get("/")
|
|
async def handler(request):
|
|
return json(
|
|
{
|
|
"request.ip": request.ip,
|
|
"request.conn_info.client": request.conn_info.client,
|
|
"request.conn_info.client_ip": request.conn_info.client_ip,
|
|
}
|
|
)
|
|
|
|
app.run(sock=my_ipv6_sock)
|
|
```
|
|
|
|
```bash
|
|
$ curl http://\[::1\]:8000
|
|
{
|
|
"request.ip": "::1",
|
|
"request.conn_info.client": "[::1]",
|
|
"request.conn_info.client_ip": "::1"
|
|
}
|
|
|
|
```
|
|
|
|
### Alternate `Config` and `Sanic.ctx` objects
|
|
|
|
You can now pass your own config and context objects to your Sanic applications. A custom configuration *should* be a subclass of `sanic.config.Config`. The context object can be anything you want, with no restrictions whatsoever.
|
|
|
|
```python
|
|
class CustomConfig(Config):
|
|
...
|
|
|
|
config = CustomConfig()
|
|
app = Sanic("custom", config=config)
|
|
assert isinstance(app.config, CustomConfig)
|
|
```
|
|
|
|
And...
|
|
|
|
```python
|
|
class CustomContext:
|
|
...
|
|
|
|
ctx = CustomContext()
|
|
app = Sanic("custom", ctx=ctx)
|
|
assert isinstance(app.ctx, CustomContext)
|
|
```
|
|
|
|
### Sanic CLI improvements
|
|
|
|
1. New flag for existing feature: `--auto-reload`
|
|
2. Some new shorthand flags for existing arguments
|
|
3. New feature: `--factory`
|
|
4. New feature: `--simple`
|
|
5. New feature: `--reload-dir`
|
|
|
|
#### Factory applications
|
|
|
|
For applications that follow the factory pattern (a function that returns a `sanic.Sanic` instance), you can now launch your application from the Sanic CLI using the `--factory` flag.
|
|
|
|
```python
|
|
from sanic import Blueprint, Sanic, text
|
|
|
|
bp = Blueprint(__file__)
|
|
|
|
@bp.get("/")
|
|
async def handler(request):
|
|
return text("😎")
|
|
|
|
def create_app() -> Sanic:
|
|
app = Sanic(__file__)
|
|
app.blueprint(bp)
|
|
return app
|
|
```
|
|
|
|
You can now run it:
|
|
|
|
```bash
|
|
$ sanic path.to:create_app --factory
|
|
```
|
|
|
|
#### Sanic Simple Server
|
|
|
|
Sanic CLI now includes a simple pattern to serve a directory as a web server. It will look for an `index.html` at the directory root.
|
|
|
|
```bash
|
|
$ sanic ./path/to/dir --simple
|
|
```
|
|
|
|
|
|
.. warning::
|
|
|
|
This feature is still in early *beta* mode. It is likely to change in scope.
|
|
|
|
|
|
#### Additional reload directories
|
|
|
|
When using either `debug` or `auto-reload`, you can include additional directories for Sanic to watch for new files.
|
|
|
|
```bash
|
|
sanic ... --reload-dir=/path/to/foo --reload-dir=/path/to/bar
|
|
```
|
|
|
|
|
|
.. tip::
|
|
|
|
You do *NOT* need to include this on your application directory. Sanic will automatically reload when any Python file in your application changes. You should use the `reload-dir` argument when you want to listen and update your application when static files are updated.
|
|
|
|
|
|
|
|
### Version prefix
|
|
|
|
When adding `version`, your route is prefixed with `/v<YOUR_VERSION_NUM>`. This will always be at the beginning of the path. This is not new.
|
|
|
|
```python
|
|
# /v1/my/path
|
|
app.route("/my/path", version=1)
|
|
```
|
|
|
|
Now, you can alter the prefix (and therefore add path segments *before* the version).
|
|
|
|
```python
|
|
# /api/v1/my/path
|
|
app.route("/my/path", version=1, version_prefix="/api/v")
|
|
```
|
|
|
|
The `version_prefix` argument is can be defined in:
|
|
|
|
- `app.route` and `bp.route` decorators (and all the convenience decorators also)
|
|
- `Blueprint` instantiation
|
|
- `Blueprint.group` constructor
|
|
- `BlueprintGroup` instantiation
|
|
- `app.blueprint` registration
|
|
|
|
### Signal event auto-registration
|
|
|
|
Setting `config.EVENT_AUTOREGISTER` to `True` will allow you to await any signal event even if it has not previously been defined with a signal handler.
|
|
|
|
```python
|
|
@app.signal("do.something.start")
|
|
async def signal_handler():
|
|
await do_something()
|
|
await app.dispatch("do.something.complete")
|
|
|
|
# somethere else in your app:
|
|
await app.event("do.something.complete")
|
|
```
|
|
|
|
### Infinitely reusable and nestable `Blueprint` and `BlueprintGroup`
|
|
|
|
A single `Blueprint` may not be assigned and reused to multiple groups. The groups themselves can also by infinitely nested into one or more other groups. This allows for an unlimited range of composition.
|
|
|
|
### HTTP methods as `Enum`
|
|
|
|
Sanic now has `sanic.HTTPMethod`, which is an `Enum`. It can be used interchangeably with strings:
|
|
|
|
```python
|
|
from sanic import Sanic, HTTPMethod
|
|
|
|
@app.route("/", methods=["post", "PUT", HTTPMethod.PATCH])
|
|
async def handler(...):
|
|
...
|
|
```
|
|
|
|
### Expansion of `HTTPMethodView`
|
|
|
|
Class based views may be attached now in one of three ways:
|
|
|
|
**Option 1 - Existing**
|
|
```python
|
|
class DummyView(HTTPMethodView):
|
|
...
|
|
|
|
app.add_route(DummyView.as_view(), "/dummy")
|
|
```
|
|
|
|
**Option 2 - From `attach` method**
|
|
```python
|
|
class DummyView(HTTPMethodView):
|
|
...
|
|
|
|
DummyView.attach(app, "/")
|
|
```
|
|
|
|
**Option 3 - From class definition at `__init_subclass__`**
|
|
```python
|
|
class DummyView(HTTPMethodView, attach=app, uri="/"):
|
|
...
|
|
```
|
|
|
|
Options 2 and 3 are useful if your CBV is located in another file:
|
|
|
|
```python
|
|
from sanic import Sanic, HTTPMethodView
|
|
|
|
class DummyView(HTTPMethodView, attach=Sanic.get_app(), uri="/"):
|
|
...
|
|
```
|
|
|
|
## News
|
|
|
|
### Discord and support forums
|
|
|
|
If you have not already joined our community, you can become a part by joining the [Discord server](https://discord.gg/FARQzAEMAA) and the [Community Forums](https://community.sanicframework.org/). Also, follow [@sanicframework](https://twitter.com/sanicframework) on Twitter.
|
|
|
|
### SCO 2022 elections
|
|
|
|
The Summer 🏝/Winter ❄️ (choose your Hemisphere) is upon us. That means we will be holding elections for the SCO. This year, we will have the following positions to fill:
|
|
|
|
- Steering Council Member (2 year term)
|
|
- Steering Council Member (2 year term)
|
|
- Steering Council Member (1 year term)
|
|
- Release Manager v22
|
|
- Release Manager v22
|
|
|
|
[@vltr](https://github.com/vltr) will be staying on to complete his second year on the Steering Council.
|
|
|
|
If you are interested in learning more, you can read about the SCO [roles and responsibilities](../project/scope.md#roles-and-responsibilities), or Adam Hopkins on Discord.
|
|
|
|
Nominations will begin September 1. More details will be available on the Forums as we get closer.
|
|
|
|
### New project underway
|
|
|
|
We have added a new project to the SCO umbrella: [`sanic-ext`](https://github.com/sanic-org/sanic-ext). It is not yet released, and in heavy active development. The goal for the project will ultimately be to replace [`sanic-openapi`](https://github.com/sanic-org/sanic-openapi) with something that provides more features for web application developers, including input validation, CORS handling, and HTTP auto-method handlers. If you are interested in helping out, let us know on Discord. Look for an initial release of this project sometime (hopefully) before the September release.
|
|
|
|
## Thank you
|
|
|
|
Thank you to everyone that participated in this release: :clap:
|
|
|
|
[@aaugustin](https://github.com/aaugustin)
|
|
[@ahopkins](https://github.com/ahopkins)
|
|
[@ajaygupta2790](https://github.com/ajaygupta2790)
|
|
[@ashleysommer](https://github.com/ashleysommer)
|
|
[@ENT8R](https://github.com/ent8r)
|
|
[@fredlllll](https://github.com/fredlllll)
|
|
[@graingert](https://github.com/graingert)
|
|
[@harshanarayana](https://github.com/harshanarayana)
|
|
[@jdraymon](https://github.com/jdraymon)
|
|
[@Kyle-Verhoog](https://github.com/kyle-verhoog)
|
|
[@sanjeevanahilan](https://github.com/sanjeevanahilan)
|
|
[@sjsadowski](https://github.com/sjsadowski)
|
|
[@Tronic](https://github.com/tronic)
|
|
[@vltr](https://github.com/vltr)
|
|
[@ZinkLu](https://github.com/zinklu)
|
|
|
|
---
|
|
|
|
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/).
|