The mod_wsgi Python Module

When a WSGI application runs under Apache with mod_wsgi loaded, the mod_wsgi module is built into the Python interpreter and exposes attributes and functions that let the application introspect the hosting environment, subscribe to lifecycle and request events, read request and process metrics, and use mod_wsgi-specific helper types. This page is a reference of that surface.

The built-in mod_wsgi module is installed into sys.modules by mod_wsgi itself when an interpreter is initialised; it is not the same module as the mod_wsgi Python package distributed on PyPI that provides the mod_wsgi-express command. See Detecting mod_wsgi at Runtime for reliable ways to tell whether code is running under Apache/mod_wsgi rather than just having the PyPI package installed.

Module attributes

version

The mod_wsgi version as a (major, minor, micro) tuple, reflecting the version of mod_wsgi.so loaded into Apache.

process_group

The name of the daemon process group hosting the interpreter, or the empty string when running in embedded mode. Matches the first argument given to WSGIDaemonProcess.

application_group

The name of the application group (sub interpreter) hosting the WSGI application. Matches the value given to WSGIApplicationGroup. The special name %{GLOBAL} resolves to the empty string and runs in the main interpreter; any other non-empty name runs in a named sub interpreter.

maximum_processes

The maximum number of processes hosting interpreters in the current process group. In daemon mode this is the processes= value on WSGIDaemonProcess; in embedded mode it is queried from the active Apache MPM.

threads_per_process

The maximum number of worker threads per process. In daemon mode this is the threads= value on WSGIDaemonProcess; in embedded mode it is queried from the active Apache MPM.

Event subscription

subscribe_events(callback)

Register callback to receive every event mod_wsgi publishes. The callback is invoked with the event name as a single positional argument and the event payload as keyword arguments. The expected signature is:

def callback(name, **event):
    ...

or, when only specific keys matter, declare them as keyword-only parameters:

def callback(name, *, request_id, request_start, **event):
    ...

The function returns the callback unchanged so it can also be used as a decorator:

@mod_wsgi.subscribe_events
def callback(name, **event):
    ...

If the callback returns a dict, its keys are merged into the event dict (the same dict passed as kwargs to subscribers) before subsequent subscribers of the same event firing run. The merge is shallow and applies only to this dispatch; the next event published gets a fresh event dict.

To carry data across events for the same request, for example to set a value at request_started and read it at request_finished, mutate mod_wsgi.request_data() instead. That dict is the same object referenced under the request_data key in every event for the request and lives for the request’s lifetime.

subscribe_shutdown(callback)

A shortcut for subscribing only to the process_stopping event. The callback shape matches subscribe_events and the callback is invoked once, when the process is shutting down. Like subscribe_events, the function returns the callback so it can be used as a decorator:

@mod_wsgi.subscribe_shutdown
def cleanup(*args, **kwargs):
    ...

Inert in service-script daemons (WSGIDaemonProcess threads=0); use signal.signal() directly in those processes. See Registering Cleanup Code and the service-script notes in Subscribing to Events.

subscribe_signals(callback)

Register callback to receive the process_signal event when the daemon process is sent SIGHUP or SIGUSR2. The callback shape matches subscribe_events and the payload carries signame (canonical string like "SIGHUP") and signum (numeric value for convenience). The function returns the callback so it can be used as a decorator:

@mod_wsgi.subscribe_signals
def on_signal(name, *, signame, **event):
    if signame == 'SIGHUP':
        reload_config()
    elif signame == 'SIGUSR2':
        dump_diagnostics()

Only effective in request-handling daemon-mode processes. In embedded mode the call is permitted (and still returns the callback so decorator syntax does not silently nullify the user’s function symbol) but the callback is discarded; a warning and a Python stack trace identifying the registration site are emitted to the Apache error log. Service-script daemons (WSGIDaemonProcess threads=0) are also unsupported here; use signal.signal() directly in those processes. See Subscribing to Events for details.

