Skip to content

Please add releases for recent releases #475

Description

@mikeckennedy

Hi all. I noticed there haven't been releases (as in https://github.com/pallets/quart/releases ) for the past 2 years. It would be really helpful to know whether there are important updates, CVEs, breaking changes, etc for recent releases.

I created some for the past releases below to help smooth this along if you agree there should be releases listed on the repo. I also attached this below as Markdown in case that is helpful. quart-release-notes.md


Quart release notes, 0.21.0 through 0.23.1

Reconstructed from the git history and CHANGES.md, with dates taken from PyPI.


0.21.0: Python 3.14 support and a session key rotation fix

Released 2026-07-23

Seven months after 0.20.0, and mostly a catch-up with Flask. The supported Python range moves to 3.11 through 3.14, and a Flask security fix for secret key rotation comes across with it.

Security

  • Backported the Flask fix for signing key selection order when key rotation is enabled through SECRET_KEY_FALLBACKS (GHSA-4grg-w6v8-c28g). itsdangerous expects the oldest key first and the active signing key last, and Quart had the list the other way round, so sessions were being signed with a fallback key rather than the current one. If you use SECRET_KEY_FALLBACKS, this is the release you want.

Python versions

  • Added support for Python 3.14, including a CI run against the free-threaded build.
  • Dropped Python 3.9 and 3.10. The floor is now 3.11, and the importlib-metadata and typing-extensions conditional dependencies are gone with them.

Added

  • SESSION_COOKIE_PARTITIONED config value, matching Flask. Setting it marks the session cookie as partitioned (CHIPS), which is what lets a third-party cookie work on the top-level site that set it instead of being blocked outright.
  • --debug / --no-debug flags for quart run, matching the Flask CLI.
  • Quart.default_config is now built from Flask.default_config rather than being maintained as a separate literal, so Flask config keys arrive automatically instead of drifting apart.

Fixed

  • app.run() now detects URI schemes that do not need a port and configures the server accordingly, rather than forcing a port onto them.
  • max_form_parts is no longer applied to non-multipart forms. URL-encoded forms are cheap to parse, so capping the part count there only rejected valid requests.
  • Exception propagation was corrected. (Reverted in 0.22.0, once it turned out to conflate propagation with traceback rendering. See below.)

Typing and docs

  • Removed the invalid uses of AnyStr.
  • Added detailed type hints for Request.files, and documented the type.
  • template_folder now accepts os.PathLike, for parity with Flask.
  • Assorted documentation fixes: add_websocket example code, server-sent events, the event loop page, the async generator section, and the blog example's pathlib usage.

0.22.0: Logging, root_path, and traceback corrections

Released 2026-08-19

Four weeks later, three bug fixes. One of them backs out a change from 0.21.0.

Fixed

  • app.run() now logs through Quart's logger instead of Hypercorn's. Customising app.logger previously had no effect on development server output, which was surprising.
  • Fixed request paths when root_path is set and the application is not mounted at the root. The old code did path.split(root_path, 1)[1], which split on the first occurrence of the root path anywhere in the URL, so a path that happened to repeat the mount string was rewritten incorrectly. It now checks the prefix and uses str.removeprefix.
  • Reverted the exception propagation change from 0.21.0. Whether a traceback is rendered is a decision for the ASGI layer and should not be driven by PROPAGATE_EXCEPTIONS; a traceback is now shown when debug or testing is set.

Performance

  • Small improvements to the global proxies and to Response.

Docs

  • Documented the ordering in which the serving functions are called.
  • Added a missing default argument to the docs.

0.23.0: New reloader, merged contexts, and real disconnect handling

Released 2026-08-29. Yanked from PyPI, use 0.23.1 instead.

The biggest of these four releases, and the one that breaks things. The development reloader is rewritten, the request and websocket contexts collapse into a single AppContext as they did in Flask, and the ASGI layer is reworked around task groups and queue shutdown.

