DEV Community

Cover image for Explaining WSGI and Python Applications
Juan  Benito
Juan Benito

Posted on

Explaining WSGI and Python Applications

WSGI is one of the concepts you read when you start developing your python web project. However is a widely used concept, it might be quite difficult to understand when you read it the first couple of times. this article will answer the next questions

  • What is WSGI?
  • Why was WSGI created?
  • How does WSGI help us when we deploy our python web projects?

Definition

WSGI means Web Server Gateway Interface, it is the standard which define the rules followed by the Application Servers, such as Gunicorn, and the Frameworks we use to develop our applications, such as Django.

History

When python started being popular as programming language, many developers wanted to use it for the prominent web world until then dominated by PHP. In order to use python code on the server, the community developed an Apache module called mod_python.

However mod_python was popular, many vulnerabilities and bugs came up. Finally, the community decided to create a formal specification.

This specification was defined in the proposals PEP 0333 and PEP 3333. The goals of these proposals was to create a standard solution safe and secure, but flexible enough to be used with different python technologies.

The found solution was WSGI.

Theory

This standard defined how an Application Server should load a Python Application and work together. It allowed developers to create different Application Servers which are able to work with any python application if they meet the WSGI specification.

Alt Text
Python Web Project communication diagram

The picture above illustrate how our application WSGI server and our python application communicate with each other. They are completely decoupled, they don't need to know each other, they only need to satisfy the WSGI standard.

The WSGI server is in charge to handle the information received by the peripherals of our server (it might be using a network socket, file, stdin...). The incoming information will be adapted to the WSGI standard, the server will load our python application and it will send the information to it. It will become the Gateway of our application.

The Python Application needs to understand how the WSGI server will load it, how he will receive the information, and how it will need to be returned. Our application will bootstrap itself in the appropriate way to be loaded by the server, and being able to communicate with it.

Code

The theory is fine, but as developers we like to see the theory translated into code. The examples below are an Application Server (Funicorn) and Python Application (Fango), neither of these programs are production ready, are not tested and might contains bugs. They are examples to illustrate how the standard works in a general way.

In order to get easier to understand, only the most important parts of the standard are implemented.

Application Server

# Sibling Fake of gunicorn
import os
import sys
from typing import List, Tuple


class Funicorn:

    def __init__(self, app_path: str):
        self.application = load_application(application_path)
        self.buffer_output = sys.stdout.buffer
        self.set_environ()

    def set_environ(self):
        self.environ = {**os.environ}

        self.environ['wsgi.input'] = sys.stdin.buffer
        self.environ['wsgi.errors'] = sys.stderr.buffer

    def write(self, data: bytes):
        self.buffer_output.write(data)

    def write_headers(self, status, headers: List[Tuple(str, str)]):
        self.write(f'Status: {status}\r\n'.encode())

        for hkey, hvalue in headers:
            self.write(f'{hkey}: {hvalue}\r\n'.encode())

            self.write('\r\n'.encode())

    def start_response(self, status, response_headers: List[Tuple(str, str)], **kwargs):
        self.write_headers(status, response_headers)

    def finish(self):
        self.buffer_output.flush()

    def run(self):
        response_body = self.application({**self.environ}, self.start_response)
        self.write(response_body)


if __name__ == 'main':
    application_path = 'path_to_fango.app'
    funicorn = Funicorn(application_path)

    funicorn.run()

Enter fullscreen mode Exit fullscreen mode

Gist - Funicorn

Python Application

# Sibling fake of Django
from typing import Dict, Any


class Fango:

    def __call__(environ: Dict[str, Any], start_response) -> bytes:
        HELLO = b'Hello!'
        status = '200 OK'
        response_headers = [('Content-type', 'text/plain')]
        start_response(status, response_headers)
        return HELLO


app = Fango()
Enter fullscreen mode Exit fullscreen mode

Gist - Fango

Conclusion

The WSGI standard boost the number of libraries created by the python community in the web applications fields, allowing the developers to decouple the server technology from the python application in a isolated way.

The framework developers who didn't need to worry about how to handle the socket connection, the income requests or concurrent connections, was able to spend the time in the creation of many python frameworks loved by many of us as Django, Flask, Pyramid... and so on.

If you would like to dive deeper about how WSGI works I would recommend you go directly to the proposals PEP 0333 and PEP 3333, they contains the whole specification.

Discussion (0)