Events

mod_wsgi publishes the following events. subscribe_events callbacks see all of them; subscribe_shutdown callbacks see only process_stopping; subscribe_signals callbacks see only process_signal.

request_started

Fires immediately before the WSGI application callable is invoked. Payload keys include request_id, thread_id, server_pid (Apache child worker PID), request_start (seconds since epoch), application_object, callable_object (the configured callable name as a string), application_start, request_environ (the WSGI environment), and request_data. Daemon mode adds queue_start, daemon_start, daemon_connects and daemon_restarts.

response_started

Fires when the WSGI application calls start_response. Payload keys are request_id, response_status (the status line), response_headers, exception_info (or None), and request_data.

request_finished

Fires after the response has been fully written. Payload includes the request-start fields plus timing fields (application_finish, application_time, input_time, output_time), counter fields (input_reads, input_length, output_writes, output_length, status), CPU fields (cpu_user_time, cpu_system_time, cpu_time), GIL-contention fields (gil_wait_time, gil_wait_count), and request_data.

request_exception

Fires when an uncaught exception propagates out of the WSGI application callable. Payload keys are request_id, exception_info (a (type, value, traceback) tuple as produced by sys.exc_info), and request_data.

process_signal

Fires in a daemon process when SIGHUP or SIGUSR2 is delivered to the daemon. Payload keys are signame (a string, "SIGHUP" or "SIGUSR2") and signum (the numeric value of the signal on the current platform). Subscribers should branch on signame because numeric signal values vary across platforms. Embedded mode never publishes this event; see Subscribing to Events for details.

process_stopping

Fires once per process when mod_wsgi is shutting it down. Payload key shutdown_reason is one of:

  • "shutdown_signal" — SIGTERM (Apache stop or restart).

  • "graceful_signal" — SIGUSR1 graceful drain.

  • "eviction_signal" — operator-driven eviction.

  • "maximum_requests"maximum-requests limit reached.

  • "restart_interval"restart-interval reached.

  • "inactivity_timeout"inactivity-timeout expired.

  • "request_timeout"request-timeout triggered.

  • "startup_timeout"startup-timeout triggered.

  • "deadlock_timeout"deadlock-timeout triggered.

  • "cpu_time_limit"cpu-time-limit exceeded.

  • "script_reload" — WSGI script reload.

The request_id field shared across the request events is the same identifier Apache uses as the request log ID — the value substituted by %L in LogFormat and ErrorLogFormat directives. Subscribers can use it to cross-correlate event data with Apache’s access and error logs.

Per-request state

request_data()

Return the per-request scratchpad dict for the current thread. The dict is created at the start of each request and is the same object passed to subscribers as the request_data event payload key. The application and subscribers share it, so keys an event subscriber sets become visible to the application and vice versa. Raises RuntimeError if called outside the context of an active request.

active_requests

A dict, keyed by request ID, of requests currently being handled by the process. Populated automatically as requests start and finish; subscribers can read it to inspect concurrent in-flight work.

Metrics

start_recording_metrics()

Enable per-request metrics accounting and seed the per-reader baselines so subsequent calls to request_metrics() and process_metrics() return data. Idempotent; safe to call unconditionally at application import time. When external telemetry reporting is enabled (the WSGITelemetryService directive), the external reporter is the canonical metrics consumer and this call has no observable effect from the Python API: the two accessors below still return None to signal the configured mode. Without an explicit call to this function, both accessors return None even when telemetry is off.

request_metrics()

Return a dict of timing and resource counters for the sample interval since the last call. Useful for emitting per-request observability data from the application. Returns None if start_recording_metrics() has not been called or if external telemetry reporting is enabled.

process_metrics()

Return a dict of process-level aggregates: served-request count, request busy time, current and peak resident memory, CPU user and system time, current active-request count, and similar counters covering the lifetime of the process. Returns None under the same conditions as request_metrics().

server_metrics()

