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
websocketslibrary - @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_MODULESsetting.
Terminated deprecations:
- BREAKING Recipes were removed. This includes the
RecipeandRecipeBookclasses, as well asapp.recipe(). Consider using routers instead. - BREAKING
app.add_asgi_middleware()was removed. Please useapp.add_middleware()instead. - BREAKING
@viewwas removed. Please use@route(methods=...)instead. - BREAKING
@pluginwas removed. Register plugins using thePLUGINSsetting 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
:pathconverter (e.g./images/{filename:path}) to match path-like route parameters.
v0.16.0 - 2019-06-01
- Reusable and composable routes using
Routerandapp.include_router(). - Register plugins using the new
PLUGINSsetting. 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 = Falseto disable this behavior.
- HTTP middleware is now also called when a request is routed to a sub-application.
- The
Middlewareclass now also implement the ASGI interface. Mostly an implementation detail. - Methods in
ALL_HTTP_METHODSare now lower-cased instead of upper-cased.
@pluginis deprecated in favor of using thePLUGINSsetting. The decorator now raises aDeprecationWarningand 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.mediawas deprecated since 0.14.0, and has been removed. Please useres.jsoninstead.
v0.15.1 - 2019-05-17
- Improved documentation on custom templates directories.
- The
appargument toTemplatesis 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
ASGIMiddlewarebase class. Please create plain ASGI3-compliant classes instead. - BREAKING The Bocadillo
Appis not passed to HTTPMiddlewareanymore. 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
HTTPErrorhandler 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.settingslazy object, app configuration usingbocadillo.configure(app[, settings][, **kwargs]). - Plugin mechanism: register a
(app: App) -> Nonecallable using@bocadillo.plugin, and use the new lazysettingsobject to perform plugin setup. - Build a full URL for a
LiveServerusingserver.url("/path"). Note:server.urlstill gives access to the root live server URL. create_clientandLiveServercan be imported frombocadilloinstead ofbocadillo.testing.
Documentation:
- New how-to guide on integrating with the
ormasync 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
HTTPErrorwhich will be processed by the registeredHTTPErrorhandler. - 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_rootandstatic_config. Use theSTATIC_DIR,STATIC_ROOTandSTATIC_CONFIGsettings instead. - BREAKING Removed
allowed_hosts. Use theALLOWED_HOSTSsetting instead. - BREAKING Removed
enable_sessionsandsessions_config. Use theSESSIONSsetting instead. - BREAKING Removed
enable_corsandcors_config. Use theCORSsetting instead. - BREAKING Removed
enable_hsts. Use theHSTSsetting instead. - BREAKING Removed
enable_gzipandgzip_min_size. Use theGZIPandGZIP_MIN_SIZEsettings 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 onApphas been removed in favor of theuvicorncommand 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:appinstead 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
Appdoes not accept adebugparameter anymore. To enable hot reload, please useuvicorn --reloadinstead.
Deprecated items from 0.13:
- BREAKING:
.clientattribute onAppandRecipewas removed. Please usebocadillo.create_clientinstead.
v0.13.3 - 2019-04-17
- Install all extra features (
sessionsandfiles) 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=Falseto@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_KEYenvironment 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_envutility context manager.
Documentation:
- Testing guide.
- How-to guide on integrating with pytest.
- Chatbot server tutorial.
- HTTP middleware classes can now expect both the
innermiddleware and theappinstance to be passed as positional arguments, instead of onlyinner. This allows to perform initialisation on theappin the middleware's__init__()method. The same goes for the newASGIMiddlewarebase class.
- Stream responses (and SSE streams by extension) now stop as soon as a client disconnects. For custom disconnect handling, use
raise_on_disconnect=Trueand handlebocadillo.request.ClientDisconnectyourself. - 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.clienthas been deprecated in favor of thecreate_clienttesting helper, and will be removed in v0.14. For pytest users, consider building and using aclientfixture 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
APIclass was removed. UseAppnow. - Breaking: the
template*methods onAppno longer exists. Use theTemplateshelper instead.
v0.12.6 - 2019-03-09
- Missing
headersandquery_paramsattributes 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_DEBUGenvironment 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
appin the application script, but this can be overridden via thedeclared_asparameter 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
pipnow 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
ResponseandRequestclasses. - Browser-downloadable responses (a.k.a attachments) with
res.attachment. This is a handy shortcut for setting theContent-Dispositionheader. - (Asynchronous) file responses with
res.file(). - Generic templating with
bocadillo.templates.Templates.
- The main application class is now called
App.APIis still available as an alias. - Content types are now available on the
bocadillo.constants.CONTENT_TYPEenum, instead ofbocadillo.media.Media. - Recipes are now just apps: they expose all the parameters, attributes and methods that an
Appexposes.
Response.textandResponse.htmlare now proper write-only Python properties, which should be more friendly with type checkers.
APIhas been deprecated in favor ofApp. It will be removed in v0.13.0.- The
.template,.template_syncand.template_stringmethods onAppandRecipehave been deprecated in favor of generic templating. They will be removed in v0.13.0.
- The
handle_textmedia handler has been removed due to howResponse.textandResponse.htmlnow 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 bocaand 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
ImportErrorwhen importing frombocadillo.apiusinguvicorn<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_dispatchhook on HTTP middleware classes now takes aResponseas second argument. - The
bocadillo.exceptionsmodule has been removed:WebSocketDisconnecthas moved tobocadillo.websockets.UnsupportedMediaTypehas moved tobocadillo.media.HTTPErrorhas 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
resobject, i.e. only returning a 500 error response if the error handler does so or if it re-raised the exception. - The
after_dispatchhook on HTTP middleware classes is not called anymore if the inbound HTTP method is not supported by the view.
RoutingMiddlewarewas removed as it had been deprecated since v0.8.
v0.9.1 - 2018-01-04
- Add missing
urlattribute onWebSocketobjects, 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
methodsparameter 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 reqand@res.stream. - View definition utilities:
from_handler(),from_obj(),@view(). - In particular, the
@view()decorator (available asfrom bocadillo import view) accepts amethodsargument originally used by@api.route(). Plus, passing theallbuilt-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
viewsmodule. - 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
methodsargument 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 Requesterror response if the input JSON is malformed, which allows to skip handling theJSONDecodeErrormanually.
v0.8.0 - 2018-12-26
- Show Bocadillo version using
boca -v/-V/--version/version. bocais now accessible by running Bocadillo as a module:python -m bocadillo.HTTPErroris now available at package level:from bocadillo import HTTPError.- Built-in
HTTPErrorhandlers:error_to_html,error_to_media,error_to_text. detailargument 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
HTTPErrorhandler. If configured, the one on theAPIwill now be used instead. - The default
HTTPErrorhandler now returns plaintext instead of HTML. - The
staticmodule was renamed tostaticfiles. - The
typesmodule was renamed toapp_types. - The
viewmodule was renamed toviews. - The
routingpackage has been flattened into a singleroutingmodule.
- 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
asgireffor 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
namespaceargument 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 theAPIwhich middleware only wrap around. The only exception to this is, of course,HTTPError. - All routes now have an inferred
namebased 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_nameinstead 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.
RoutingMiddlewarehas been renamed toMiddleware. It will still be available asRoutingMiddlewareuntil 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
GETis supported,HEADwill 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
APIclass.
- 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.routingpackage, 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:customcommand 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
nameto@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_hostargument 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/plainresponse is sent. - Responses with 204 status code and no content do not set the
Content-Typeheader anymore.
v0.3.1 - 2018-11-09
- Fixed mis-configured
setup.pypreventing 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_rootandstatic_dirarguments toAPI(). By default, thestaticfolder is served at/static. This can be disabled by passingstatic_root = NonetoAPI(). - 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/plaincontent 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.contentattribute is now for raw response content, and will not set thetext/plaincontent 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
HTTPErrorexceptions 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
methodsargument 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 Allowedresponse 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
APIclass, an ASGI-compatible application. RequestandResponseobjects, 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/plainby default,application/jsonifresponse.mediawas set orres.contentwas 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
PORTenvironment variable. IfPORTis 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.pyapp.README.md.CONTRIBUTING.md.