Skip to content

v2.5.0

Choose a tag to compare

@github-actions github-actions released this 13 May 08:46
· 2 commits to main since this release

v2.5.0 (2026-05-13)

This release is published under the MIT License.

Bug Fixes

  • backend: Allow passing arguments without values (175cc01)

  • django_runserver: Don't double-wrap server class with ThreadingMixIn (b272ad2)

DjangoRunserver was passing ThreadedWSGIServer as server_cls AND threading=True to django.core.servers.basehttp.run(). But run() itself does type("WSGIServer", (socketserver.ThreadingMixIn, server_cls), {}) when threading=True, which fails with "Cannot create a consistent method resolution order" because ThreadedWSGIServer already has ThreadingMixIn before WSGIServer in its own MRO.

Django's stock runserver passes plain WSGIServer as server_cls and relies on the threading=True kwarg to get the mixin applied. Match that.

Bug crashed the server on the first connection (or arguably on every start, since the failing line ran in basehttp.run before serve_forever).

The unit test that previously asserted server_cls is ThreadedWSGIServer when threaded was actively locking in the bug; updated to assert plain WSGIServer in both threaded and nothreading paths and added a comment explaining why.

Verified live: prodserver dev now starts, binds on 127.0.0.1:8000, and serves requests without the MRO error.

https://claude.ai/code/session_01JgEsTLFS5TYwpRwjZJJyJg

  • werkzeug: Make Werkzeug's debugger actually fire on exceptions (27a8cd9)

Two bugs were keeping Django's yellow debug page in front instead of Werkzeug's interactive debugger:

  1. We never patched django.views.debug.technical_500_response, so Django's WSGI handler caught every exception and rendered its own debug page; DebuggedApplication never saw the exception.

