Changelog for Falcon 2.0.0
==========================

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 :class:`~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: :meth:`~falcon.Response.set_header`,
  :meth:`~falcon.Response.delete_header`, :meth:`~falcon.Response.get_header`,
  :meth:`~falcon.Response.set_headers`.
- :attr:`falcon.testing.Result.json` now returns ``None`` when the response body is
  empty, rather than raising an error.
- :meth:`~falcon.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.
- :meth:`~falcon.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.
- :attr:`~falcon.RequestOptions.keep_blank_qs_values` now defaults to ``True``
  instead of ``False``.
- :attr:`~falcon.RequestOptions.auto_parse_qs_csv` now defaults to ``False``
  instead of ``True``.
- ``independent_middleware`` kwarg on :class:`falcon.API` now defaults to
  ``True`` instead of ``False``.
- The ``stream_len`` property of the :class:`~falcon.Response` class was changed to
  be an alias of the new :attr:`~falcon.Response.content_length` property. Please
  use :meth:`~falcon.Response.set_stream` or :attr:`~falcon.Response.content_length`
  instead, going forward, as ``stream_len`` is now deprecated.
- Request :attr:`~falcon.Request.context_type` was changed from dict to a bare class
  implementing the mapping interface.
  (See also: :ref:`bare_class_context_type`)
- Response :attr:`~falcon.Response.context_type` was changed from dict to a bare class
  implementing the mapping interface.
  (See also: :ref:`bare_class_context_type`)
- :class:`~.media.JSONHandler` and :class:`~.HTTPError` no longer use
  `ujson` in lieu of the standard `json` library (when `ujson` is available in
  the environment). Instead, :class:`~.media.JSONHandler` can now be configured
  to use arbitrary ``dumps()`` and ``loads()`` functions. If you
  also need to customize :class:`~.HTTPError` serialization, you can do so via
  :meth:`~.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 :ref:`middleware <middleware>` methods and :ref:`hooks <hooks>` must
  now accept the arguments as specified in the relevant interface definitions
  as of Falcon 2.0. All backwards-compatible shims have been removed.
- Custom error serializers are now required to accept the arguments as
  specified by :meth:`~.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 :attr:`falcon.Request.headers` and
  :attr:`falcon.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.
- The :attr:`falcon.Request.stream` attribute 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, :attr:`~falcon.Request.bounded_stream` may be used instead.
- :attr:`falcon.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: :meth:`~.API.add_error_handler`

- :attr:`~.falcon.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
  :class:`~falcon.Request` class.
- The deprecated ``get_param_as_dict()`` method alias was removed from the
  :class:`~falcon.Request` class. Please use :meth:`~falcon.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.
- :meth:`~.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
  :meth:`~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()``.

  See also: :class:`~.media.BaseHandler`

- The deprecated ``falcon.routing.create_http_method_map()`` method was
  removed.
- The keyword arguments for :meth:`~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
    )

- :attr:`~.falcon.RequestOptions.auto_parse_qs_csv` now defaults
  to ``False`` instead of ``True``.
- The ``HTTPRequestEntityTooLarge`` class was renamed to
  :class:`~falcon.HTTPPayloadTooLarge`.
- Two of the keyword arguments for :meth:`~falcon.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 :meth:`falcon.media.validators.jsonschema.validate` decorator now uses
  :meth:`functools.wraps` to make the decorated method look like the original.
- Previously, :class:`~.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
  :class:`~.NoRepresentation`). This has now been fixed so
  that custom error serializers will be called for all instances of
  :class:`~.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 :attr:`~falcon.Request.if_match` and :attr:`~falcon.Request.if_none_match` properties
  now return a list of :class:`falcon.ETag` objects rather than the raw
  value of the If-Match or If-None-Match headers, respectively.
- When setting the :attr:`~falcon.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 :data:`falcon.DEFAULT_MEDIA_TYPE` and
  :data:`falcon.MEDIA_JSON` :ref:`constants <media_type_constants>`, as well
  as the default value of the `media_type` keyword argument specified for
  the :class:`falcon.API` initializer. This change also affects the default
  value of the :attr:`.RequestOptions.default_media_type` and
  :attr:`.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 :attr:`~falcon.Response.headers` property to the :class:`~falcon.Response` class.
- Removed the :py:mod:`six` and :py:mod:`python-mimeparse` dependencies.
- Added a new :attr:`~falcon.Response.complete` property to the :class:`~falcon.Response`
  class. This can be used to short-circuit request processing when the response
  has been pre-constructed.
- Request :attr:`~falcon.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.

  Applications can work around this change by explicitly overriding
  :attr:`~falcon.Request.context_type` to dict.
  (See also: :ref:`bare_class_context_type`)
