<?php include('HEADER.php'); ?>

<h1>Python Web Development</h1>

<h2>WSGI</h2>

<p><a href="https://www.python.org/dev/peps/pep-0333/">WSGI</a> is the Python Web Server Gateway Interface, an interface between a webserver and a web framework. Like CGI, WSGI is a protocol. Many webservers, including Apache and nginx, support WSGI. This means we don't have to worry about different web servers when writing our application (and developers of different frameworks don't have to worry about particular web servers either).</p>

<p><a href="http://gunicorn.org/">Gunicorn</a> is a nice little WSGI web server to use for development. It's even installable into our virtualenvs with pip, like: <code>virtualenv myproject; cd myproject; source bin/activate; pip install gunicorn</code>. However, some frameworks, including Bottle, come with their own testing/development WSGI servers, so don't rush to install Gunicorn. Something like Apache's <code>mod_wsgi</code> may be more suitable for production use.</p>

<ul>
<li>With CGI, the webserver knows very little about the execution environment of the script. With WSGI, there's an agreed interface about what the webserver can expect from the script and what the script can expect from the webserver.</li>
<li>With CGI, the webserver waits, gets a request, forks a process to deal with it, and washes its hands. With WSGI, there may be a long-running daemon-like process to keep the script resident, saving startup time.</li>
<li>A WSGI server may do more or less sophisticated caching or state persistence/management that would be impossible with fire-and-forget CGI.</li>
<li>WSGI improves/embellishes the CGI design in several ways, like automatically parsing the HTTP request headers, supplying any POST input as a file-like object, and providing helper functions to format responses.</li>
<li>Apache's mod_wsgi can run embedded in Apache or as a separate daemon.</li>
</ul>

<p>Create <code>test.py</code>:</p>

<pre>
def hello(environ, start_response):
    """Very simple WSGI demo."""
    msg = b'Hello, world!\n'
    status = '200 OK'
    response_headers = [
        ('Content-type','text/plain'),
        ('Content-Length', str(len(msg)))
    ]
    start_response(status, response_headers)
    return iter([msg])
</pre>

<p>Fire up the WSGI server, like <code>$ gunicorn -b localhost:8888 test:hello</code>, and hit <a href="http://localhost:8888">http://localhost:8888</a>.</p>

<h2><code>pip</code> and <code>virtualenv</code></h2>

<pre>sudo apt-get install python-pip python-virtualenv</pre>

<p><code>pip</code> is a package manager for Python, like CPAN for Perl or gems for Ruby.</p>

<p><strong>Don't</strong> install packages globally on a system (e.g. &mdash; don't `sudo pip whatever`). Instead, use virtualenv.</p>

<p><code>virtualenv</code> lets us install pip packages in an isolated directory , just for our project, so that packages and versions don't conflict like they would installed globally on a system. It also make packaging our project for distribution easier.</p>

<pre>$ cd ~/repo/
$ virtualenv myproject
New python executable in myproject/bin/python
Installing setuptools............done.
Installing pip...............done.</pre>

<p>Activate the virtual environment, and install packages:</p>

<pre>$ cd ~/repo/myproject/
$ source bin/activate
&lt;myproject&gt;$ pip install bottle</pre>

<p>When we're done working in the virtual environment:</p>

<pre>&lt;myproject&gt;$ deactivate
$ </pre>

<p><code>&lt;myproject&gt;$ pip freeze &gt; requirements.txt</code> creates a file showing all the packages and package versions in the environment, which will be useful if we (or another user or developer) want to recreate/deploy the app.</p>

<h3>.git</h3>

<p>When we set up our .git repo in <code>myproject/</code>, we'll probably want to include the virtualenv directories in .gitignore:</p>

<pre>bin/
include/
lib/
local/ </pre>

<h2>Frameworks</h2>

<p>Django and Flask are two of <a href="https://wiki.python.org/moin/WebFrameworks">Pythons's many web frameworks</a>. <a href="https://www.djangoproject.com/">Django</a> is a batteries-included framework; <a href="http://flask.pocoo.org/">Flask</a> is light-weight.</p>

<p>We'll be looking at the framework <a href="http://bottlepy.org/docs/dev/index.html">Bottle</a>, which is similar to Flask.</p>

<pre>
from bottle import route, run, template

@route('/hello')
def hello():
    return "Hello, World!"
@route('/hello/&lt;name&gt;')
def helloName(name):
    return template('Hello, {{name}}, how are you?', name=name)

run(host='localhost', port=8080, debug=True)
</pre>

<p>We run it like: <code>python ./hello.py</code>, and point our web browser at <a href="http://127.0.0.1:8080/hello">http://127.0.0.1:8080/hello</a> or <a href="http://127.0.0.1:8080/hello/Paul">http://127.0.0.1:8080/hello/Paul</a>.</p>

<p>The <a href="http://bottlepy.org/docs/dev/tutorial.html">Bottle tutorial</a> is surprisingly thorough, and the whole framework is a single file with under 4,000 lines of <a href="https://github.com/bottlepy/bottle/blob/master/bottle.py">source code</a>.</p>

<h2>References</h2>

<ul>
<li><a href="http://docs.python-guide.org/en/latest/scenarios/web/">Python Guide: Web Applications</a></li>
<li><a href="http://dabapps.com/blog/introduction-to-pip-and-virtualenv-python/">pip and virtualenv</a></li>
<li><a href="http://docs.python-guide.org/en/latest/dev/virtualenvs/">Python Guide: Virtual Environments</a></li>
<li><a href="http://lucumr.pocoo.org/2007/5/21/getting-started-with-wsgi/">Getting Started with WSGI</a></li>
<li><a href="http://uwsgi-docs.readthedocs.io/en/latest/WSGIquickstart.html">WSGI Quickstart</a></li>
<li><a href="http://stackoverflow.com/questions/219110/how-python-web-frameworks-wsgi-and-cgi-fit-together">How Python WSGI and CGI fit together</a></li>
<li><a href="https://www.python.org/dev/peps/pep-0333/">PEP 333 -- Python Web Server Gateway Interface v1.0</a></li>
<li><a href="https://www.python.org/dev/peps/pep-3333/">PEP 3333 -- Python Web Server Gateway Interface v1.0.1 (update that includes Python 3 support)</a></li>
<li><a href=""></a></li>
</ul>

<?php include('../FOOTER.php'); ?>
