{ geekBlog }

a geeky blog about things

Reinvent the Wheel: Webframeworks (Teil 1)

am 2. Januar 2019 von cp

Auf der PyCon 2017 hielt Jacob Kaplan-Moss einen sehr interessanten Talk mit dem Titel "Let's build a web framework!". Davon inspiriert möchte ich mich hier auch an das Thema wagen, denn wie er in dem Talk schon sagte: "Das Rad neu zu erfinden ist eine tolle Idee, wenn man mehr über Räder lernen will."

Ein kleiner Disclaimer an dieser Stelle: Wie so oft gibt es nicht den einen richtigen Weg bzw. die eine richtige Lösung. Mein Lösungsansatz war im Folgenden, es möglichst anschaulich zu gestalten. Wer dazu noch Verbesserungsvorschläge hat kann dies gerne auf GitHub machen.

Ein paar Grundlagen

Um die Schnittstelle zwischen Webserver und Webframework oder Webapplication zu standardisieren definierte man im Dezember 2003 mit PEP 333 das Python Web Server Gateway Interface (WSGI) und machte es 2010 mit PEP 3333 auch Python 3 kompatibel. Da wir uns (zumindest ich mich) in dieser Serie um die Webframework-Seite kümmern wollen betrachten wir den Server-Teil weniger und halten uns an die Schnittstellen-Definition um mit jeden beliebigen WSGI-Server reden zu können.


Ein erstes Beispiel

Nun aber ans Eingemachte! Wir nehmen uns die Referenz-Implementation welche auf PEP 333(3) basiert und bereits eine Demo-Application enthält. Diese gibt ein "Hello World" aus und alle Umgebungsvariablen aus, welche die Anwendung gesetzt hat bzw. der Browser mitgibt.

from wsgiref.simple_server import make_server, demo_app


if __name__ == '__main__':
    with make_server('localhost', 8000, demo_app) as server:
        server.serve_forever()

Wenn man das ganze nun ausführt und http://localhost:8000/ aufruft, erhält man sein Hello World:

$ curl -i http://localhost:8000/
HTTP/1.0 200 OK
Date: Wed, 02 Jan 2019 08:13:07 GMT
Server: WSGIServer/0.2 CPython/3.7.2
Content-Type: text/plain; charset=utf-8
Content-Length: xxx

Hello world!

CONTENT_LENGTH = ''
CONTENT_TYPE = 'text/plain'
GATEWAY_INTERFACE = 'CGI/1.1'
[...]
HTTP_ACCEPT = '*/*'
HTTP_HOST = 'localhost:8000'
HTTP_USER_AGENT = 'curl/7.58.0'
LANG = 'en_GB.UTF-8'
[...]
QUERY_STRING = ''
REMOTE_ADDR = '127.0.0.1'
REMOTE_HOST = ''
REQUEST_METHOD = 'GET'
SCRIPT_NAME = ''
SERVER_NAME = 'localhost'
SERVER_PORT = '8000'
SERVER_PROTOCOL = 'HTTP/1.1'
SERVER_SOFTWARE = 'WSGIServer/0.2'
TERM = 'xterm'
wsgi.errors = <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>
wsgi.file_wrapper = <class 'wsgiref.util.FileWrapper'>
wsgi.input = <_io.BufferedReader name=4>
wsgi.multiprocess = False
wsgi.multithread = True
wsgi.run_once = False
wsgi.url_scheme = 'http'
wsgi.version = (1, 0)