It was yanked hours later over a stray debug print (see 0.23.1). Everything below still applies. Install 0.23.1 to get it.

Breaking changes

  • The contexts are merged. RequestContext and WebsocketContext are gone, along with the request_ctx and websocket_ctx proxies in quart.globals. There is now one AppContext carrying optional request, websocket and session data, with has_request and has_websocket properties. The public top-level API is unchanged: request, websocket, session, g, current_app, has_request_context(), has_websocket_context() and the copy_current_*_context decorators all work as before. Code that reached into quart.ctx or quart.globals directly will need updating.
  • g is no longer copied when a context is copied. This follows an explicit Flask decision. Work started with copy_current_request_context now gets a fresh g, so anything stashed there before the copy will not be visible inside the background task.
  • Python 3.11 and 3.12 are no longer supported. The floor is now 3.13, which is what makes asyncio.Queue.shutdown available for the disconnect handling below.
  • Client disconnects raise ClientDisconnectedError instead of CancelledError. Reads and sends against a disconnected client raise the new exception, which is exported from quart.wrappers. Handlers may also run for longer after a disconnect than they used to, since the teardown path changed.

Reloader

  • The development reloader now runs the app in a subprocess and reloads it continually, the way Werkzeug's does. A syntax error is recoverable: fix the file and the server comes back, instead of the whole process dying.
  • Signal handling no longer applies to the reloading parent process, so Ctrl+C works even while the app is failing to import.
  • The reloader now uses sys.orig_argv rather than reconstructing the command line, which should retire a long tail of reloader bugs.
  • A return code of 3 tells the reloader to stop and shut down. (Werkzeug uses 3 for the opposite meaning, so this is worth noting if you script around it.)

ASGI layer

  • HTTP and websocket connections are now driven by an asyncio.TaskGroup instead of asyncio.wait plus manual cancellation.
  • Request bodies flow through an asyncio.Queue with shutdown() rather than being accumulated into a bytearray, so a disconnect closes the queue and surfaces as ClientDisconnectedError. Body.append is replaced by await Body.put() and await Body.get().
  • Websockets gained a Buffer class, overridable through Websocket.buffer_class, in place of the queue that lived on the ASGI connection object.
  • Supports ASGI WWW 2.4, where sending after disconnection raises OSError; that is converted to ClientDisconnectedError.

Fixed

  • All teardown functions now run even if one of them raises. Errors are collected and re-raised together in an ExceptionGroup instead of the first one aborting the rest, so teardown functions are free to fail. (The _CollectErrors helper is taken from Flask's main branch, ahead of a Flask release.)
  • Blueprint teardown_websocket functions were never registered on the app. They are now.

0.23.1: Removes a debug print that echoed request bodies

Released 2026-08-29

0.23.0 shipped with a print(data) left in Body.__await__, in src/quart/wrappers/request.py. Every call that awaited a request body printed the accumulated bytes to stdout, once per chunk. On a busy server that is a lot of noise, and on any server it means request bodies land in the process output and from there into logs: form posts, JSON payloads, credentials and all. 0.23.0 was yanked for it.

  • Removed the print from Body.__await__, plus two others in an example and a test.
  • Enabled ruff's T20 (flake8-print) rules so a stray print fails lint rather than reaching a release.

There are no other changes; 0.23.1 is 0.23.0 with the print removed.


Notes for the maintainer

Two small things turned up while reading through the history.

docs/discussion/python_versions.rst currently says releases from 0.22.0 onward require Python 3.13. The floor actually moved in 0.23.0. 0.22.0 still declares requires-python = ">=3.11". The line was edited as part of the backpressure commit, which was written before the release it landed in was decided.

ClientDisconnectedError is exported from quart.wrappers but not from the top-level quart package. Anyone catching it has to reach for the submodule, which is a slightly awkward import for what is now the ordinary way a disconnect surfaces to application code.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions