Changelog for Falcon 3.0.0#
Summary#
We are pleased to present Falcon 3.0, a major new release that includes
ASGI-based
asyncio
and WebSocket
support, fantastic multipart/form-data parsing, better error handling, enhancements to existing features, and the
usual assortment of bug fixes.
This is easily the biggest release—in terms of both hours volunteered and code contributed—that we have ever done. We sincerely thank our stupendous group of 38 contributors who submitted pull requests for this release, as well as all those who have generously provided financial support to the project.
When we began working on this release, we knew we wanted to not only evolve the
framework’s existing features, but also to deliver first-class, user-friendly
asyncio
support alongside our existing WSGI
feature
set.
On the other hand, we have always fought the temptation to expand Falcon’s
scope, in order to leave room for community projects and standards to innovate
around common, self-contained capabilities. And so when ASGI arrived on the scene, we saw it as
the perfect opportunity to deliver long-requested asyncio
and
WebSocket
features while still encouraging
sharing and reuse within the Python web community.
It can be painful to migrate a large code base to a major new version of a framework. Therefore, in 3.0 we went to great lengths to minimize breaking changes, although a number of methods and attributes were deprecated. That being said, everyone will likely run up against at least one or two items in the breaking changes list below. Please carefully review the list of changes and thoroughly test your apps with Falcon 3.0 before deploying to production.
Leading up to this release, members of the core maintainers team spent many hours (and not a few late nights and weekends) prototyping, tuning, and testing in order to uphold the high standards of correctness and reliability for which Falcon is known. That being said, no code is perfect, so please don’t hesitate to reach out on falconry/user or GitHub if you run into any issues.
Again, thanks so much to everyone who supported this release! Over the years we like to think that our little framework has had a positive impact on the Python community, and has even helped nudge the state of the art forward. And it is all thanks to our amazing supporters and contributors.
Changes to Supported Platforms#
Python 3.8 and 3.9 are now fully supported.
Python 3.6+ is only required when using the new ASGI interface. WSGI is still supported on Python 3.5+.
Python 3.5 support is deprecated and may be removed in the next major release.
Python 3.4 is no longer supported.
The Falcon 2.x series was the last to support Python language version 2. As a result, support for CPython 2.7 and PyPy2.7 was removed in Falcon 3.0.
Breaking Changes#
The class
OptionalRepresentation
and the attributehas_representation
were deprecated. The default error serializer now generates a representation for every error type that derives fromfalcon.HTTPError
. In addition, Falcon now ensures that any previously set response body is cleared before handling any raised exception. (#452)The class
NoRepresentation
was deprecated. All subclasses offalcon.HTTPError
now have a media type representation. (#777)In order to reconcile differences between the framework’s support for WSGI vs. ASGI, the following breaking changes were made:
falcon.testing.create_environ()
previously set a default User-Agent header, when one was not provided, to the value'curl/7.24.0 (x86_64-apple-darwin12.0)'
. As of Falcon 3.0, the default User-Agent string is nowf'falcon-client/{falcon.__version__}'
. This value can be overridden for the sake of backwards-compatibility by settingfalcon.testing.helpers.DEFAULT_UA
.The
falcon.testing.create_environ()
function’s protocol keyword argument was renamed to http_version and now only includes the version number (the value is no longer prefixed with'HTTP/'
).The
falcon.testing.create_environ()
function’s app keyword argument was renamed to root_path.The writeable property of
falcon.stream.BoundedStream
was renamed to writable per the standard file-like I/O interface (the old name was a misspelling)If an error handler raises an exception type other than
falcon.HTTPStatus
orfalcon.HTTPError
, remaining middleware process_response methods will no longer be executed before bubbling up the unhandled exception to the web server.falcon.get_http_status()
no longer accepts floats, and the method itself is deprecated.falcon.app_helpers.prepare_middleware()
no longer accepts a single object; the value that is passed must be an iterable.falcon.Request.access_route
now includes the value of theremote_addr
property as the last element in the route, if not already present in one of the headers that are checked.When the
'REMOTE_ADDR'
field is not present in the WSGI environ, Falcon will assume'127.0.0.1'
for the value, rather than simply returningNone
forfalcon.Request.remote_addr
.
The changes above were implemented as part of the ASGI+HTTP work stream. (#1358)
Header-related methods of the
Response
class no longer coerce the passed header name to a string viastr()
. (#1497)An unhandled exception will no longer be raised to the web server. Rather, the framework now installs a default error handler for the
Exception
type. This also means that middleware process_response methods will still be called in this case, rather than being skipped as previously. The new default error handler simply generates an HTTP 500 response. This behavior can be overridden by specifying your own error handler forException
viaadd_error_handler()
. (#1507)Exceptions are now handled by the registered handler for the most specific matching exception class, rather than in reverse order of registration. “Specificity” is determined by the method resolution order of the raised exception type. (See
add_error_handler()
for more details.) (#1514)The deprecated
stream_len
property was removed from theResponse
class. Please useset_stream()
orcontent_length
instead. (#1517)If
RequestOptions.strip_url_path_trailing_slash
is enabled, routes should now be added without a trailing slash. Previously, the trailing slash was always removed as a side effect of a bug regardless of thestrip_url_path_trailing_slash
option value. See also: How does Falcon handle a trailing slash in the request path? (#1544)Rename
falcon.Response.body
andfalcon.HTTPStatus.body
totext
. The old name is deprecated, but still available. (#1578)Referencing the class
falcon.stream.BoundedStream
through thefalcon.request_helpers
module is deprecated. It is now accessible from the modulefalcon.stream
. (#1583)General refactoring of internal media handler:
Deserializing an empty body with a handler that does not support it will raise
falcon.MediaNotFoundError
, and will be rendered as a400 Bad Request
response. This error may be suppressed by passing a default value toget_media
to be used in case of empty body. See alsofalcon.Request.get_media()
for details. PreviouslyNone
was returned in all cases without calling the handler.Exceptions raised by the handlers are wrapped as
falcon.MediaMalformedError
, and will be rendered as a400 Bad Request
response.Subsequent calls to
falcon.Request.get_media()
orfalcon.Request.media
will re-raise the same exception, if the first call ended in an error, unless the exception was afalcon.MediaNotFoundError
and a default value is passed to thedefault_when_empty
attribute of the current invocation. PreviouslyNone
was returned.
External handlers should update their logic to align to the internal Falcon handlers. (#1589)
The
falcon.Response.data
property now just simply returns the same data object that it was set to, if any, rather than also checking and serializing the value of thefalcon.Response.media
property. Instead, a newrender_body()
method has been implemented, which can be used to obtain the HTTP response body for the request, taking into account thetext
,data
, andmedia
attributes. (#1679)The
params_csv
parameter now defaults toFalse
infalcon.testing.simulate_request()
. The change was made to match the default value of the request optionauto_parse_qs_csv
(False
since Falcon 2.0). (#1730)The
falcon.HTTPError.to_json()
now returnsbytes
instead ofstr
. Importingjson
fromfalcon.util
is deprecated. (#1767)The private attributes for
JSONHandler
were renamed, and the private attributes used byMessagePackHandler
were replaced. Subclasses that refer to these variables will need to be updated. In addition, the undocumentedfalcon.media.Handlers.find_by_media_type()
method was deprecated and may be removed in a future release. (#1822)
New & Improved#
ASGI+WebSocket support was added to the framework via
falcon.asgi.App
andfalcon.asgi.WebSocket
. (#321)The error classes in
falcon.errors
were updated to have thetitle
anddescription
keyword arguments and to correctly handle headers passed as list of tuples (#777)MultipartFormHandler
was added to enable support for multipart forms (of content typemultipart/form-data
) throughfalcon.Request.get_media()
. (#953)The
falcon.Response.status
attribute can now be also set to anhttp.HTTPStatus
instance, an integer status code, as well as anything supported by thefalcon.code_to_http_status()
utility method. (#1135)A new kwarg,
cors_enable
, was added to thefalcon.App
initializer.cors_enable
can be used to enable a simple blanket CORS policy for all responses. (See also: CORS.) (#1194)ASGI+HTTP support was added to the framework via a new class,
falcon.asgi.App
. The testing module was also updated to fully support ASGI apps, including two new helper functions:falcon.testing.create_scope()
andfalcon.testing.create_asgi_req()
. WSGI users also get a newfalcon.testing.create_req()
method. As part of the ASGI work, several additional utility functions were added, includingfalcon.is_python_func()
,falcon.http_status_to_code()
andfalcon.code_to_http_status()
; as well as sync/async helpersfalcon.get_running_loop()
,falcon.create_task()
,falcon.sync_to_async()
,falcon.wrap_sync_to_async()
, andfalcon.wrap_sync_to_async_unsafe()
. (#1358)The
falcon.App
class initializer now supports a new argumentsink_before_static_route
(defaultTrue
, maintaining 2.0 behavior) to specify ifsinks
should be handled before or afterstatic routes
. (#1372)The
falcon.Response.append_link()
method now supports setting the crossorigin link CORS settings attribute. (#1410)Falcon now supports all WebDAV methods (RFC 2518 and RFC 4918), such as COPY, LOCK, MKCOL, MOVE, PROPFIND, PROPPATCH and UNLOCK. (#1426)
Added inspect module to collect information about an application regarding the registered routes, middleware, static routes, sinks and error handlers (See also: Inspect Module.) (#1435)
WSGI path decoding in
falcon.Request
was optimized, and is now significantly faster than in Falcon 2.0. (#1492)The
set_headers()
method now accepts an instance of any dict-like object that implements anitems()
method. (#1546)Change
falcon.routing.CompiledRouter
to compile the routes only when the first request is routed. This can be changed by passingcompile=True
tofalcon.routing.CompiledRouter.add_route()
. (#1550)The
set_cookie()
method now supports setting the SameSite cookie attribute. (#1556)The
falcon.API
class was renamed tofalcon.App
. The oldAPI
class remains available as an alias for backwards-compatibility, but it is now considered deprecated and will be removed in a future release. (#1579)URLEncodedFormHandler
was added to enable support for URL-encoded forms (of content typeapplication/x-www-form-urlencoded
) throughfalcon.Request.get_media()
. Theauto_parse_form_urlencoded
option is now deprecated in favor ofURLEncodedFormHandler
. (See also: How can I access POSTed form params?). (#1580)get_param_as_bool()
now supports the use of't'
and'y'
values forTrue
, as well as'f'
and'n'
forFalse
. (#1606)falcon.testing.simulate_request()
now accepts a content_type keyword argument. This provides a more convenient way to set this common header vs. the headers argument. (#1646)When no route matches a request, the framework will now raise a specialized subclass of
HTTPNotFound
(HTTPRouteNotFound
) so that a custom error handler can distinguish that specific case if desired. (#1647)Default media handlers
were simplified by removing a separate handler for the now-obsoleteapplication/json; charset=UTF-8
. As a result, providing a custom JSON media handler will now unambiguously cover bothapplication/json
and the formerContent-type
. (#1717)
Fixed#
Previously, the default
CompiledRouter
was erroneously stripping trailing slashes from URI templates. This has been fixed so that it is now possible to add two different routes for a path with and without a trailing forward slash (see also:RequestOptions.strip_url_path_trailing_slash
). (#1544)falcon.uri.decode()
andfalcon.uri.parse_query_string()
no longer explode quadratically for a large number of percent-encoded characters. The time complexity of these utility functions is now always close to O(n). (#1594)When
auto_parse_qs_csv
is enabled, the framework now correctly parses all occurrences of the same parameter in the query string, rather than only splitting the values in the first occurrence. For example, whereas previouslyt=1,2&t=3,4
would become['1', '2', '3,4']
, now the resulting list will be['1', '2', '3', '4']
(#1597)The
parse_query_string()
utility function is now correctly parsing an empty string as{}
. (#1600)Previously, response serialization errors (such as in the case of a faulty custom media handler, or because an instance of
HTTPUnsupportedMediaType
was raised for an unsupported response content type) were unexpectedly bubbled up to the application server. This has been fixed, and these errors are now handled exactly the same way as other exceptions raised in a responder (see also: Error Handling). (#1607)falcon.Request.forwarded_host
now contains the port when proxy headers are not set, to make it possible to correctly reconstruct the URL when the application is not behind a proxy. (#1678)The
Response.downloadable_as
property is now correctly encoding non-ASCII filenames as per RFC 6266 recommendations. (#1749)The
falcon.routing.CompiledRouter
no longer mistakenly sets route parameters while exploring non matching routes. (#1779)The
to_query_str()
method now correctly encodes parameter keys and values. As a result, the params parameter insimulate_request()
will now correctly pass values containing special characters (such as'&'
) to the application. (#1871)falcon.uri.encode
andfalcon.uri.encode_value
now escape all percent characters by default even if it appears they have already been escaped. Thefalcon.uri.encode_check_escaped
andfalcon.uri.encode_value_check_escaped
methods have been added to give the option of retaining the previous behavior where needed. These new methods have been applied to thefalcon.Response.location
,falcon.Response.content_location
,falcon.Response.append_link()
attrs and methods to retain previous behavior. (#1872)Previously, methods marked with the
deprecated()
utility wrapper could raise an unexpectedAttributeError
when running under certain applications servers such as Meinheld. This has been fixed so thatdeprecated()
no longer relies on the availability of interpreter-specific stack frame instrospection capabilities. (#1882)
Misc#
Deprecate the use of positional arguments for the optional kw args of the
falcon.HTTPError
subclasses (#777)Setup towncrier to make CHANGES reporting much easier. (#1461)
Fix test errors on Windows (#1656)
A new method,
get_media()
, was added that can now be used instead of thefalcon.Request.media
property to make it more clear to app maintainers that getting the media object for a request involves a side-effect of consuming and deserializing the body stream. The original property remains available to ensure backwards-compatibility with existing apps. (#1679)Falcon now uses the
falcon.Response
media handlers when serializing to JSONfalcon.HTTPError
andfalcon.asgi.SSEvent
.falcon.Request
will use its defined media handler when loading a param as JSON withfalcon.Request.get_param_as_json()
. (#1767)The add_link() method of the
falcon.Request
class was renamed tofalcon.Response.append_link()
. The old name is still available as a deprecated alias. (#1801)
Contributors to this Release#
Many thanks to all of our talented and stylish contributors for this release!