Home >Technology peripherals >It Industry >Python Web Applications: The basics of WSGI
Beneath Django, Flask, Bottle, and every other Python web framework, lies the Web Server Gateway Interface, or WSGI for short. WSGI is to Python what Servlets are to Java — a common specification for web servers that allows different web servers and application frameworks to interact based on a common API. However, as with most things, the Python version is considerably simpler.
WSGI is defined in PEP 3333, which I encourage you to read as a reference if you want more information after this quick intro.
This article will introduce you to the WSGI spec from an application developer’s perspective, and show you how to work directly with WSGI to create applications (if you so desire).
Here’s the most basic Python web app possible:
<span>def app(environ, start_fn): </span> start_fn<span>('200 OK', [('Content-Type', 'text/plain')]) </span> <span>return ["Hello World!\n"] </span>
That’s it! The whole file. Call it app.py and run it with any WSGI-compatible server and you’ll get a Hello World response with a 200 status. You can use gunicorn for this; just install it via pip (pip install gunicorn) and run it with gunicorn app:app. This command tells gunicorn to get the WSGI callable from the app variable in the app module.
Right now, you should be pretty excited. Just 3 lines for a running application? That must be some sort of record (barring PHP, because mod_php is cheating). I bet you’re just raring to know more.
So what are the essential parts of a WSGI application?
By way of example, these next two examples are equivalent to the first:
<span>def app(environ, start_fn): </span> start_fn<span>('200 OK', [('Content-Type', 'text/plain')]) </span> <span>return ["Hello World!\n"] </span>
<span>class app(object): </span> <span>def __init__(self, environ, start_fn): </span> self<span>.environ = environ </span> self<span>.start_fn = start_fn </span> <span>def __iter__(self): </span> self<span>.start_fn('200 OK', [('Content-Type', 'text/plain')]) </span> <span>yield "Hello World!\n" </span>
You might already be thinking of ways that you can use this information, but probably the most relevant one is writing middlewares.
Middlewares are an easy way to extend the functionality of WSGI apps. Since you need only provide a callable, you can wrap it up in other functions however you please.
For example, say we want to examine the contents of environ. We can easily create a middleware to do so, as in this example:
<span>class Application(object): </span> <span>def __call__(self, environ, start_fn): </span> start_fn<span>('200 OK', [('Content-Type', 'text/plain')]) </span> <span>yield "Hello World!\n" </span> app <span>= Application() </span>
Here, log_environ is a function that returns a function, which pretty-prints the environ argument before deferring to the original callback.
The advantage of writing middlewares this way is that the middleware and the handler don’t have to know or care about each other. You could easily bolt log_environ onto a Flask application, for example, since Flask apps are WSGI apps.
A few other useful middleware ideas:
<span>import pprint </span> <span>def handler(environ, start_fn): </span> start_fn<span>('200 OK', [('Content-Type', 'text/plain')]) </span> <span>return ["Hello World!\n"] </span> <span>def log_environ(handler): </span> <span>def _inner(environ, start_fn): </span> pprint<span>.pprint(environ) </span> <span>return handler(environ, start_fn) </span> <span>return _inner </span> app <span>= log_environ(handler) </span>
You can use reduce to apply a bunch of middleware at once if you don’t want to make a big pyramid a the bottom of your file:
<span>import pprint </span> <span>def handle_error(handler): </span> <span>def _inner(environ, start_fn): </span> <span>try: </span> <span>return handler(environ, start_fn) </span> <span>except Exception as e: </span> <span>print e # Log error </span> start_fn<span>('500 Server Error', [('Content-Type', 'text/plain')]) </span> <span>return ['500 Server Error'] </span> <span>return _inner </span> <span>def wrap_query_params(handler): </span> <span>def _inner(environ, start_fn): </span> qs <span>= environ.get('QUERY_STRING') </span> environ<span>['QUERY_PARAMS'] = urlparse.parse_qs(qs) </span> <span>return handler(environ, start_fn) </span> <span>return _inner </span>
You can also write middleware that modifies the response, by taking advantage of the start_fn argument. Here’s a middleware that reverses the output if the Content-Type header is text/plain:
<span># Applied from bottom to top on the way in, then top to bottom on the way out </span>MIDDLEWARES <span>= [wrap_query_params, </span> log_environ<span>, </span> handle_error<span>] </span> app <span>= reduce(lambda h, m: m(h), MIDDLEWARES, handler) </span>
It’s a little more tangled thanks to the separation of start_fn and response, but still perfectly workable.
Also note that, to be strictly spec-compliant with WSGI, we must check for a close
method on the response and call it if present. Legacy WSGI applications may also return a write
function instead of an iterable upon calling handler; if you want your middleware to support older applications, you may need to handle this case.
Once you start playing with raw WSGI a little bit, you start to understand why Python has literally dozens of web frameworks. WSGI makes it pretty simple to build something up starting from scratch. For example, you might be considering the problem of routing:
<span>def reverser(handler): </span> <span># A reverse function </span> rev <span>= lambda it: it[::-1] </span> <span>def _inner(environ, start_fn): </span> do_reverse <span>= [] # Must be a reference type such as a list </span> <span># Override start_fn to check the content type and set a flag </span> <span>def start_reverser(status, headers): </span> <span>for name, value in headers: </span> <span>if (name.lower() == 'content-type' </span> <span>and value.lower() == 'text/plain'): </span> do_reverse<span>.append(True) </span> <span>break </span> <span># Remember to call `start_fn` </span> start_fn<span>(status, headers) </span> response <span>= handler(environ, start_reverser) </span> <span>try: </span> <span>if do_reverse: </span> <span>return list(rev(map(rev, response))) </span> <span>return response </span> <span>finally: </span> <span>if hasattr(response, 'close'): </span> response<span>.close() </span> <span>return _inner </span>
Working with WSGI directly can be nice if you enjoy the flexibility of assembling libraries over
Of course, for non-specialized applications, you’ll probably still want to use a framework just so that edge cases are properly handled and whatnot.
There are a bunch of ways to serve WSGI apps. We already talked about Gunicorn, which is a decent option. uWSGI is another great option. Just make sure you set up something like nginx in front of these to serve static assets and you should have a solid starting point.
And that’s all there is to it!
WSGI, or Web Server Gateway Interface, is a standard interface between web servers and web applications. It plays a crucial role in Python web applications as it allows the application and the web server to communicate and send requests to each other. WSGI acts as a bridge, enabling the web server to forward requests from a client (like a web browser) to a web application. The application then processes the request and sends a response back to the client via the web server.
WSGI works by defining a common interface that allows web servers to communicate with web applications. When a client sends a request to a web server, the server uses the WSGI interface to pass that request to the web application. The application processes the request and returns a response, which the server then sends back to the client. This process allows for a consistent and reliable way for web servers and applications to interact, regardless of their specific implementations.
There are several popular WSGI servers available for Python, each with its own strengths and weaknesses. Some of the most commonly used ones include Gunicorn, uWSGI, and mod_wsgi. Gunicorn is known for its simplicity and ease of use, while uWSGI is praised for its speed and efficiency. Mod_wsgi, on the other hand, is a module for Apache servers and is often used in enterprise environments.
Deploying a Python web application using WSGI involves several steps. First, you need to install a WSGI server, such as Gunicorn or uWSGI. Next, you need to configure your web server to use the WSGI server as a proxy for handling requests to your application. This involves modifying your server’s configuration file to include the necessary WSGI settings. Finally, you need to start your WSGI server and point it to your application’s WSGI file.
Yes, you can use WSGI with Django. In fact, Django comes with a built-in WSGI application that you can use to deploy your Django projects. The Django WSGI application acts as a bridge between your Django project and the web server, allowing them to communicate and exchange requests and responses.
WSGI and ASGI are both interfaces for Python web applications, but they serve different purposes. WSGI is a synchronous interface that allows for communication between a web server and a web application. ASGI, or Asynchronous Server Gateway Interface, is an extension of WSGI that supports asynchronous operations. This means that with ASGI, your application can handle multiple requests at the same time without blocking, making it more efficient for real-time applications.
Creating a WSGI application in Python involves defining a function that accepts two arguments: an environment dictionary and a start_response callable. The environment dictionary contains information about the incoming request, while the start_response callable is used to start the response to the request. Your function should return an iterable that produces the body of the response.
Using WSGI in Python web applications offers several benefits. First, it provides a standard interface for web servers and applications, making it easier to develop and deploy Python web applications. Second, it allows for greater flexibility, as you can choose from a variety of WSGI-compatible servers and applications. Finally, it promotes code reusability, as you can use the same WSGI application with different servers and middleware.
Yes, you can use WSGI with Flask. In fact, Flask applications are WSGI applications by default. When you create a Flask application, you’re actually creating a WSGI application that the Flask framework will use to handle incoming requests and send responses.
A WSGI middleware is a component that sits between a WSGI server and a WSGI application. It can process requests before they reach the application and responses before they’re sent back to the client. Middleware can be used to implement a variety of functionalities, such as session management, URL routing, and authentication.
The above is the detailed content of Python Web Applications: The basics of WSGI. For more information, please follow other related articles on the PHP Chinese website!