Summary
-------
Many thanks to all of our awesome contributors (listed down below) who made
this release possible!
In 2.0 we added a number of new convenience methods and properties. We also
made it a lot cleaner and less error-prone to assign multiple routes to the
same resource class via suffixed responders.
Also noteworthy is the significant effort we invested in improving the
accuracy, clarity, and breadth of the docs. We hope these changes will help
make the framework easier to learn for newcomers.
Middleware methods can now short-circuit request processing, and we improved
cookie and ETag handling. Plus, the testing framework received several
improvements to make it easier to simulate certain types of requests.
As this is the first major release that we have had in quite a while, we have
taken the opportunity to clean up many parts of the framework. Deprecated
variables, methods, and classes have been removed, along with all
backwards-compatibility shims for old method signatures. We also changed the
defaults for a number of request options based on community feedback.
Please carefully review the list of breaking changes below to see what
you may need to tweak in your app to make it compatible with this release.
Changes to Supported Platforms
------------------------------
- CPython 3.7 is now fully supported.
- Falcon 2.x series is the last to support Python language version 2. As a
result, support for CPython 2.7 and PyPy2.7 will be removed in Falcon 3.0.
- Support for CPython 3.4 is now deprecated and will be removed in Falcon 3.0.
- Support for CPython 2.6, CPython 3.3 and Jython 2.7 has been dropped.
Breaking Changes
----------------
- Previously, several methods in the ``falcon.Response`` class
could be used to attempt to set raw cookie headers. However,
due to the Set-Cookie header values not being combinable
as a comma-delimited list, this resulted in an
incorrect response being constructed for the user agent in
the case that more than one cookie was being set. Therefore,
the following methods of ``falcon.Response`` now raise an
instance of ``ValueError`` if an attempt is made to use them
for Set-Cookie: ``set_header()``, ``delete_header()``,
``get_header()``, ``set_headers()``.
- ``testing.Result.json`` now returns ``None`` when the response body is
empty, rather than raising an error.
- ``Request.get_param_as_bool()`` now defaults to treating valueless
parameters as truthy, rather than falsy. ``None`` is still returned
by default when the parameter is altogether missing.
- ``Request.get_param_as_bool()`` no longer raises an error for a
valueless parameter when the ``blank_as_true`` keyword argument is ``False``.
Instead, ``False`` is simply returned in that case.
- ``RequestOptions.keep_blank_qs_values`` now defaults to ``True`` instead of
``False``.
- ``RequestOptions.auto_parse_qs_csv`` now defaults to ``False`` instead of
``True``.
- ``independent_middleware`` kwarg on ``falcon.API`` now defaults to ``True``
instead of ``False``.
- The deprecated ``stream_len`` property was removed from the ``Response``
class. Please use ``Response.set_stream()`` or ``Response.content_length``
instead.
- ``Request.context_type`` was changed from dict to a bare class implementing
the mapping interface.
- ``Response.context_type`` was changed from dict to a bare class implementing
the mapping interface.
- ``JSONHandler`` and ``HTTPError`` no longer use
`ujson` in lieu of the standard `json` library (when `ujson` is available in
the environment). Instead, ``JSONHandler`` can now be configured
to use arbitrary ``dumps()`` and ``loads()`` functions. If you
also need to customize ``HTTPError`` serialization, you can do so via
``API.set_error_serializer()``.
- The ``find()`` method for a custom router is now required to accept the
``req`` keyword argument that was added in a previous release. The
backwards-compatible shim was removed.
- All middleware methods and hooks must now accept the arguments as specified
in the relevant interface definitions as of Falcon 1.4. All
backwards-compatible shims have been removed.
- Custom error serializers are now required to accept the arguments as
specified by ``API.set_error_serializer()`` for the past few releases.
The backwards-compatible shim has been removed.
- An internal function, ``make_router_search()``, was removed from the
``api_helpers`` module.
- An internal function, ``wrap_old_error_serializer()``, was removed from the
``api_helpers`` module.
- In order to improve performance, the ``Request.headers`` and
``Request.cookies`` properties now return a direct reference to
an internal cached object, rather than making a copy each time. This
should normally not cause any problems with existing apps since these objects
are generally treated as read-only by the caller.
- ``Request.stream`` is no longer wrapped in a bounded stream when
Falcon detects that it is running on the wsgiref server. If you
need to normalize stream semantics between wsgiref and a production WSGI
server, ``Request.bounded_stream`` may be used instead.
- ``Request.cookies`` now gives precedence to the first value
encountered in the Cookie header for a given cookie name, rather than the
last.
- The ordering of the parameters passed to custom error handlers was adjusted
to be more intuitive and consistent with the rest of the framework::
# Before
def handle_error(ex, req, resp, params):
pass
# Falcon 2.0
def handle_error(req, resp, ex, params):
pass
See also: ``API.add_error_handler()``
- ``RequestOptions.strip_url_path_trailing_slash`` now defaults
to ``False`` instead of ``True``.
- The deprecated ``falcon.testing.TestCase.api`` property was removed.
- The deprecated ``falcon.testing.TestCase.api_class`` class variable was removed.
- The deprecated ``falcon.testing.TestBase`` class was removed.
- The deprecated ``falcon.testing.TestResource`` class was removed.
- The deprecated ``protocol`` property was removed from the
``Request`` class.
- The deprecated ``get_param_as_dict()`` method alias was removed from the
``Request`` class. Please use ``Request.get_param_as_json()``
instead.
- Routers were previously allowed to accept additional args and
keyword arguments, and were not required to use the variadic form. Now,
they are only allowed to accept additional options as variadic keyword
arguments, and to ignore any arguments they don't support. This helps
overridden router logic be less fragile in terms of their interface
contracts, which also makes it easier to keep Falcon backwards-compatible
in the face of any future changes in this area.
- ``API.add_route()`` previously accepted `*args`, but now no longer does.
- The ``add_route()`` method for custom routers no longer takes a `method_map`
argument. Custom routers should, instead, call the
``falcon.routing.map_http_methods()`` function directly
from their ``add_route()`` method if they require this mapping.
- The ``serialize()`` media handler method now receives an extra
`content_type` argument, while the ``deserialize()`` method now takes
`stream`, `content_type`, and `content_length` arguments, rather than a
single `raw` argument. The raw data can still be obtained by executing
``raw = stream.read()``.
- The deprecated ``falcon.routing.create_http_method_map()`` method was
removed.
- The keyword arguments for ``falcon.uri.parse_query_string()`` were renamed
to be more concise::
# Before
parsed_values = parse_query_string(
query_string, keep_blank_qs_values=True, parse_qs_csv=False
)
# Falcon 2.0
parsed_values = parse_query_string(
query_string, keep_blank=True, csv=False
)
- ``falcon.RequestOptions.auto_parse_qs_csv`` now defaults
to ``False`` instead of ``True``.
- The ``HTTPRequestEntityTooLarge`` class was renamed to
``HTTPPayloadTooLarge``.
- Two of the keyword arguments for ``Request.get_param_as_int()`` were
renamed to avoid shadowing built-in Python names::
# Before
dpr = req.get_param_as_int('dpr', min=0, max=3)
# Falcon 2.0
dpr = req.get_param_as_int('dpr', min_value=0, max_value=3)
- The ``falcon.media.validators.jsonschema.validate()`` decorator now uses
``functools.wraps()`` to make the decorated method look like the original.
- Previously, ``HTTPError`` instances for which the `has_representation`
property evaluated to ``False`` were not passed to custom error serializers
(such as in the case of types that subclass
``NoRepresentation``). This has now been fixed so
that custom error serializers will be called for all instances of
``HTTPError``.
- Request cookie parsing no longer uses the standard library
for most of the parsing logic. This may lead to subtly different results
for archaic cookie header formats, since the new implementation is based on
RFC 6265.
- The ``Request.if_match`` and ``Request.if_none_match`` properties
now return a list of ``falcon.ETag`` objects rather than the raw
value of the If-Match or If-None-Match headers, respectively.
- When setting the ``Response.etag`` header property, the value will
now be wrapped with double-quotes (if not already present) to ensure
compliance with RFC 7232.
- The default error serializer no longer sets the `charset` parameter for the
media type returned in the Content-Type header, since UTF-8 is the default
encoding for both JSON and XML media types. This should not break
well-behaved clients, but could impact test cases in apps that
assert on the exact value of the Content-Type header.
- Similar to the change made to the default error serializer, the default JSON
media type generally used for successful responses was also modified
to no longer specify the `charset` parameter.
This change affects both the ``falcon.DEFAULT_MEDIA_TYPE`` and
``falcon.MEDIA_JSON`` constants, as well
as the default value of the `media_type` keyword argument specified for
the ``falcon.API()`` initializer. This change also affects the default
value of the ``RequestOptions.default_media_type`` and
``ResponseOptions.default_media_type`` options.
New & Improved
--------------
- Several performance optimizations were made to hot code paths in the
framework to make Falcon 2.0 even faster than 1.4 in some cases.
- Numerous changes were made to the docs to improve clarity and to provide
better recommendations on how to best use various parts of the framework.
- Added a new ``headers`` property to the ``Response`` class.
- Removed the ``six`` and ``python-mimeparse`` dependencies.
- Added a new ``complete`` property to the ``Response`` class. This can be used
to short-circuit request processing when the response has been pre-constructed.
- ``Request.context_type`` now defaults to a bare class allowing to set
attributes on the request context object::
# Before
req.context['role'] = 'trial'
req.context['user'] = 'guest'
# Falcon 2.0
req.context.role = 'trial'
req.context.user = 'guest'
To ease the migration path, the previous behavior is supported by
implementing the mapping interface in a way that object attributes and
mapping items are linked, and setting one sets the other as well. However, as
of Falcon 2.0, the dict context interface is considered deprecated, and may
be removed in a future release.
- ``Response.context_type`` now defaults to a bare class allowing
to set attributes on the response context object::
# Before
resp.context['cache_strategy'] = 'lru'
# Falcon 2.0
resp.context.cache_strategy = 'lru'
To ease the migration path, the previous behavior is supported by
implementing the mapping interface in a way that object attributes and
mapping items are linked, and setting one sets the other as well. However, as
of Falcon 2.0, the dict context interface is considered deprecated, and may
be removed in a future release.
- ``JSONHandler`` can now be configured to use arbitrary
``dumps()`` and ``loads()`` functions. This enables support not only for
using any of a number of third-party JSON libraries, but also for
customizing the keyword arguments used when (de)serializing objects.
- Added a new method, ``get_cookie_values()``, to the ``Request``
class. The new method supports getting all values provided for a given
cookie, and is now the preferred mechanism for reading request cookies.
- Optimized request cookie parsing. It is now roughly an order of magnitude
faster.
- ``append_header()`` now supports appending raw Set-Cookie header values.
- Multiple routes can now be added for the same resource instance using a
suffix to distinguish the set of responders that should be used. In this way,
multiple closely-related routes can be mapped to the same resource while
preserving readability and consistency.
- The ``falcon.media.validators.jsonschema.validate()`` decorator now
supports both request and response validation.
- A static route can now be configured to return the data from a default file
when the requested file path is not found.
- The ordering of the parameters passed to custom error handlers was adjusted
to be more intuitive and consistent with the rest of the framework::
# Before
def handle_error(ex, req, resp, params):
pass
# Falcon 2.0
def handle_error(req, resp, ex, params):
pass
- All error classes now accept a `headers` keyword argument for customizing
response headers.
- A new method, ``get_param_as_float()``, was added to the
``Request`` class.
- A new method, ``has_param()``, was added to the
``Request`` class.
- A new property, ``content_length``, was added to the
``Response`` class. Either ``Response.set_stream()`` or
``Response.content_length`` should be used going forward, as
``stream_len`` is now deprecated.
- All ``get_param_*()`` methods of the ``Request`` class now accept a
`default` argument.
- A new header property, ``expires``, was added to the
``Response`` class.
- The ``falcon.routing.CompiledRouter`` class now exposes a
``map_http_methods()`` method that child
classes can override in order to customize the mapping of HTTP methods to
resource class methods.
- The ``serialize()`` media handler method now receives an extra
`content_type` argument, while the ``deserialize()`` method now takes
`stream`, `content_type`, and `content_length` arguments, rather than a
single `raw` argument. The raw data can still be obtained by executing
``raw = stream.read()``.
- The ``Response.get_header()`` method now accepts a `default` keyword
argument.
- The ``TestClient.simulate_request()`` method now supports
overriding the host and remote IP address in the WSGI environment, as well
as setting arbitrary additional CGI variables in the WSGI environment.
- The ``TestClient.simulate_request()`` method now supports
passing a query string as part of the path, as an alternative to using the
`params` or `query_string` keyword arguments.
- Added a deployment guide to the docs for uWSGI and NGINX on Linux.
- The ``falcon.uri.decode()`` method now accepts an `unquote_plus` keyword
argument. The new argument defaults to ``False`` to avoid a breaking change.
- The ``Request.if_match`` and ``Request.if_none_match`` properties
now return a list of ``falcon.ETag`` objects rather than the raw
value of the If-Match or If-None-Match headers, respectively.
- ``API.add_error_handler()`` now supports specifying an iterable of
exception types to match.
- The default error serializer no longer sets the `charset` parameter for the
media type returned in the Content-Type header, since UTF-8 is the default
encoding for both JSON and XML media types.
- Similar to the change made to the default error serializer, the default JSON
media type generally used for successful responses was also modified
to no longer specify the `charset` parameter.
This change affects both the ``falcon.DEFAULT_MEDIA_TYPE`` and
``falcon.MEDIA_JSON`` constants, as well
as the default value of the `media_type` keyword argument specified for
the ``falcon.API()`` initializer. This change also affects the default
value of the ``RequestOptions.default_media_type`` and
``ResponseOptions.default_media_type`` options.
Fixed
-----
- Fixed a docs issue where with smaller browser viewports, the API
documentation will start horizontal scrolling.
- The color scheme for the docs was modified to fix issues with contrast and
readability when printing the docs or generating PDFs.
- The ``TestClient.simulate_request()`` method now forces
header values to `str` on Python 2 as required by PEP-3333.
- The ``HTTPRequestEntityTooLarge`` class was renamed to
``HTTPPayloadTooLarge`` and the reason phrase was updated
per RFC 7231.
- The ``falcon.CaseInsensitiveDict`` class now inherits from
``collections.abc.MutableMapping`` under Python 3, instead of
``collections.MutableMapping``.
- The ``\ufffd`` character is now disallowed in requested static file paths.
- The ``falcon.media.validators.jsonschema.validate()`` decorator now uses
``functools.wraps`` to make the decorated method look like the original.
- The ``falcon-print-routes`` CLI tool no longer raises an unhandled error
when Falcon is cythonized.
- The plus character (``'+'``) is no longer unquoted in the request path, but
only in the query string.
- Previously, ``HTTPError`` instances for which the `has_representation`
property evaluated to ``False`` were not passed to custom error serializers
(such as in the case of types that subclass
``NoRepresentation``). This has now been fixed so
that custom error serializers will be called for all instances of
``HTTPError``.
- When setting the ``Response.etag`` header property, the value will
now be wrapped with double-quotes (if not already present) to ensure
compliance with RFC 7232.
- Fixed ``TypeError`` being raised when using Falcon's testing framework
to simulate a request to a generator-based WSGI app.
Contributors to this Release
----------------------------
Many thanks to all of our talented and stylish contributors to this release!
- Bertrand Lemasle
- `CaselIT <https://github.com/CaselIT>`_
- `DmitriiTrofimov <https://github.com/DmitriiTrofimov>`_
- `KingAkeem <https://github.com/KingAkeem>`_
- `Nateyo <https://github.com/Nateyo>`_
- Patrick Schneeweis
- `TheMushrr00m <https://github.com/TheMushrr00m>`_
- `ZDBioHazard <https://github.com/ZDBioHazard>`_
- `alysivji <https://github.com/alysivji>`_
- `aparkerlue <https://github.com/aparkerlue>`_
- `astonm <https://github.com/astonm>`_
- `awbush <https://github.com/awbush>`_
- `bendemaree <https://github.com/bendemaree>`_
- `bkcsfi <https://github.com/bkcsfi>`_
- `brooksryba <https://github.com/brooksryba>`_
- `carlodri <https://github.com/carlodri>`_
- `grktsh <https://github.com/grktsh>`_
- `hugovk <https://github.com/hugovk>`_
- `jmvrbanac <https://github.com/jmvrbanac>`_
- `kandziu <https://github.com/kandziu>`_
- `kgriffs <https://github.com/kgriffs>`_
- `klardotsh <https://github.com/klardotsh>`_
- `mikeylight <https://github.com/mikeylight>`_
- `mumrau <https://github.com/mumrau>`_
- `nZac <https://github.com/nZac>`_
- `navyad <https://github.com/navyad>`_
- `ozzzik <https://github.com/ozzzik>`_
- `paneru-rajan <https://github.com/paneru-rajan>`_
- `safaozturk93 <https://github.com/safaozturk93>`_
- `santeyio <https://github.com/santeyio>`_
- `sbensoussan <https://github.com/sbensoussan>`_
- `selfvin <https://github.com/selfvin>`_
- `snobu <https://github.com/snobu>`_
- `steven-upside <https://github.com/steven-upside>`_
- `tribals <https://github.com/tribals>`_
- `vytas7 <https://github.com/vytas7>`_