How do you run and conformance-check a WSGI application with only the standard library?
answer
- The specification ships with an executable copy
- One stdlib package, several small pieces
- A server good enough for a demo
- A wrapper that asserts the contract
- A helper that fills a fake environ
basics
~10 sThe stdlib package wsgiref is the reference implementation: wsgiref.simple_server.make_server runs a single-threaded HTTP server around your callable, and wsgiref.validate.validator wraps an application so any PEP 3333 violation raises an assertion.
solid answer
~40 s`wsgiref` ships with CPython as the specification's reference implementation and is useful in three ways. `wsgiref.simple_server.make_server(host, port, app)` gives a real, single-threaded HTTP server — fine for a demo or a smoke test, never for production, because it handles one request at a time and has no timeouts or hardening. `wsgiref.validate.validator(app)` returns a wrapper that checks both sides of the contract on every call and raises `AssertionError` on a violation: a body item that is not bytes, a malformed status string, headers that are not a list of two-tuples, `start_response` called too late. `wsgiref.util.setup_testing_defaults(environ)` fills a dict with the mandatory keys so you can call an application in a unit test without a socket, and `wsgiref.headers.Headers` wraps a header list with case-insensitive access.
code
python · 19 linesfrom wsgiref.util import setup_testing_defaults
from wsgiref.validate import validator
def bad_app(environ, start_response):
start_response("200 OK", [("Content-Type", "text/plain")])
return ["this should have been bytes"]
environ = {}
setup_testing_defaults(environ)
result = validator(bad_app)(environ, lambda status, headers: None)
try:
for chunk in result:
print(chunk)
except AssertionError as exc:
print("validator rejected it:", exc)
finally:
result.close()go deeper
Recall that the standard library already contains a WSGI server and a conformance wrapper, so a hand-written application can be run and checked with no dependencies at all.
Explain the split of responsibilities inside the package: a demo server, a validator that asserts the contract, and a helper that builds a fake environ for unit tests without a socket.
Show where it belongs in a real workflow: wrap hand-written middleware in the validator during tests, and be able to state the reference server's limits — one request at a time, no timeouts, no hardening — before anyone asks.
Frame it as the value of an executable specification: a reference implementation in the standard library is why the ecosystem's servers and frameworks interoperate, and why conformance is testable in CI rather than argued about.
### Why a reference implementation ships in the stdlib A specification that nobody implements drifts. `wsgiref` exists so PEP 3333 has an executable definition inside CPython itself: a server that drives applications, handlers that implement the protocol's rules, and a validator that asserts them. It is not a deployment target, and the documentation says so plainly — it is a teaching, testing and conformance tool. ### The pieces **`wsgiref.simple_server`** builds on the standard-library HTTP server machinery. `make_server(host, port, app)` returns a server object; `serve_forever()` loops, and `handle_request()` serves exactly one request, which is handy in a test. It is single-threaded and processes requests strictly in order, so a slow handler blocks everything behind it; there is no request-size limit, no keep-alive tuning and no timeout policy. `demo_app` is a tiny built-in application that prints the environ, useful when you want to see what a server actually passes. **`wsgiref.validate`** is the interesting one. `validator(app)` returns an application with the same signature that asserts the contract as data flows through it. On the way in it checks that `environ` is a dict, that its keys are strings, that the mandatory CGI keys are present, and that the request-body and error streams have the methods the specification requires. On the way out it checks the status string's shape, that headers are a list of two-tuples of strings, that `start_response` was called before the first chunk, and — the one that catches the most bugs — that every item yielded by the returned iterable is `bytes`. Violations raise `AssertionError` with a message naming the rule, which is exactly what you want in CI around a hand-written application or a piece of middleware. **`wsgiref.util`** carries small helpers, of which `setup_testing_defaults(environ)` is the one worth remembering: it fills in the mandatory keys so an application can be called directly in a unit test, with no socket and no server. It also has helpers for reconstructing the request URL and for handling the file-wrapper optimisation. **`wsgiref.headers`** provides `Headers`, a case-insensitive mapping view over the list of header tuples an application is about to return — convenient for middleware that adds or replaces a header without caring how the underlying list is ordered. **`wsgiref.handlers`** holds the base classes that actually implement the server side of the protocol, including a CGI handler. You reach for these only when writing a server or embedding one. ### Where it fits in practice Most applications never import `wsgiref`, because a framework and a production server sit in between. It earns its place in two situations. First, when you write **middleware** by hand — middleware is the easiest part of the ecosystem to get subtly wrong, and wrapping the stack in the validator during tests turns a vague downstream failure into a precise assertion. Second, when you need a **zero-dependency HTTP endpoint** inside a tool or a test fixture: a handful of lines gives you a real server that speaks HTTP without adding a package. ### The caveat to say out loud Interviewers ask this to see whether a candidate knows the difference between a reference implementation and a production one. The right answer names `wsgiref`, uses it for development, tests and conformance, and states the limitation without being prompted: one request at a time, no hardening, no timeouts, so it belongs behind nothing and in front of nobody.
- Why is wsgiref.simple_server unsuitable for production traffic?It serves one request at a time in a single thread, so any slow handler blocks every queued client. It also has no timeouts, no request-size limits, no worker supervision and no hardening against slow or malformed clients. It exists to demonstrate and test the protocol, and the standard-library documentation says so explicitly.
- What kinds of violations does wsgiref.validate.validator actually catch?Body items that are not `bytes`, a status string with the wrong shape, headers that are not a list of two-tuples of strings, `start_response` called after output has begun or never called at all, and an environ missing mandatory keys or carrying non-string keys. Each raises `AssertionError` naming the rule, which makes it a good wrapper for middleware tests.
saying these in an interview costs you the question
- Suggests wsgiref.simple_server for production traffic
- Thinks a third-party package is required to run WSGI
- Believes the validator checks HTTP semantics
- Assumes make_server is multi-threaded
- Cannot name any way to test an app without a socket