Return a dict reflecting the Apache scoreboard view of the server: per-process and per-worker state, total requests served, bytes transferred, and similar server-wide aggregates. The data is gated by configuration; see WSGIServerMetrics for the embedded-mode gate and WSGIDaemonProcess for the daemon-mode gate.

Types

FileWrapper

A callable that wraps a file-like object so the WSGI adapter can return its contents using sendfile() or equivalent optimisations. The same callable is also exposed in the WSGI environment under the key wsgi.file_wrapper. See File Wrapper Extension.

RequestTimeout

An exception class derived directly from BaseException, raised in a daemon-process worker thread when the configured request-timeout= is exceeded and the daemon monitor injects a timeout into the worker. Deriving from BaseException (not Exception) means well-written code does not accidentally catch it via except Exception:. Application code may catch it for per-request cleanup but should re-raise so the WSGI adapter can return 504. See WSGIDaemonProcess for request-timeout= semantics.

Companion apache module

Alongside mod_wsgi, mod_wsgi installs an apache module into sys.modules carrying introspection attributes about the Apache server hosting the application. Application code can import apache to inspect the server configuration; the module is available in both embedded and daemon mode.

version

Apache server version as a (major, minor, patchlevel) tuple, distinct from mod_wsgi.version (which is the mod_wsgi extension version).

maximum_processes

Maximum number of processes hosting interpreters. Same value as mod_wsgi.maximum_processes.

threads_per_process

Maximum number of worker threads per process. Same value as mod_wsgi.threads_per_process.

description

The Apache server description string, as returned by ap_get_server_description(), e.g. "Apache/2.4.58 (Unix)".

mpm_name

The name of the active Apache MPM, e.g. "event", "worker" or "prefork".

build_date

The date on which the running Apache binary was built.

WSGI environ keys

The WSGI environ dict passed to the application has the standard WSGI keys plus mod_wsgi-specific keys mirroring the module attributes and adding per-request information.

mod_wsgi.version

(major, minor, micro) tuple, same value as the module-level version. Reliable runtime test that the request is being served by Apache/mod_wsgi.

mod_wsgi.process_group

Same value as the module-level process_group.

mod_wsgi.application_group

Same value as the module-level application_group.

mod_wsgi.request_id

The Apache request log ID — the same identifier substituted by %L in LogFormat and ErrorLogFormat. Useful for cross-correlating application data with Apache’s access and error logs. Same value as the request_id field on the request events.

mod_wsgi.connection_id

The Apache connection log ID, set when Apache has generated one for the underlying connection.

mod_wsgi.thread_id

Numeric ID of the worker thread handling this request.

mod_wsgi.server_pid

Process ID of the Apache child worker that accepted the request, as a decimal string. In embedded mode this is the process the WSGI application is running in; in daemon mode it is the originating Apache child, distinct from the daemon process serving the request (the daemon’s own PID is available via os.getpid()).

The four request-timing keys below are Python float values in seconds since the epoch. They carry the same instants as the identically-named fields on the request_started and request_finished event payloads.

mod_wsgi.request_start

Time Apache received the request.

mod_wsgi.queue_start

Time Apache wrote the request onto the daemon socket. 0.0 in embedded mode (no queue phase).

mod_wsgi.daemon_start

Time the daemon process accepted the request and began handling it. 0.0 in embedded mode.

mod_wsgi.application_start

Time the worker thread was about to invoke the WSGI application callable for this request.

WSGI script __name__

mod_wsgi imports each WSGI script file as if it were a module, but because WSGI scripts can live anywhere on disk and not on sys.path, the imported name is synthesised from a hash of the script path with the prefix _mod_wsgi_. A WSGI script can detect that it is being loaded by mod_wsgi (rather than executed directly) with:

if __name__.startswith('_mod_wsgi_'):
    ...

See Detecting mod_wsgi at Runtime for other reliable ways to detect the mod_wsgi runtime.