<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Perplexed Labs &#187; web.py</title>
	<atom:link href="http://blog.perplexedlabs.com/tag/web-py/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.perplexedlabs.com</link>
	<description>web development war stories from the frontlines to the backend</description>
	<lastBuildDate>Mon, 16 May 2011 14:19:38 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.2</generator>
		<item>
		<title>Python&#8217;s Tornado has swept me off my feet</title>
		<link>http://blog.perplexedlabs.com/2010/07/01/pythons-tornado-has-swept-me-off-my-feet/</link>
		<comments>http://blog.perplexedlabs.com/2010/07/01/pythons-tornado-has-swept-me-off-my-feet/#comments</comments>
		<pubDate>Thu, 01 Jul 2010 13:00:37 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Infrastructure]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[asynchronous]]></category>
		<category><![CDATA[non-blocking]]></category>
		<category><![CDATA[REST]]></category>
		<category><![CDATA[tornado]]></category>
		<category><![CDATA[web.py]]></category>

		<guid isPermaLink="false">http://blog.perplexedlabs.com/?p=457</guid>
		<description><![CDATA[I've been working with Python's Tornado for about 2 months now and I love it. Tornado is a non-blocking web server written in Python. It's structure is similar to web.py so users of that popular Python web framework will feel right at home. This is a structure that lends itself really well to developing RESTful [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2010/03/04/python-data-sharing-in-the-multiprocessing-module/' rel='bookmark' title='Permanent Link: Python data sharing in the multiprocessing module'>Python data sharing in the multiprocessing module</a></li>
<li><a href='http://blog.perplexedlabs.com/2010/07/24/tornado-1-0-released/' rel='bookmark' title='Permanent Link: Tornado 1.0 Released'>Tornado 1.0 Released</a></li>
<li><a href='http://blog.perplexedlabs.com/2010/11/01/asynchronous-dns-resolution-in-tornados-asynchttpclient-curl-multi-c-ares/' rel='bookmark' title='Permanent Link: Async DNS Resolution in Tornado&#8217;s AsyncHttpClient (curl multi, c-ares)'>Async DNS Resolution in Tornado&#8217;s AsyncHttpClient (curl multi, c-ares)</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>I've been working with Python's <a href="http://www.tornadoweb.org/">Tornado</a> for about 2 months now and I love it.</p>
<p>Tornado is a non-blocking web server written in Python.  It's structure is similar to web.py so users of that popular Python web framework will feel right at home.  This is a structure that lends itself really well to developing RESTful APIs as the methods you write to handle incoming requests are named after the HTTP methods used:</p>
<pre class="brush: python; title: ;">
class PlaceHandler(tornado.web.RequestHandler):
    def get(self, id):
        # respond to a GET
        self.write('GETting something')

    def post(self):
        # respond to a POST
        self.write('POSTing something')
</pre>
<p>You match URI paths to "handlers" (the <em>controller</em> for those MVC folk) via a list of regex, handler tuples that instantiate an "application".</p>
<pre class="brush: python; title: ;">
application = tornado.web.Application([
    (r&quot;/place&quot;, PlaceHandler),
    (r&quot;/place/([0-9]+)&quot;, PlaceHandler)
])

if __name__ == &quot;__main__&quot;:
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
</pre>
<p>As usual any values that are captured from the regex are passed, in order, to the method that receives the request in the handler.</p>
<p>Because of it's non-blocking nature Tornado bundles an asynchronous HTTP client for use internally.  Additional modules include a command line and config file convenience library, escaping, 3rd party authentication (Facebook, Twitter, etc.), a wrapper around MySQLdb, and templating.  All in all this makes it a formidable web framework in its own right, especially if you're looking for something that's light and <a href="http://www.tornadoweb.org/documentation#performance">FAST</a>.</p>
<p>In production, I'm running 4 Tornado instances per server behind <a href="http://nginx.org/">nginx</a>.</p>
<p>One issue not addressed out of the box was daemonizing the Tornado instance.  I added PID file management and the ability to daemonize as follows (pid.py module follows):</p>
<pre class="brush: python; title: ;">
# capture stdout/err in logfile
log_file = 'tornado.%s.log' % options.port
log = open(os.path.join(settings.log_path, log_file), 'a+')

# check pidfile
pidfile_path = settings.PIDFILE_PATH % options.port
pid.check(pidfile_path)

# daemonize
daemon_context = daemon.DaemonContext(stdout=log, stderr=log, working_directory='.')
with daemon_context:
    # write the pidfile
    pid.write(pidfile_path)

    # initialize the application
    http_server = tornado.httpserver.HTTPServer(application.app)
    http_server.listen(options.port, '127.0.0.1')

    try:
        # enter the Tornado IO loop
        tornado.ioloop.IOLoop.instance().start()
    finally:
        # ensure we remove the pidfile
        pid.remove(pidfile_path)
</pre>
<p>And now the pid.py module:</p>
<pre class="brush: python; title: ;">
# pid.py - module to help manage PID files
import os
import logging
import fcntl
import errno

def check(path):
    # try to read the pid from the pidfile
    try:
        logging.info(&quot;Checking pidfile '%s'&quot;, path)
        pid = int(open(path).read().strip())
    except IOError, (code, text):
        pid = None
        # re-raise if the error wasn't &quot;No such file or directory&quot;
        if code != errno.ENOENT:
            raise

    # try to kill the process
    try:
        if pid is not None:
            logging.info(&quot;Killing PID %s&quot;, pid)
            os.kill(pid, 9)
    except OSError, (code, text):
        # re-raise if the error wasn't &quot;No such process&quot;
        if code != errno.ESRCH:
            raise

def write(path):
    try:
        pid = os.getpid()
        pidfile = open(path, 'wb')
        # get a non-blocking exclusive lock
        fcntl.flock(pidfile.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
        # clear out the file
        pidfile.seek(0)
        pidfile.truncate(0)
        # write the pid
        pidfile.write(str(pid))
        logging.info(&quot;Writing PID %s to '%s'&quot;, pid, path)
    except:
        raise
    finally:
        try:
            pidfile.close()
        except:
            pass

def remove(path):
    try:
        # make sure we delete our pidfile
        logging.info(&quot;Removing pidfile '%s'&quot;, path)
        os.unlink(path)
    except:
        pass
</pre>
<p>I'm going to follow up this post another on how I added a simple concept of "models" and an easy way to perform MySQL transactions.  Let me know if you have any specific questions!</p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2010/03/04/python-data-sharing-in-the-multiprocessing-module/' rel='bookmark' title='Permanent Link: Python data sharing in the multiprocessing module'>Python data sharing in the multiprocessing module</a></li>
<li><a href='http://blog.perplexedlabs.com/2010/07/24/tornado-1-0-released/' rel='bookmark' title='Permanent Link: Tornado 1.0 Released'>Tornado 1.0 Released</a></li>
<li><a href='http://blog.perplexedlabs.com/2010/11/01/asynchronous-dns-resolution-in-tornados-asynchttpclient-curl-multi-c-ares/' rel='bookmark' title='Permanent Link: Async DNS Resolution in Tornado&#8217;s AsyncHttpClient (curl multi, c-ares)'>Async DNS Resolution in Tornado&#8217;s AsyncHttpClient (curl multi, c-ares)</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2010/07/01/pythons-tornado-has-swept-me-off-my-feet/feed/</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
	</channel>
</rss>

