All notable changes to Bocadillo are documented here.
The format of this document is based on Keep a Changelog.
Versioning policy
Bocadillo adheres to Semantic Versioning, BUT…
Bocadillo is still in Alpha (< 1.0) version. As such, breaking API changes will only cause minor version bumps instead of major ones until v1.0 is reached.
As a result, we strongly recommend you read this document carefully before upgrading to any new alpha version. Breaking API changes will be denoted with a BREAKING prefix.
v0.18.3 - 2019-10-22
v0.18.2 - 2019-08-03
- Support for Python 3.8b3.
v0.18.1 - 2019-08-01
- Fixed a dependency conflict with the
websockets
library - @NeolithEra
v0.18.0 - 2019-07-07
- Compatibility with Starlette ≥ 0.12.2.
- Redirections in application of HSTS now redirect with
308 Permanent Redirect
.
- Drop support for Starlette < 0.12.2.
v0.17.0 - 2019-06-25
- Choose where Bocadillo should look for providers using the new
PROVIDER_MODULES
setting.
Terminated deprecations:
- BREAKING Recipes were removed. This includes the
Recipe
andRecipeBook
classes, as well asapp.recipe()
. Consider using routers instead. - BREAKING
app.add_asgi_middleware()
was removed. Please useapp.add_middleware()
instead. - BREAKING
@view
was removed. Please use@route(methods=...)
instead. - BREAKING
@plugin
was removed. Register plugins using thePLUGINS
setting instead.
v0.16.2 - 2019-06-18
- Added support for uvicorn 0.8.x.
- (Docs) Add Vue example repo to frontend frameworks guide.
- (Docs) Apply new brand.
- (Docs) Redesign home page.
- (Docs) "Bocadillo Blog" becomes "Bocadillo News".
v0.16.1 - 2019-06-02
- Previously, route parameter matching was not strict: a parameter could match an entire section of the URL, leading to confusing behavior. You must now use the
:path
converter (e.g./images/{filename:path}
) to match path-like route parameters.
v0.16.0 - 2019-06-01
- Reusable and composable routes using
Router
andapp.include_router()
. - Register plugins using the new
PLUGINS
setting. app.add_middleware()
now also supports ASGI middleware classes.- Exceptions raised in ASGI middleware are now processed by error handlers.
- HTTP methods on function-based views can now be specified using the
methods=
argument to@route()
.
- Bocadillo now handles missing trailing slashes by performing a temporary redirect (302) if needed. This results in more robust URL matching. Set
REDIRECT_TRAILING_SLASH = False
to disable this behavior.
- HTTP middleware is now also called when a request is routed to a sub-application.
- The
Middleware
class now also implement the ASGI interface. Mostly an implementation detail. - Methods in
ALL_HTTP_METHODS
are now lower-cased instead of upper-cased.
@plugin
is deprecated in favor of using thePLUGINS
setting. The decorator now raises aDeprecationWarning
and will be removed in v0.17.0.app.add_asgi_middleware()
is deprecated in favor ofapp.add_middleware()
. It will be removed in v0.17.0.- The
@view()
decorator has been deprecated in favor of@route(methods=...)
. It will be removed in v0.17.0
- BREAKING
res.media
was deprecated since 0.14.0, and has been removed. Please useres.json
instead.
v0.15.1 - 2019-05-17
- Improved documentation on custom templates directories.
- The
app
argument toTemplates
is now deprecated, and will be removed in 0.16.
v0.15.0 - 2019-04-26
- Bocadillo applications now implement ASGI version 3.0. Support for uvicorn 0.7.x is therefore ensured.
- BREAKING Removed
ASGIMiddleware
base class. Please create plain ASGI3-compliant classes instead. - BREAKING The Bocadillo
App
is not passed to HTTPMiddleware
anymore. Prefer plugins to perform global app initialisation. - Drop support for uvicorn < 0.6.
v0.14.2 - 2019-04-26
- Support for uvicorn 0.6.x.
v0.14.1 - 2019-04-21
- The default
HTTPError
handler now returns the error as JSON instead of plain text.
v0.14.0 - 2019-04-21
Features:
- Route parameter validation based on type annotations for HTTP and WebSocket views, e.g.
pk: int
. Annotation with any TypeSystem field is supported. - Query parameter injection and validation, e.g.
limit: int = None
. Annotation with any TypeSystem field is supported. - JSON validation based on TypeSystem.
- Settings infrastructure: Django-style
bocadillo.settings
lazy object, app configuration usingbocadillo.configure(app[, settings][, **kwargs])
. - Plugin mechanism: register a
(app: App) -> None
callable using@bocadillo.plugin
, and use the new lazysettings
object to perform plugin setup. - Build a full URL for a
LiveServer
usingserver.url("/path")
. Note:server.url
still gives access to the root live server URL. create_client
andLiveServer
can be imported frombocadillo
instead ofbocadillo.testing
.
Documentation:
- New how-to guide on integrating with the
orm
async ORM. Replaces the how-to guide on integrating with Tortoise ORM.
- CORS, HSTS, GZip, allowed hosts, static files and sessions are now implemented via plugins. See the "Removed" section for the impact on the application API.
- BREAKING: redirects now use an exception syntax:
raise Redirect("/foo")
(from bocadillo import Redirect
) instead ofapp.redirect("/foo)
.app.redirect()
has been removed consequently.
- The code base now uses
__slots__
in all relevant places. We expect some speed improvements as a result. - Error handlers can now re-raise exceptions for further processing, e.g. re-raise an
HTTPError
which will be processed by the registeredHTTPError
handler. - Previously, error handlers were searched for parent exception classes only. We now look for an error handler for the exception's class first, and then look for error handlers for any parent exception class.
App
parameters:
- BREAKING Removed
static_dir
,static_root
andstatic_config
. Use theSTATIC_DIR
,STATIC_ROOT
andSTATIC_CONFIG
settings instead. - BREAKING Removed
allowed_hosts
. Use theALLOWED_HOSTS
setting instead. - BREAKING Removed
enable_sessions
andsessions_config
. Use theSESSIONS
setting instead. - BREAKING Removed
enable_cors
andcors_config
. Use theCORS
setting instead. - BREAKING Removed
enable_hsts
. Use theHSTS
setting instead. - BREAKING Removed
enable_gzip
andgzip_min_size
. Use theGZIP
andGZIP_MIN_SIZE
settings instead.
Named routes:
- BREAKING Removed route names and namespaces, i.e.
app.route(name="foo")
andapp.route(namespace="bar")
are not supported anymore. - BREAKING Removed
app.url_for()
. Please use relative URLs if you need to refer to another route by URL.
Other:
- BREAKING: the
.run()
method onApp
has been removed in favor of theuvicorn
command shipped with the uvicorn ASGI server (which comes installed with Bocadillo). In particular, theif __name__ == "__main__": app.run()
invokation is now obsolete. Just useuvicorn app:app
instead ofpython app.py
(anduvicorn.run(app)
for programmatic usage). - BREAKING: synchronous views, HTTP middleware callbacks, hooks and error handlers are not supported anymore. Appropriate error messages will help you migrate to an all-async application.
- BREAKING: route parameter validation via specifiers (e.g.
{id:d}
) is not supported anymore. Please use type annotation-based validation instead (e.g.pk: int
). - BREAKING: debug mode has been removed, which means
App
does not accept adebug
parameter anymore. To enable hot reload, please useuvicorn --reload
instead.
Deprecated items from 0.13:
- BREAKING:
.client
attribute onApp
andRecipe
was removed. Please usebocadillo.create_client
instead.
v0.13.3 - 2019-04-17
- Install all extra features (
sessions
andfiles
) usingpip install bocadillo[full]
.
v0.13.2 - 2019-04-13
- Update Jinja dependency to 2.10.1+.
v0.13.1 - 2019-03-19
- WebSocket auto-accept: in order to reduce boilerplate for common use cases, WebSocket endpoints now automatically
.accept()
and.close()
the connection (or, equivalently, enter withasync with ws:
block). This is backwards compatible: enteringasync with ws:
has no effect if the connection has already been accepted. Revert to the old behavior by passingauto_accept=False
to@app.websocket_route()
.
v0.13.0 - 2019-03-16
Features:
- Providers: explicit, modular and flexible runtime dependency injection system, inspired by pytest fixtures. Implemented via aiodine.
- Server-Sent Event support:
- Define an event stream with
@res.event_stream
. - Format SSE messages with
server_event
.
- Define an event stream with
- Cookie-based sessions: set the
SECRET_KEY
environment variable, and access/modify viareq.session
. - New base class for ASGI middleware:
ASGIMiddleware
. In the docs, old-style ASGI middleware has been rebranded as "pure" ASGI middleware. - Testing helpers:
create_client
,LiveServer
. - Add an
override_env
utility context manager.
Documentation:
- Testing guide.
- How-to guide on integrating with pytest.
- Chatbot server tutorial.
- HTTP middleware classes can now expect both the
inner
middleware and theapp
instance to be passed as positional arguments, instead of onlyinner
. This allows to perform initialisation on theapp
in the middleware's__init__()
method. The same goes for the newASGIMiddleware
base class.
- Stream responses (and SSE streams by extension) now stop as soon as a client disconnects. For custom disconnect handling, use
raise_on_disconnect=True
and handlebocadillo.request.ClientDisconnect
yourself. - ASGI middleware is now applied even when the request is routed to a sub-application (e.g. a recipe). In the past, this could lead to CORS headers not being added on a recipe despite them being configured on the root application.
app.client
has been deprecated in favor of thecreate_client
testing helper, and will be removed in v0.14. For pytest users, consider building and using aclient
fixture in your tests:
# tests.py
import pytest
from bocadillo.testing import create_client
from myproject import app
@pytest.fixture
def client():
return create_client(app)
def test_stuff(client):
...
Deprecated items from 0.12:
- BREAKING: the
API
class was removed. UseApp
now. - Breaking: the
template*
methods onApp
no longer exists. Use theTemplates
helper instead.
v0.12.6 - 2019-03-09
- Missing
headers
andquery_params
attributes onWebSocket
.
v0.12.5 - 2019-03-06
- A bug from v0.12.4 disallowed the creation of an application in a Python interpreter. This has been fixed.
v0.12.4 - 2019-03-05
- Add support for uvicorn 0.5.x.
- Activate debug mode via the
BOCADILLO_DEBUG
environment variable.
- When launching the application script in debug mode, hot reload was activated but it did not actually reload the application in case of changes. This has been fixed. Caveat: the application should be declared as
app
in the application script, but this can be overridden via thedeclared_as
parameter toApp.run
.
v0.12.3 - 2019-03-04
- Hot fix: pin Uvicorn to <0.5 while we investigate compatibility with 0.5+.
v0.12.2 - 2019-03-01
- Pass extra WhiteNoise configuration attributes using
App(static_config=...)
.
- Changes to static files are now picked up in debug mode.
v0.12.1 - 2019-02-28
- Installing from
pip
now checks that Python 3.6+ is installed.
v0.12.0 - 2019-02-22
This release contains replacements for important features (API
, app-level template rendering). Their old usage has been deprecated but is still available until the next minor release.
This means that there shouldn't be any breaking changes. If you do experiment breaking changes, please report them to us!
- API reference for the
Response
andRequest
classes. - Browser-downloadable responses (a.k.a attachments) with
res.attachment
. This is a handy shortcut for setting theContent-Disposition
header. - (Asynchronous) file responses with
res.file()
. - Generic templating with
bocadillo.templates.Templates
.
- The main application class is now called
App
.API
is still available as an alias. - Content types are now available on the
bocadillo.constants.CONTENT_TYPE
enum, instead ofbocadillo.media.Media
. - Recipes are now just apps: they expose all the parameters, attributes and methods that an
App
exposes.
Response.text
andResponse.html
are now proper write-only Python properties, which should be more friendly with type checkers.
API
has been deprecated in favor ofApp
. It will be removed in v0.13.0.- The
.template
,.template_sync
and.template_string
methods onApp
andRecipe
have been deprecated in favor of generic templating. They will be removed in v0.13.0.
- The
handle_text
media handler has been removed due to howResponse.text
andResponse.html
now work.
v0.11.2 - 2019-02-21
- Using
await req.form()
previously required to install a third-party library,python-multipart
, which is now bundled by default.
v0.11.1 - 2019-02-19
- Fixed a bug that caused import errors when using Starlette < 0.11.
v0.11.0 - 2019-02-11
- A parser for URL patterns used to be compiled on every call to a route. The parser is now compiled once and for all on startup. As a result, URL matching is slightly faster.
- New colors and logo for the docs site.
- Various documentation improvements.
- BREAKING: Boca was moved to a separate package: boca. It can be installed from PyPI using
pip install boca
and does not come installed with Bocadillo by default.
v0.10.3 - 2019-02-02
- Better documentation about route parameters, including how to implement wildcard matching.
- Previously, it was not possible to create a catch-all route using the pattern
{}
because a leading slash was automatically added, preventing the pattern from matching a request to the root URL/
. This has been fixed!
v0.10.2 - 2019-01-27
- Recipes now support redirections, e.g.
recipe.redirect(name="recipe:foo")
.
- Using
url_for()
in a template rendered from a recipe (e.g.await recipe.template()
) used to raise anUndefinedError
. This has been fixed.
v0.10.1 - 2019-01-21
- Now requires
uvicorn>=0.3.26
.
- Fixed a bug that caused an
ImportError
when importing frombocadillo.api
usinguvicorn<0.3.26
.
v0.10.0 - 2019-01-17
- In-browser traceback of unhandled exceptions when running with
debug=True
. - Various documentation improvements and additions, e.g. databases discussion and Tortoise ORM how-to.
- The
before_dispatch
hook on HTTP middleware classes now takes aResponse
as second argument. - The
bocadillo.exceptions
module has been removed:WebSocketDisconnect
has moved tobocadillo.websockets
.UnsupportedMediaType
has moved tobocadillo.media
.HTTPError
has moved tobocadillo.errors
(but is still available at the top level:from bocadillo import HTTPError
).
- Other internal refactoring that should not affect framework users.
- Discussions are now in a separate section in the documentation.
- Even if an error handler was registered for a given exception class, Bocadillo used to return a 500 error response. It will now honor what the error handler does to the
res
object, i.e. only returning a 500 error response if the error handler does so or if it re-raised the exception. - The
after_dispatch
hook on HTTP middleware classes is not called anymore if the inbound HTTP method is not supported by the view.
RoutingMiddleware
was removed as it had been deprecated since v0.8.
v0.9.1 - 2018-01-04
- Add missing
url
attribute onWebSocket
objects, which prevented accessing information about the URL from WebSocket views.
v0.9.0 - 2018-01-03
This release has breaking API changes due to an overhaul of the view system.
If your application uses the features below, you are most likely affected and should review these changes thoroughly before upgrading:
- Use hooks via
@api.before()
or@api.after()
. - Restriction of HTTP methods via the
methods
parameter to@api.route()
.
- Support for WebSockets, including routing with
@api.websocket_route()
. - Send a chunk-encoded response with
res.chunked = True
. - Support for request and response streaming with
async for chunk in req
and@res.stream
. - View definition utilities:
from_handler()
,from_obj()
,@view()
. - In particular, the
@view()
decorator (available asfrom bocadillo import view
) accepts amethods
argument originally used by@api.route()
. Plus, passing theall
built-in has the same effect as defining.handle()
on the analogous class-based view — i.e. supporting all HTTP methods. - Function-based views are automatically decorated with
@view()
to ensure backwards compatibility.
from bocadillo import API, view
api = API()
# This:
@api.route("/")
async def index(req, res):
pass
# Is equivalent to:
@api.route("/")
@view()
async def index(req, res):
pass
# Which is equivalent to:
@api.route("/")
@view(methods=["get"])
async def index(req, res):
pass
# Which is itself *strictly* equivalent to:
@api.route("/")
class Index:
async def get(self, req, res):
pass
- API reference for the
views
module. - Various documentation additions and improvements.
- BREAKING: hooks were moved to a separate module:
bocadillo.hooks
. You must now use@hooks.before()
/@hooks.after()
instead of@api.before()
/@api.after()
and@recipe.before()
/@recipe.after()
. - BREAKING: hooks must now be placed right above the view being decorated. This affects both function-based views and class-based views (but not method views).
from bocadillo import API, hooks
api = API()
async def before(req, res, params):
print("before!")
# < 0.9
@api.before(before)
@api.route("/")
async def foo(req, res):
pass
@api.before(before)
@api.route("/")
class Foo:
pass
# >= 0.9:
@api.route("/")
@hooks.before(before)
async def foo(req, res):
pass
@api.route("/")
@hooks.before(before)
class Foo:
pass
- BREAKING: the
methods
argument to@api.route()
has been removed. To specify allowed methods on function-based views, you must now use the@view()
decorator — see below.
from bocadillo import API, view
api = API()
# < 0.9
@api.route("/", methods=["post"])
async def foo(req, res):
pass
# >= 0.9
@api.route("/")
@view(methods=["post"])
async def foo(req, res):
pass
- Removed dependency on
async_generator
.
v0.8.1 - 2018-12-27
await req.json()
now returns a400 Bad Request
error response if the input JSON is malformed, which allows to skip handling theJSONDecodeError
manually.
v0.8.0 - 2018-12-26
- Show Bocadillo version using
boca -v/-V/--version/version
. boca
is now accessible by running Bocadillo as a module:python -m bocadillo
.HTTPError
is now available at package level:from bocadillo import HTTPError
.- Built-in
HTTPError
handlers:error_to_html
,error_to_media
,error_to_text
. detail
argument toHTTPError
.- Startup and shutdown events with
api.on()
. - Security guide.
- Deployment guide.
api.run()
now accepts extra keyword arguments that will be passed touvicorn.run()
.- API reference for all public functionality.
- Exceptions raised in middleware callbacks were always handled by the HTML
HTTPError
handler. If configured, the one on theAPI
will now be used instead. - The default
HTTPError
handler now returns plaintext instead of HTML. - The
static
module was renamed tostaticfiles
. - The
types
module was renamed toapp_types
. - The
view
module was renamed toviews
. - The
routing
package has been flattened into a singlerouting
module.
- Serving static files from a non-existing directory (including the default one) used to raise an invasive warning. It has been silenced.
- Removed example application.
- Removed dependency on
asgiref
for WSGI sub-apps.
v0.7.0 - 2018-12-13
- Recipes: a way to group stuff together and allow composition of bocadillos.
- Recipe books: a way to group multiple recipes into a single recipe.
- Route namespaces via
namespace
argument to@api.route()
. - Add GZip support through
enable_gzip
. - Add ASGI-compliant middleware support via
api.add_asgi_middleware()
. - Background tasks via
res.background
.
- Exceptions raised in
before_dispatch()
andafter_dispatch()
middleware callbacks will now always lead to 500 error responses — they won't be handled by error handlers anymore, because these are registered on theAPI
which middleware only wrap around. The only exception to this is, of course,HTTPError
. - All routes now have an inferred
name
based on their function or class name. Explicit route naming is still possible. - Because of the above, names of routes in recipes now use the recipe's name as a namespace, i.e.
recipe_name:route_name
instead ofroute_name
. - Unsafe HTTP verbs used to be supported by defaults on function-based routes. Only the safe ones, GET and HEAD, are supported by default now.
RoutingMiddleware
has been renamed toMiddleware
. It will still be available asRoutingMiddleware
until v0.8.
- Errors returned by custom error handlers could have 200 status in case the handler did not set any status code. It now defaults to 500.
- If
GET
is supported,HEAD
will automatically be implemented.
v0.6.1 - 2018-12-04
- Documentation on the routing algorithm.
- More documentation on how to write views.
- API reference for the
API
class.
- Restructure documentation into 4 clear sections: Getting Started, Topics, How-To and API Reference.
- All things related to routing are now in a dedicated
bocadillo.routing
package, which provides a reusableRoutingMixin
. This does not introduce any API changes. - Code refactoring for the hooks and templates features. No API changes involved.
- Rewritten
CONTRIBUTING.md
.
v0.6.0 - 2018-11-26
- Route hooks via
@api.before()
and@api.after()
. - Media types and media handlers:
API([media_type='application/json'])
,api.media_type
,api.media_handlers
. - Support for async callbacks on
RoutingMiddleware
. - Documentation for the above.
- (Development) Black auto-formatting with pre-commit.
- (Development) Documentation guide in
CONTRIBUTING.md
.
- Documentation improvements.
- Exceptions raised inside a middleware callback
(
before_dispatch()
orafter_dispatch()
) are now properly handled by registered error handlers (they were previously left uncaught). - Middleware callbacks (especially
before_dispatch()
) won't be called anymore if the HTTP method is not allowed.
v0.5.0 - 2018-11-18
- Add
boca
, Bocadillo's extensible CLI. - Add
init:custom
command to generate files for building custom Boca commands. - Add VuePress-powered documentation site.
- Moved docs from README.md to docs site.
v0.4.0 - 2018-11-10
- Named routes. Define a named route by passing a
name
to@api.route()
. Get the URL path to a route usingapi.url_for()
or, in templates, theurl_for()
global. - Redirections using
api.redirect()
. Can be by route name, internal URL, or external URL. Make it permanent withpermanent=True
. - Template rendering from string using
api.template_string()
. - Add allowed hosts configuration through
allowed_host
argument toAPI()
. - Experimental support for routing middleware through
bocadillo.RoutingMiddleware
. - Add CORS support with restrictive defaults. Enable using
enable_cors = True
, configure throughcors_config
. - Add HSTS support through
enable_hsts
.
- Updated example app to demonstrate usage of redirects and named routes.
- Responses without content do not send an empty JSON object response anymore. Instead, an empty
text/plain
response is sent. - Responses with 204 status code and no content do not set the
Content-Type
header anymore.
v0.3.1 - 2018-11-09
- Fixed mis-configured
setup.py
preventing Bocadillo from being installed frompip
.
v0.3.0 - 2018-11-09
- Plain text responses using
res.text
. - HTML responses using
res.html
. - Jinja2-powered template rendering through
await api.template()
andapi.template_sync()
. - Mount ASGI or WSGI sub-apps using
app.mount(prefix, sub_app)
. - Static assets using WhiteNoise. Configurable through the
static_root
andstatic_dir
arguments toAPI()
. By default, thestatic
folder is served at/static
. This can be disabled by passingstatic_root = None
toAPI()
. - Register more static files locations by mounting a
bocadillo.static()
sub-app. - Check (at "compile time") that a) a route pattern begins with a forward slash, and b) all parameters of a route are used on its view and vice-versa.
- Use
text/plain
content type if none was set within a view.
- Example app in a dedicated
example/
folder. - Allow overriding a route by reusing a route pattern. Previously, this would have raised an exception.
- Default static root is now
/static
. It previously defaulted to the static directory, which causes issues if the latter was not a relative path. - The
res.content
attribute is now for raw response content, and will not set thetext/plain
content type anymore. Allows to send responses of arbitrary content type. - The default error handler now sends HTML content instead of plain text.
v0.2.1 - 2018-11-04
- Add this
CHANGELOG.md
. - Add error handling.
- Provide a default HTTP error handler, which catches
HTTPError
exceptions during request processing and returns the appropriate HTTP response. - Allow to customize error handling through
@api.error_handler()
andapi.add_error_handler()
. - Allow to restrict HTTP methods supported by a route using the
methods
argument to@api.route()
. Ignored for class-based views: HTTP methods should be restricted by implementing/not implementing the corresponding method on the class.
- Return a
405 Method Not Allowed
response when trying to use a non-implemented method on a class-based view. The previous behavior was to raise an uncaughtValueError
. - Updated
example.py
.
- Fixed a bug that prevented routes without parameters to be handled correctly.
- Prevent registering multiple routes on the same pattern.
- The
API
class, an ASGI-compatible application. Request
andResponse
objects, which are wrappers around Starlette's.- Plain text responses using
res.content
. - JSON responses through
res.media
. - Automatic configuration of the response's
Content-Type
:text/plain
by default,application/json
ifresponse.media
was set orres.content
was left empty. - Route registration through
@api.route()
. - Parametrized routes through f-string expressions, e.g.
{my_param}
. Parameters are passed directly to the view, e.g.my_view(req, resp, my_param)
. Parameters are compliant with the Literal string interpolation specification. In particular, type specifiers are supported (e.g.{age:d}
) which provides basic validation capabilities. - Class-based views. HTTP methods (GET, POST, PUT, PATCH, DELETE) are mapped to the corresponding lowercase methods on the class, e.g.
.get()
. A generic.handle()
method can also be given to process any request (other methods will then be ignored). - Default bind host and port:
127.0.0.1:8000
. - Automatic host and port based on the
PORT
environment variable. IfPORT
is set, a) the app will bind on that port, b) if no host was specified, the app will bind to known hosts (i.e.0.0.0.0
). example.py
app.README.md
.CONTRIBUTING.md
.