runserver_plus patches it unconditionally — to a re-raising shim by default (so DebuggedApplication catches and renders the in-browser debugger), or to a postmortem hook when --pm/--pdb/--ipdb is set (drops to the chosen debugger). The previous _apply_pdb_hook only ran for pdb/ipdb/pm AND tried to patch sys.excepthook (which doesn't fire for thread-local WSGI exceptions) instead of technical_500_response.

Replaced with _install_technical_500_handler, called from _inner_run whenever use_debugger or any post-mortem flag is set.

  1. We were setting WERKZEUG_RUN_MAIN=true in init when noreload was set. On werkzeug 3.x this env var means "I'm the reloader child, expect WERKZEUG_SERVER_FD" — werkzeug crashes with KeyError on the FD lookup. The runserver_plus version of this trick was a way to skip the explicit DebuggedApplication wrap in the noreload path and let run_simple(use_debugger=True) do it; but since we always wrap explicitly (so trusted_hosts/PIN can be set), we don't need the gate at all. Werkzeug sets WERKZEUG_RUN_MAIN itself when its reloader spawns the child.

Removed the env-setting line. The wrap conditional now correctly distinguishes parent/single-process (env unset → wrap) from reloader child (env set by werkzeug → skip wrap, child gets re-wrapped via run_simple's internal use_debugger handling).

Verified live: hitting a route that raises now returns Werkzeug's interactive debug page (with debugger.js / console.png / WERKZEUG_DEBUG_PIN markers) instead of Django's 500.

Tests updated: replaced TestPdbHook with TestTechnical500Handler covering the default re-raise path, the postmortem path, the ipdb-missing case, and inner_run installation. The previous test asserting WERKZEUG_RUN_MAIN gets set is inverted to assert it does NOT.

https://claude.ai/code/session_01JgEsTLFS5TYwpRwjZJJyJg

Features

  • Add CeleryFlower backend for Celery monitoring (81132ef)

Adds a CeleryFlower backend that launches the Flower monitoring web UI via the flower subcommand registered on the configured Celery app. Includes a new flower optional-dependency extra, tests, docs, and CI/tox coverage.

https://claude.ai/code/session_019cKCam6ph9f6ZWjh7jfVyV

  • Rename prodserver command to server (deprecate prodserver) (858cff9)

Introduces a new server management command that takes over as the primary way to start configured production processes. The original prodserver command is retained as a deprecated subclass that emits a DeprecationWarning on every invocation, giving users a clear migration window.

The PRODUCTION_PROCESSES setting and the django_prodserver package name are intentionally unchanged. The prodserver alias will be removed in django-prodserver 4.0.0.

Bumps version to 3.0.0 and updates README, docs, and autodoc accordingly.

  • V3.0.0 (a54488c)

  • backends: Add DaphneRunserver ASGI development backend (15975d4)

Adds a pure-Python ASGI dev server backend that mirrors what channels-runserver 3.0.5 used to do (Channels 4.x dropped the override), by driving daphne.Server, daphne.endpoints, daphne.access directly and calling out to Django's autoreload / BaseCommand.check / system-checks machinery rather than going through any management command.

Slots into PRODUCTION_PROCESSES alongside DjangoRunserver with a single ARGS-only config surface whose keys mirror Daphne's CLI flags 1:1. Channels-3 parity flags (addrport, ipv6, noreload, nostatic, insecure, http_timeout, websocket_handshake_timeout) plus the full Daphne CLI surface (unix_socket, fd, endpoints, verbosity, root_path, proxy_headers, ping/timeout tunables, request_buffer_size, server_name, access_log) are all reachable through ARGS, so this single backend covers both dev and production-leaning Daphne configs.

Hard-checks daphne is installed and ASGI_APPLICATION is set in init (mirroring DjangoQ2Worker), and enforces signal_handlers=not use_reloader when constructing daphne.Server so the autoreload supervisor owns SIGINT.

Static files use django.contrib.staticfiles.handlers.ASGIStaticFilesHandler gated on the same condition runserver uses (staticfiles in INSTALLED_APPS and not nostatic and (DEBUG or insecure)).

The existing devserver command is left untouched.

https://claude.ai/code/session_01JgEsTLFS5TYwpRwjZJJyJg

  • backends: Add DjangoRunserver development backend (96c9222)

Adds a pure-Python development server backend that mirrors Django's runserver command using its lower-level primitives (basehttp.run, autoreload, BaseCommand.check, StaticFilesHandler) rather than shelling into call_command("runserver", ...). Slots into PRODUCTION_PROCESSES alongside the existing production backends with a single ARGS-only config surface whose keys mirror runserver's CLI flags 1:1 (addrport, ipv6, noreload, nothreading, nostatic, insecure).

The existing devserver command is left untouched.

  • backends: Add WerkzeugRunserver / RunserverPlus dev backend (3764b38)

Mirrors django-extensions' runserver_plus by driving werkzeug.serving. run_simple directly, with no django-extensions dependency. Slots into PRODUCTION_PROCESSES alongside DjangoRunserver and DaphneRunserver via BaseRunserverBackend, with a single ARGS-only config surface whose keys match runserver_plus CLI flags 1:1.

Full feature mirror under ARGS: - base (free from BaseRunserverBackend): addrport, ipv6, noreload, nostatic, insecure - run_simple kwargs: threaded, nothreading, processes, extra_files, exclude_patterns, reloader_type (auto/watchdog/stat), reloader_interval, passthrough_errors - debugger: use_debugger, nopin, trusted_hosts, evalex (parent-process DebuggedApplication wrap, with WERKZEUG_DEBUG_PIN handling) - ssl: cert_file, key_file, ssl_dev_cert_dir (auto-generates dev certs via werkzeug.serving.make_ssl_devcert when files don't exist; requires pyOpenSSL only when SSL is configured) - ergonomics: browser, output, print_sql, truncate_sql, print_sql_location, pdb, ipdb, pm, keep_meta_shutdown, startup_messages

Werkzeug owns reloading: start_server is overridden to bypass django.utils.autoreload.run_with_reloader, since run_simple has its own reloader with watchdog support and extra_files / exclude_patterns that Django's autoreload doesn't expose.

Hard-checks werkzeug in init (ImproperlyConfigured if missing). Optional deps (pyOpenSSL, sqlparse, pygments, ipdb) are gated lazily behind the features that need them.

RunserverPlus ships as a thin subclass alias for users who recognise the django-extensions name.

New extras groups in pyproject.toml: werkzeug (just the server) and runserver-plus (werkzeug + pyOpenSSL + sqlparse + pygments). New [testenv:werkzeug] in tox.ini, werkzeug added to coverage env's depends and to the justfile envs list. Tests/settings.py gets a dev-werkzeug entry.

Tests: 49 new (all socket-free; mock run_simple, DebuggedApplication, webbrowser, make_ssl_devcert, OpenSSL, ipdb), 378/378 passing overall. The test asserting that start_server does NOT call Django's autoreload locks in the deliberate departure from the other two backends.

https://claude.ai/code/session_01JgEsTLFS5TYwpRwjZJJyJg


Detailed Changes: v2.4.0...v2.5.0