diff --git a/docs/changes/4.0.0.rst b/docs/changes/4.0.0.rst index bc463cae5..c313cf75d 100644 --- a/docs/changes/4.0.0.rst +++ b/docs/changes/4.0.0.rst @@ -79,7 +79,312 @@ runtime behavior, but may surface new or different errors with type checkers. Also, make sure to :ref:`let us know ` which essential aliases are missing from the public interface! -.. towncrier release notes start +Breaking Changes +---------------- + +- Falcon is no longer vendoring the + `python-mimeparse `__ library; + the relevant functionality has instead been reimplemented in the framework + itself, fixing a handful of long-standing bugs in the new implementation. + + If you use standalone + `python-mimeparse `__ in your + project, do not worry! We will continue to maintain it as a separate package + under the Falconry umbrella (we took over about 3 years ago). + + The following new behaviors are considered breaking changes: + + * Previously, the iterable passed to + :meth:`req.client_prefers ` had to be sorted in + the order of increasing desirability. + :func:`~falcon.mediatypes.best_match`, and by proxy + :meth:`~falcon.Request.client_prefers`, now consider the provided media types + to be sorted in the (more intuitive, we hope) order of decreasing + desirability. + + * Unlike ``python-mimeparse``, the new + :ref:`media type utilities ` consider media types with + different values for the same parameters as non-matching. + + One theoretically possible scenario where this change can affect you is only + installing a :ref:`media ` handler for a content type with parameters; + it then may not match media types with conflicting values (that used to match + before Falcon 4.0). + If this turns out to be the case, also + :ref:`install the same handler ` for the generic + ``type/subtype`` without parameters. + + The new functions, + :func:`falcon.mediatypes.quality` and :func:`falcon.mediatypes.best_match`, + otherwise have the same signature as the corresponding methods from + ``python-mimeparse``. (`#864 `__) +- A number of undocumented internal helpers were renamed to start with an + underscore, indicating they are private methods intended to be used only by the + framework itself: + + * ``falcon.request_helpers.header_property`` → + ``falcon.request_helpers._header_property`` + * ``falcon.request_helpers.parse_cookie_header`` → + ``falcon.request_helpers._parse_cookie_header`` + * ``falcon.response_helpers.format_content_disposition`` → + ``falcon.response_helpers._format_content_disposition`` + * ``falcon.response_helpers.format_etag_header`` → + ``falcon.response_helpers._format_etag_header`` + * ``falcon.response_helpers.format_header_value_list`` → + ``falcon.response_helpers._format_header_value_list`` + * ``falcon.response_helpers.format_range`` → + ``falcon.response_helpers._format_range`` + * ``falcon.response_helpers.header_property`` → + ``falcon.response_helpers._header_property`` + * ``falcon.response_helpers.is_ascii_encodable`` → + ``falcon.response_helpers._is_ascii_encodable`` + + If you were relying on these internal helpers, you can either copy the + implementation into your codebase, or switch to the underscored variants. + (Needless to say, though, we strongly recommend against referencing private + methods, as we provide no SemVer guarantees for them.) (`#1457 `__) +- A number of previously deprecated methods, attributes and classes have now been + removed: + + * In Falcon 3.0, the use of positional arguments was deprecated for the + optional initializer parameters of :class:`falcon.HTTPError` and its + subclasses. + + We have now redefined these optional arguments as keyword-only, so passing + them as positional arguments will result in a :class:`TypeError`: + + >>> import falcon + >>> falcon.HTTPForbidden('AccessDenied') + Traceback (most recent call last): + <...> + TypeError: HTTPForbidden.__init__() takes 1 positional argument but 2 were given + >>> falcon.HTTPForbidden('AccessDenied', 'No write access') + Traceback (most recent call last): + <...> + TypeError: HTTPForbidden.__init__() takes 1 positional argument but 3 were given + + Instead, simply pass these parameters as keyword arguments: + + >>> import falcon + >>> falcon.HTTPForbidden(title='AccessDenied') + + >>> falcon.HTTPForbidden(title='AccessDenied', description='No write access') + + + * The ``falcon-print-routes`` command-line utility is no longer supported; + ``falcon-inspect-app`` is a direct replacement. + + * :class:`falcon.stream.BoundedStream` is no longer re-imported via + ``falcon.request_helpers``. + If needed, import it directly as :class:`falcon.stream.BoundedStream`. + + * A deprecated alias of :class:`falcon.stream.BoundedStream`, + ``falcon.stream.Body``, was removed. Use :class:`falcon.stream.BoundedStream` + instead. + + * A deprecated utility function, ``falcon.get_http_status()``, was removed. + Please use :meth:`falcon.code_to_http_status` instead. + + * A deprecated routing utility, ``compile_uri_template()``, was removed. + This function was only employed in the early versions of the framework, and + is expected to have been fully supplanted by the + :class:`~falcon.routing.CompiledRouter`. In a pinch, you can simply copy its + implementation from the Falcon 3.x source tree into your application. + + * The deprecated ``Response.add_link()`` method was removed; please use + :meth:`Response.append_link ` instead. + + * The deprecated ``has_representation()`` method for :class:`~falcon.HTTPError` + was removed, along with the ``NoRepresentation`` and + ``OptionalRepresentation`` classes. + + * An undocumented, deprecated public method ``find_by_media_type()`` of + :class:`media.Handlers ` was removed. + Apart from configuring handlers for Internet media types, the rest of + :class:`~falcon.media.Handlers` is only meant to be used internally by the + framework (unless documented otherwise). + + * Previously, the ``json`` module could be imported via ``falcon.util``. + This deprecated alias was removed; please import ``json`` directly from the + :mod:`standard library `, or another third-party JSON library of + choice. + + We decided, on the other hand, to keep the deprecated :class:`falcon.API` alias + until Falcon 5.0. (`#1853 `__) +- Previously, it was possible to create an :class:`~falcon.App` with the + ``cors_enable`` option, and add additional :class:`~falcon.CORSMiddleware`, + leading to unexpected behavior and dysfunctional CORS. This combination now + explicitly results in a :class:`ValueError`. (`#1977 `__) +- The default value of the ``csv`` parameter in + :func:`~falcon.uri.parse_query_string` was changed to ``False``, matching the + default behavior of other parts of the framework (such as + :attr:`req.params `, the test client, etc). + If the old behavior fits your use case better, pass the ``csv=True`` keyword + argument explicitly. (`#1999 `__) +- The deprecated ``api_helpers`` was removed in favor of the ``app_helpers`` + module. In addition, the deprecated ``body`` attributes of the + :class:`~falcon.Response`, :class:`asgi.Response `, and + :class:`~falcon.HTTPStatus` classes were removed. (`#2090 `__) +- The function :func:`falcon.http_date_to_dt` now validates HTTP dates to have + the correct timezone set. It now also returns timezone-aware + :class:`~datetime.datetime` objects. (`#2182 `__) +- ``setup.cfg`` was dropped in favor of consolidating all static project + configuration in ``pyproject.toml`` (``setup.py`` is still needed for + programmatic control of the build process). While this change should not impact + the framework's end-users directly, some ``setuptools``\-based legacy workflows + (such as the obsolete ``setup.py test``) will no longer work. (`#2314 `__) +- The ``is_async`` keyword argument was removed from + :meth:`~falcon.media.validators.jsonschema.validate`, as well as the hooks + :meth:`~falcon.before` and :meth:`~falcon.after`, since it represented a niche + use case that is even less relevant with the recent advances in the ecosystem: + Cython 3.0+ will now correctly mark cythonized ``async def`` functions as + coroutines, and pure-Python factory functions that return a coroutine can now + be marked as such using :func:`inspect.markcoroutinefunction` + (Python 3.12+ is required). (`#2343 `__) + + +New & Improved +-------------- + +- A new keyword argument, `link_extension`, was added to + :meth:`falcon.Response.append_link` as specified in + `RFC 8288, Section 3.4.2 + `__. (`#228 `__) +- A new ``path`` :class:`converter ` + capable of matching segments that include ``/`` was added. (`#648 `__) +- The new implementation of :ref:`media type utilities ` + (Falcon was using the ``python-mimeparse`` library before) now always favors + the exact media type match, if one is available. (`#1367 `__) +- Type annotations have been added to Falcon's public interface to the package + itself in order to better support `Mypy `__ + (or other type checkers) users without having to install any third-party + typeshed packages. (`#1947 `__) +- Similar to the existing :class:`~falcon.routing.IntConverter`, a new + :class:`~falcon.routing.FloatConverter` has been added, allowing to convert + path segments to ``float``. (`#2022 `__) +- The default error serializer will now use the response media handlers + to better negotiate the response content type with the client. + The implementation still defaults to JSON if the client does not indicate any + preference. (`#2023 `__) +- :class:`~falcon.asgi.WebSocket` now supports providing a reason for closing the + socket, either directly via :meth:`~falcon.asgi.WebSocket.close` or by + configuring :attr:`~falcon.asgi.WebSocketOptions.default_close_reasons`. (`#2025 `__) +- An informative representation was added to :class:`testing.Result ` + for easier development and interpretation of failed tests. The form of ``__repr__`` is as follows: + ``Result<{status_code} {content-type header} {content}>``, where the content part will reflect + up to 40 bytes of the result's content. (`#2044 `__) +- A new method :meth:`falcon.Request.get_header_as_int` was implemented. (`#2060 `__) +- A new property, :attr:`~falcon.Request.headers_lower`, was added to provide a + unified, self-documenting way to get a copy of all request headers with + lowercase names to facilitate case-insensitive matching. This is especially + useful for middleware components that need to be compatible with both WSGI and + ASGI. :attr:`~falcon.Request.headers_lower` was added in lieu of introducing a + breaking change to the WSGI :attr:`~falcon.Request.headers` property that + returns uppercase header names from the WSGI ``environ`` dictionary. (`#2063 `__) +- In Python 3.13, the ``cgi`` module is removed entirely from the stdlib, + including its ``parse_header()`` method. Falcon addresses the issue by shipping + an own implementation; :func:`falcon.parse_header` can also be used in your projects + affected by the removal. (`#2066 `__) +- A new ``status_code`` attribute was added to the :attr:`falcon.Response `, + :attr:`falcon.asgi.Response `, + :attr:`HTTPStatus `, + and :attr:`HTTPError ` classes. (`#2108 `__) +- Following the recommendation from + `RFC 9239 `__, the + :ref:`MEDIA_JS ` constant has been updated to + ``text/javascript``. Furthermore, this and other media type constants are now + preferred to the stdlib's :mod:`mimetypes` for the initialization of + :attr:`~falcon.ResponseOptions.static_media_types`. (`#2110 `__) +- A new keyword argument, `samesite`, was added to + :meth:`~falcon.Response.unset_cookie` that allows to override the default + ``Lax`` setting of `SameSite` on the unset cookie. (`#2124 `__) +- A new keyword argument, `partitioned`, was added to + :meth:`~falcon.Response.set_cookie` to opt a cookie into partitioned storage, + with a separate cookie jar per each top-level site. + (See also + `CHIPS `__ + for a more detailed description of this web technology.) (`#2213 `__) +- The class ``falcon.HTTPPayloadTooLarge`` was renamed to + :class:`falcon.HTTPContentTooLarge`, together with the accompanying HTTP + :ref:`status code ` update, in order to reflect the newest HTTP + semantics as per + `RFC 9110, Section 15.5.14 `__. + (The old class name remains available as a deprecated compatibility alias.) + + In addition, one new :ref:`status code constant ` was added: + ``falcon.HTTP_421`` (also available as ``falcon.HTTP_MISDIRECTED_REQUEST``) + in accordance with + `RFC 9110, Section 15.5.20 `__. (`#2276 `__) +- The :class:`~falcon.CORSMiddleware` now properly handles the missing ``Allow`` + header case, by denying the preflight CORS request. + The static file route has been updated to properly support CORS preflight, + by allowing ``GET`` requests. (`#2325 `__) +- Added :attr:`falcon.testing.Result.content_type` and + :attr:`falcon.testing.StreamedResult.content_type` as a utility accessor + for the ``Content-Type`` header. (`#2349 `__) +- A new flag, :attr:`~falcon.ResponseOptions.xml_error_serialization`, has been + added to :attr:`~falcon.ResponseOptions` that can be used to disable automatic + XML serialization of :class:`~falcon.HTTPError` when using the default error + serializer (and the client prefers it). + + This new flag currently defaults to ``True``, preserving the same behavior as + the previous Falcon versions. Falcon 5.0 will either change the default to + ``False``, or remove the automatic XML error serialization altogether. + If you wish to retain support for XML serialization in the default error + serializer, you should add a + :ref:`response media handler for XML `. + + In accordance with this change, the :meth:`falcon.HTTPError.to_xml` method was + deprecated. (`#2355 `__) + + +Fixed +----- + +- The web servers used for tests are now run through :any:`sys.executable` in + order to ensure that they respect the virtualenv in which tests are being run. (`#2047 `__) +- Previously, importing :class:`~falcon.testing.TestCase` as a top-level + attribute in a test module could make ``pytest`` erroneously attempt to collect + its methods as test cases. This has now been prevented by adding a ``__test__`` + attribute (set to ``False``) to the :class:`~falcon.testing.TestCase` class. (`#2147 `__) +- Falcon will now raise an instance of + :class:`~falcon.errors.WebSocketDisconnected` from the :class:`OSError` that + the ASGI server signals in the case of a disconnected client (as per + the `ASGI HTTP & WebSocket protocol + `__ version ``2.4``). + It is worth noting though that Falcon's + :ref:`built-in receive buffer ` normally detects the + ``websocket.disconnect`` event itself prior the potentially failing attempt to + ``send()``. + + Disabling this built-in receive buffer (by setting + :attr:`~falcon.asgi.WebSocketOptions.max_receive_queue` to ``0``) was also + found to interfere with receiving ASGI WebSocket messages in an unexpected + way. The issue has been fixed so that setting this option to ``0`` now properly + bypasses the buffer altogether, and extensive test coverage has been added for + validating this scenario. (`#2292 `__) +- Customizing + :attr:`MultipartParseOptions.media_handlers + ` could previously + lead to unintentionally modifying a shared class variable. + This has been fixed, and the + :attr:`~falcon.media.multipart.MultipartParseOptions.media_handlers` attribute + is now initialized to a fresh copy of handlers for every instance of + :class:`~falcon.media.multipart.MultipartParseOptions`. To that end, a proper + :meth:`~falcon.media.Handlers.copy` method has been implemented for the media + :class:`~falcon.media.Handlers` class. (`#2293 `__) + + +Misc +---- + +- The :ref:`utility functions ` ``create_task()`` and + ``get_running_loop()`` are now deprecated in favor of their standard library + counterparts, :func:`asyncio.create_task` and :func:`asyncio.get_running_loop`. (`#2253 `__) +- The :class:`falcon.TimezoneGMT` class was deprecated. Use the UTC timezone + (:attr:`datetime.timezone.utc`) from the standard library instead. (`#2301 `__) + + Contributors to this Release ----------------------------