- Response :attr:`~falcon.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.

  Applications can work around this change by explicitly overriding
  :attr:`~falcon.Response.context_type` to dict.
  (See also: :ref:`bare_class_context_type`)
- :class:`~.media.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, :meth:`~falcon.Request.get_cookie_values`, to the
  :class:`~falcon.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.
- :meth:`~falcon.Response.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.

  See also: :meth:`~.API.add_route`

- The :meth:`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.

  See also: :meth:`~.API.add_static_route`

- 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: :meth:`~.API.add_error_handler`.

- All error classes now accept a `headers` keyword argument for customizing
  response headers.
- A new method, :meth:`~falcon.Request.get_param_as_float`, was added to the
  :class:`~falcon.Request` class.
- A new method, :meth:`~falcon.Request.has_param`, was added to the
  :class:`~falcon.Request` class.
- A new property, :attr:`~falcon.Response.content_length`, was added to the
  :class:`~falcon.Response` class. Either :meth:`~falcon.Response.set_stream` or
  :attr:`~falcon.Response.content_length` should be used going forward, as
  ``stream_len`` is now deprecated.
- All ``get_param_*()`` methods of the :class:`~falcon.Request` class now accept a
  `default` argument.
- A new header property, :attr:`~falcon.Response.expires`, was added to the
  :class:`~falcon.Response` class.
- The :class:`~.routing.CompiledRouter` class now exposes a
  :class:`~falcon.routing.CompiledRouter.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()``.

  See also: :class:`~.media.BaseHandler`

- The :meth:`~falcon.Response.get_header` method now accepts a `default` keyword
  argument.
- The :meth:`~falcon.testing.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 :meth:`~falcon.testing.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 :meth:`~.uri.decode` method now accepts an `unquote_plus` keyword
  argument. The new argument defaults to ``False`` to avoid a breaking change.
- The :meth:`~falcon.Request.if_match` and :meth:`~falcon.Request.if_none_match` properties
  now return a list of :class:`falcon.ETag` objects rather than the raw
  value of the If-Match or If-None-Match headers, respectively.
- :meth:`~.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 :data:`falcon.DEFAULT_MEDIA_TYPE` and
  :data:`falcon.MEDIA_JSON` :ref:`constants <media_type_constants>`, as well
  as the default value of the `media_type` keyword argument specified for
  the :class:`falcon.API` initializer. This change also affects the default
  value of the :attr:`.RequestOptions.default_media_type` and
  :attr:`.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 :meth:`~falcon.testing.TestClient.simulate_request` method now forces
  header values to `str` on Python 2 as required by PEP-3333.
- The ``HTTPRequestEntityTooLarge`` class was renamed to
  :class:`~falcon.HTTPPayloadTooLarge` and the reason phrase was updated
  per RFC 7231.
- The  :class:`falcon.CaseInsensitiveDict` class now inherits from
  :class:`collections.abc.MutableMapping` under Python 3, instead of
  :class:`collections.MutableMapping`.
- The ``\ufffd`` character is now disallowed in requested static file paths.
- The :meth:`falcon.media.validators.jsonschema.validate` decorator now uses
  :meth:`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, :class:`~.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
  :class:`~.NoRepresentation`). This has now been fixed so
  that custom error serializers will be called for all instances of
  :class:`~.HTTPError`.
- When setting the :attr:`~falcon.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 for 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>`_
