Web Server Gateway Interface (Protocolo de Comunicação do Python)

O WSGI é uma especificação para uma interface simples e universal entre servidores e aplicações web ou frameworks para a linguagem de programação Python. Ele foi implementado no PEP 333.

WSGI

O WSGI é uma camada entre o Servidor Web e a Aplicação Web. Ele é um protocolo de comunicação que serve para padronizar os objetos de requisição e objetos de resposta.

Algumas linguagens de programação possuem um protocolo de comunicação próprio, o WSGI é específico do Python. O Python possui diversos frameworks web: o Flask, Django, Web2Py, FastAPI; que além de implementarem servidores próprios que suportam o WSGI configuram a função application (veremos a seguir) de maneira abstrata e que pode ser utilizada para rodar a aplicação web em outros servidores.

Note que é necessário que os servidores web forneçam suporte ao protocolo WSGI.

Como funciona?

Quando o WSGI recebe uma requisição do servidor ele precisa convertê-la para o formato do protocolo. Com isso, a requisição que chega à aplicação web conta com variáveis de requisição conhecidas como environ - com detalhes sobre a requisição - e uma função start response.

A aplicação web precisa implementar uma função que por padrão é chamada de application, ela será responsável por receber as variáveis de requisição e a função start response como callback.

Dentro de application é possível acessar as variáveis da requisição e quando todos os dados houverem sido processados chamar a função start response e retornar o body. Dessa maneira, a application gera uma resposta no formato do WSGI.

def application(environ, start_response):
	body = b"Hello World!\n"
	status = "200 OK"
	headers = [("Content-type", "text/plain")]
	start_response(status, headers)
	return [body] 

Essa é a menor aplicação web que podemos criar com Python. Precisamos agora de um servidor com suporte a WSGI que nos permita lançar o servidor em uma porta local.

Podemos utilizar bibliotecas como o gunicorn, porém o Python possui uma biblioteca built-in para testes (não deve ser utilizada em produção): wsgiref.

def application(environ, start_response):
	...
	
if __name__ == "__main__":
	from wsgiref.simple_server import make_server
	make_server("0.0.0.0", 8080, application).serve_forever()

Por não ser um módulo muito elaborado, não há mensagem dizendo que o servidor está ouvindo na porta xxxx. Entretanto, se tudo ocorreu bem, basta você acessar a porta fornecida que o body será exibido na tela no formato do content type fornecido.

Usando o HTTP Server

O Python possui a biblioteca http que permite criar servidores através do http.server como no exemplo abaixo. Essa é uma maneira mais organizada de criar a mesma aplicação que fizemos anteriormente.

from http.server import HTTPServer, BaseHTTPRequestHandler

class Index(BaseHTTPRequestHandler):
	def do_GET(self):
		self.send_response(200)
		self.send_header("Content-Type", "text/html")
		self.end_header()
		self.wfile.write("<h1>Hello World!</h1>".encode("utf-8"))

app = HTTPServer(("0.0.0.0", 8080), Index)
app.serve_forever()

Embora WSGI ainda seja muito utilizado, o ASGI é o que vem sendo mais utilizado em novos projetos. "ASGI é um sucessor espiritual do WSGI, o padrão Python de longa data para compatibilidade entre servidores web, estruturas e aplicativos. O WSGI conseguiu permitir muito mais liberdade e inovação no espaço web Python, e o objetivo do ASGI é continuar isso na terra do Python assíncrono." https://asgi.readthedocs.io/en/latest/ O Gunicorn reina como servidor WSGI. Já para ASGI vejo mais usando Uvicorn (que tem ótimo desempenho), tendo outras opções como Hypercorn, Daphne, Granian... Sendo que o Granian pode ser utilizado como servidor WSGI ou ASGI e tem desempenho espetacular pois a base dele é feita em Rust, com isso temos aplicações em Python processando muito bem milhares de requisições por segundo. Pode usar Granian para rodar Flask, Django, FastAPI, LiteStar, etc.

Obrigado pelo comentário, agregou bastante conteúdo ao post. Eu já tinha ouvido falar do Uvicorn, mas não sabia do que se tratava. Vou estudar o ASGI agora também.
Vou deixar aqui a dica de como substituir o Gunicorn / Uvicorn / Hypercorn / Daphne pelo Granian. Basta substituir: De: gunicorn projeto.wsgi:application --bind :8000 O mesmo vale para uvicorn, hypercorn, daphne... Para: WSGI granian --interface wsgi projeto.wsgi:application --port 8000 ASGI granian --interface asgi projeto.asgi:application --port 8000 É assim que uso para rodar projetos de web apps Python. O Granian tem outros parâmetros para customizar podendo escolher o número de processos, threads, etc. Tem os detalhes na página do projeto.