Changelog for Falcon 4.1.0#

Summary#

This release contains enhancements to media handling, serving static files, and a fix for the WebSockets-sink interaction, alongside performance optimizations and full support for CPython 3.14.

During this release cycle, we have migrated to publishing to PyPI with a Trusted Publisher (thanks to @webknjaz for helping to iron out the workflow details).

For those relying on other package distribution channels than PyPI, we have prepared a brand new Packaging Guide for Falcon. Please check it out and let us know what you think! Additionally, we have formalized our security maintenance policy as well as the status of stable releases: Releases and Versioning.

This release also incorporates many pull requests submitted by our community. We would like to extend our heartfelt thanks to all 17 contributors who made this release possible!

Changes to Supported Platforms#

  • CPython 3.14 is now fully supported. (#2413)

  • Although the Falcon 4.x series is only guaranteed to support Python 3.10+, this release still supports 3.8 & 3.9 at runtime using the pure Python wheel.

    Falcon 4.2 is expected to drop the end-of-life Python 3.8 completely (but runtime support will continue for 3.9 on a best effort basis).

New & Improved#

  • StaticRoute now renders Etag headers. It also checks If-None-Match in requests and returns HTTP 304 response if appropriate. (#2243)

  • StaticRoute now sets the Last-Modified header when serving static files. The improved implementation also checks the value of the If-Modified-Since header, and renders an HTTP 304 response when the requested file has not been modified. (#2244)

  • Similar to create_environ(), the create_scope() testing helper now preserves the raw URI path, and propagates it to the created ASGI connection scope as the raw_path byte string (according to the ASGI specification). (#2262)

  • Two new media_type constants, falcon.MEDIA_CSV and falcon.MEDIA_PARQUET, were added in order to provide better support for Python data analysis applications out of the box. (#2335)

  • Support for allowing cross-origin private network access was added to the built-in CORSMiddleware. The new feature is off by default, and can be enabled by passing the keyword argument allow_private_network=True to CORSMiddleware during initialization. (#2381)

  • The falcon.secure_filename() utility function can now ensure that the length of the sanitized filename does not exceed the requested limit (passed via the max_length argument). In addition, a new option, max_secure_filename_length, was added to MultipartParseOptions in order to automatically populate this argument when referencing a body part’s secure_filename. (#2420)

  • The unset_cookie() method now accepts a same_site parameter (with underscore) for consistency with set_cookie(). The previous samesite parameter (without underscore) is now deprecated (referencing it will emit a deprecation warning). (#2453)

  • A new method, __rich__, has been added to falcon.testing.Result for facilitating a rich-text representation when used together with the popular rich library.

    Provided you have installed both falcon and rich into your environment, you should be able to see a prettier rendition of the below 404-result:

    >>> import falcon
    >>> import falcon.testing
    >>> import rich.pretty
    >>> rich.pretty.install()
    >>> client = falcon.testing.TestClient(falcon.App())
    >>> client.get('/endpoint')
    Result<404 Not Found application/json b'{"title": "404 Not Found"}'>
    

    (The actual appearance may depend on your terminal and/or REPL settings.) (#2457)

  • The cythonization process was revised in the light of the performance improvements in newer CPython versions (especially 3.12+), and the compilation is now largely confined to hand-crafted C/Cython code. As a result, the framework should run even faster on modern CPython. (#2470)

  • JSONHandler can now detect a non-standard (not a subclass of ValueError) deserialization error type for a custom loads function.

    (Normally, json.loads() and third party alternatives do raise a subclass of ValueError on invalid input data, however, this is not the case for, e.g., the popular msgspec library at the time of writing.) (#2476)

Fixed#

  • Previously, Falcon’s WebSocket implementation was not documented to route the request to any sink. However, in the case of a missing route, a matching sink was actually invoked, passing ws in place of the incompatible resp.

    This mismatch has been addressed by introducing a ws keyword argument (similar to ASGI error handlers) for sink functions meant to accept WebSocket connections.

    For backwards-compatibility, when ws is absent from the sink’s signature, the WebSocket object is still passed in place of the incompatible resp. This behavior will change in Falcon 5.0: when draining a WebSocket connection, resp will always be set to None. (#2414)

Misc#

  • The readability of the Contributing docs was improved by properly rendering GitHub Markdown-flavored checkboxes. (#2318)

  • The falcon.testing.httpnow compatibility alias is now considered deprecated, and will be removed in Falcon 5.0. Use the falcon.http_now() function instead. (#2389)

Contributors to this Release#

Many thanks to all of our talented and stylish contributors for this release!