Johannes Klug @room2tweet
from some_magical_place import app
@app.route('/')
def hello():
return 'Hello World!'
app.run()
That's actually (almost) working code.
If you want to get started quickly pip install flask
and replace
from some_magical_place import app
with
from flask import Flask
app = Flask(__name__)
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello World!'
app.run()
That's working code.
...but it's obviously more fun if you actually know what you're doing.
So let's take a look behind some of the magic behind that innocent looking snippet of code.
Understand the basic principles of HTTP and Gateway interfaces and build a little web app in Python without any thirdparty libraries.
Disclaimer: don't expect anything that's production-ready. In reality you will want to rely on frameworks to do the "dirty work" of handling the HTTP stack for you.
...here's some theory.
HTTP is...
Request can be of different types (called HTTP verbs)
Server can respond with a number of status codes
HTTP is a stateless protocol.
"Stateless" means, all information necessary for communication is contained in request and response. The client cannot expect the server to remember what happened in a previous request.
$ curl -v http://www.google.com/?q=some_search
> GET /?q=some_search HTTP/1.1
> User-Agent: curl/7.27.0
> Host: www.google.com
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Fri, 13 Dec 2013 15:01:16 GMT
< Expires: -1
< Cache-Control: private, max-age=0
< Content-Type: text/html; charset=ISO-8859-1
<
<!doctype html>
<html itemscope="" itemtype="http://schema.org/WebPage">
<head>
<meta content="Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for." name="description">[...]
CGI (Common Gateway Interface) was devised to specify how a web server could deliver dynamic content to the client by calling executable scripts
import cgi
)Performance...:
Also: CGI has some security concerns, even though they can be worked around by a good server configuration.
The performance issue is a dealbreaker for many modern web applications.
CGI-based deployment is often infeasible.
Modern gateway interfaces "prefork" the application process.
Examples:
WSGI inspired Rack, which in turn inspired Jack and Plack
example in pseudo-code
function application(environment) {
status = "200 OK"; // set http status
headers = [ "content-type: text/plain" ]; // set headers
content = [ "Hello world!\n", "How are you today?\n" ]; // prepare content
return (status, headers, content); // return http response
}
WSGI was specified to propose a standard interface between web servers and Python web applications.
Example:
def hello_world(environment, start_response):
status = "200 OK"
headers = [("content-type", "text/plain")]
content = ["Hello world!\n", "How are you today?\n"]
start_response(status, headers)
return content
The gateway has to invoke the application callable for each request
def cgi_gateway(application):
# [...]
def write(data):
""" helper method used to write content to stdout """
# [...]
def start_response(status, response_headers, exc_info=None):
""" may only be called once, unless exc_info is given """
if exc_info:
# raise exception
elif headers_set:
raise Exception('headers already set')
headers_set[:] = [status, response_headers]
return write
# call application
environ = dict(os.environ.items())
result = application(environ, start_response)
# write data
for data in result:
if data:
write(data)
# send headers
# [...]
Code taken from PEP 333.
(At least) two methods: wsgiref (for development), uwsgi for production.
uwsgiref: Python reference implementation of WSGI.
def hello_world(environment, start_response):
""" hello world app as defined above """
# [...]
if __name__ == '__main__':
from wsgiref.simple_server import make_server
httpd = make_server('', 8000, hello_world)
print "Serving on port 8000..."
httpd.serve_forever()
uWSGI is a very advanced WSGI implementation and supports a lot of niceties, e.g. multiple application instances. It plays nicely with reverse proxy web servers, e.g. nginx.
You can get it very conveniently via pip.
We'll use what we have now learned to build a little webapp.
JSONStore. A web api to store and traverse a JSON object.
PUT /
Content:
{
"sniffles": [
{"name": "fred"},
{"name": "irma"},
{"name": "wobble"}
],
"snuffle": {"name": "gundar"}
}
GET /sniffles/
Response:
[
{"name": "fred"},
{"name": "irma"},
{"name": "wobble"}
]
GET /sniffles/0
Response:
{"name": "fred"}
PUT /sniffles/0/rank
Content:
"sniffle chief"
GET /sniffles/0
Response:
{
"name": "fred",
"rank": "sniffle chief"
}
Don't (!) build your web application using low-level Python libraries, unless you know what you're doing.
Good reasons to roll your own stack:
Bad reasons to roll your own stack:
Most people these days use Django. There are also tons of other web frameworks out there.