12 KiB
Version 21.6
.. toc::
Introduction
This is the second release of the version 21 release cycle. 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
. 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. 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
.
@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. You can see some of the issues resolved on the sanic-routing
release notes.
Inline streaming with eof()
Version 21.3 included big changes in how streaming is handled. 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:
@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:
@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:
- Only consisting of alphanumeric characters
- May contain a hyphen (
-
) or an underscore (_
) - 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:
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:
@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)
$ 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.
class CustomConfig(Config):
...
config = CustomConfig()
app = Sanic("custom", config=config)
assert isinstance(app.config, CustomConfig)
And...
class CustomContext:
...
ctx = CustomContext()
app = Sanic("custom", ctx=ctx)
assert isinstance(app.ctx, CustomContext)
Sanic CLI improvements
- New flag for existing feature:
--auto-reload
- Some new shorthand flags for existing arguments
- New feature:
--factory
- New feature:
--simple
- 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.
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:
$ 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.
$ 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.
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.
# /v1/my/path
app.route("/my/path", version=1)
Now, you can alter the prefix (and therefore add path segments before the version).
# /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
andbp.route
decorators (and all the convenience decorators also)Blueprint
instantiationBlueprint.group
constructorBlueprintGroup
instantiationapp.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.
@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:
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
class DummyView(HTTPMethodView):
...
app.add_route(DummyView.as_view(), "/dummy")
Option 2 - From attach
method
class DummyView(HTTPMethodView):
...
DummyView.attach(app, "/")
Option 3 - From class definition at __init_subclass__
class DummyView(HTTPMethodView, attach=app, uri="/"):
...
Options 2 and 3 are useful if your CBV is located in another file:
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 and the Community Forums. Also, follow @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 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, 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
. It is not yet released, and in heavy active development. The goal for the project will ultimately be to replace 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: 👏
@aaugustin @ahopkins @ajaygupta2790 @ashleysommer @ENT8R @fredlllll @graingert @harshanarayana @jdraymon @Kyle-Verhoog @sanjeevanahilan @sjsadowski @Tronic @vltr @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.