<?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; Python</title>
	<atom:link href="http://blog.perplexedlabs.com/tag/python/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>Async DNS Resolution in Tornado&#8217;s AsyncHttpClient (curl multi, c-ares)</title>
		<link>http://blog.perplexedlabs.com/2010/11/01/asynchronous-dns-resolution-in-tornados-asynchttpclient-curl-multi-c-ares/</link>
		<comments>http://blog.perplexedlabs.com/2010/11/01/asynchronous-dns-resolution-in-tornados-asynchttpclient-curl-multi-c-ares/#comments</comments>
		<pubDate>Mon, 01 Nov 2010 23:50:26 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Infrastructure]]></category>
		<category><![CDATA[async]]></category>
		<category><![CDATA[asynchronous]]></category>
		<category><![CDATA[asynchttpclient]]></category>
		<category><![CDATA[c-ares]]></category>
		<category><![CDATA[curl]]></category>
		<category><![CDATA[dns]]></category>
		<category><![CDATA[libcurl]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[tornado]]></category>

		<guid isPermaLink="false">http://blog.perplexedlabs.com/?p=513</guid>
		<description><![CDATA[I learned some rather important facts about cURL's multi interface (which makes it possible to perform asynchronous HTTP requests and what Python's Tornado framework uses under the hood in it's AsyncHttpClient helper). I was investigating some intermittent issues in an application at work - transient DNS issues were causing the application to become unresponsive. This [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2010/07/01/pythons-tornado-has-swept-me-off-my-feet/' rel='bookmark' title='Permanent Link: Python&#8217;s Tornado has swept me off my feet'>Python&#8217;s Tornado has swept me off my feet</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/02/08/deployment-using-capistrano-and-webistrano-via-rails-and-phusion-passenger/' rel='bookmark' title='Permanent Link: Deployment Using Capistrano / Webistrano via Rails / Phusion Passenger'>Deployment Using Capistrano / Webistrano via Rails / Phusion Passenger</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>I learned some rather important facts about cURL's multi interface (which makes it possible to perform asynchronous HTTP requests and what Python's <a href="http://www.tornadoweb.org/">Tornado</a> framework uses under the hood in it's AsyncHttpClient helper).</p>
<p>I was investigating some intermittent issues in an application at work - transient DNS issues were causing the application to become unresponsive.  This was confusing at first because it was written to perform HTTP requests asynchronously.  The important thing here is that it was specifically DNS resolution that was failing.  As I dug deeper I realized that simple health checks, ones that did not perform any HTTP requests, were also hanging... something had to be blocking.</p>
<p>Taking a look at the Tornado source, unsurprisingly, it was leveraging the <a href="http://curl.haxx.se/libcurl/c/libcurl-multi.html">multi</a> functionality of libcurl (via pycurl).  What was surprising to me was that the DNS resolution portion of the multi interface, by default, blocks on non-windows installations.  Only when compiled with <a href="http://c-ares.haxx.se/">c-ares</a> support does it perform these async.</p>
<p>You learn something new every day!</p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2010/07/01/pythons-tornado-has-swept-me-off-my-feet/' rel='bookmark' title='Permanent Link: Python&#8217;s Tornado has swept me off my feet'>Python&#8217;s Tornado has swept me off my feet</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/02/08/deployment-using-capistrano-and-webistrano-via-rails-and-phusion-passenger/' rel='bookmark' title='Permanent Link: Deployment Using Capistrano / Webistrano via Rails / Phusion Passenger'>Deployment Using Capistrano / Webistrano via Rails / Phusion Passenger</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2010/11/01/asynchronous-dns-resolution-in-tornados-asynchttpclient-curl-multi-c-ares/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Python libwkhtmltox module &#8211; wrapping a C library using Cython &#8211; convert HTML to PDF</title>
		<link>http://blog.perplexedlabs.com/2010/09/09/python-libwkhtmltox-module-wrapping-a-c-library-using-cython/</link>
		<comments>http://blog.perplexedlabs.com/2010/09/09/python-libwkhtmltox-module-wrapping-a-c-library-using-cython/#comments</comments>
		<pubDate>Thu, 09 Sep 2010 12:00:50 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Random]]></category>
		<category><![CDATA[binding]]></category>
		<category><![CDATA[c++]]></category>
		<category><![CDATA[cython]]></category>
		<category><![CDATA[libwkhtmltox]]></category>
		<category><![CDATA[module]]></category>
		<category><![CDATA[wkhtmltoimage]]></category>
		<category><![CDATA[wkhtmltopdf]]></category>
		<category><![CDATA[wrapper]]></category>

		<guid isPermaLink="false">http://blog.perplexedlabs.com/?p=483</guid>
		<description><![CDATA[First of all, big shout out to antialize for creating wkhtmltopdf (github repo). Also, this project is being hosted on GitHub @ http://github.com/mreiferson/py-wkhtmltox. wkhtmltox What is wkhtmltox you ask? It's a utility built on Nokia's Qt framework for converting HTML (including images, CSS, and Javascript) to a PDF or image. When Qt introduced it's webkit [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2010/09/15/convert-html-to-pdf-in-php-libwkhtmltox-extension/' rel='bookmark' title='Permanent Link: Convert HTML to PDF in PHP (libwkhtmltox extension)'>Convert HTML to PDF in PHP (libwkhtmltox extension)</a></li>
<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/2009/04/22/django-url-parameter-passing-and-python-strings/' rel='bookmark' title='Permanent Link: Django URL Parameter Passing and Python Strings'>Django URL Parameter Passing and Python Strings</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>First of all, big shout out to antialize for creating <a href="http://code.google.com/p/wkhtmltopdf/">wkhtmltopdf</a> (<a href="http://github.com/antialize/wkhtmltopdf">github repo</a>).</p>
<p>Also, this project is being hosted on GitHub @ <a href="http://github.com/mreiferson/py-wkhtmltox">http://github.com/mreiferson/py-wkhtmltox</a>.</p>
<h2><a href="http://code.google.com/p/wkhtmltopdf/">wkhtmltox</a></h2>
<p>What is wkhtmltox you ask?  It's a utility built on <a href="http://qt.nokia.com/">Nokia's Qt</a> framework for converting HTML (including images, CSS, <strong>and Javascript</strong>) to a PDF or image.  When Qt introduced it's <a href="http://doc.trolltech.com/4.6/qtwebkit.html">webkit module</a> it made it relatively easy to leverage it's rendering engine to produce high quality output.</p>
<p>wkhtmltox 0.10.0beta5 introduced libwkhtmltox - a simple C API making it possible to embed this functionality in higher-level scripting languages.  I jumped at the opportunity to build Python bindings...</p>
<p>First I tried <a href="http://www.riverbankcomputing.co.uk/software/sip/intro">SIP</a>, which is actually used for the Qt bindings.  A variety of issues coupled with poor documentation led me to search for other solutions.  I won't bore you with the details because it seems there are better ways...</p>
<h2><a href="http://www.cython.org/">Cython</a></h2>
<p>Note: I'm not talking about CPython (the default Python implementation written in C).  I'm talking about a toolset to build C extensions for Python.  The two major use cases being speed or, in my case, wrapping a C library.</p>
<p>It's really easy to get up and running, Cython the language is a hybrid of C and Python.  You write scripts with a .pyx extension, make things modular with .pxd files, and handle all the building and installation with <a href="http://docs.python.org/distutils/">distutils</a>.</p>
<p>Let's take a look at the API libwkhtmltox exposes (I'm only showing lines relevant to this post):</p>
<pre class="brush: cpp; title: ;">
struct wkhtmltopdf_global_settings;
typedef struct wkhtmltopdf_global_settings wkhtmltopdf_global_settings;

struct wkhtmltopdf_object_settings;
typedef struct wkhtmltopdf_object_settings wkhtmltopdf_object_settings;

struct wkhtmltopdf_converter;
typedef struct wkhtmltopdf_converter wkhtmltopdf_converter;

CAPI int wkhtmltopdf_init(int use_graphics);
CAPI int wkhtmltopdf_deinit();

CAPI const char * wkhtmltopdf_version();

CAPI wkhtmltopdf_global_settings * wkhtmltopdf_create_global_settings();
CAPI wkhtmltopdf_object_settings * wkhtmltopdf_create_object_settings();

CAPI int wkhtmltopdf_set_global_setting(wkhtmltopdf_global_settings * settings, const char * name, const char * value);
CAPI int wkhtmltopdf_set_object_setting(wkhtmltopdf_object_settings * settings, const char * name, const char * value);

CAPI wkhtmltopdf_converter * wkhtmltopdf_create_converter(wkhtmltopdf_global_settings * settings);
CAPI void wkhtmltopdf_destroy_converter(wkhtmltopdf_converter * converter);

CAPI int wkhtmltopdf_convert(wkhtmltopdf_converter * converter);
CAPI void wkhtmltopdf_add_object(wkhtmltopdf_converter * converter, wkhtmltopdf_object_settings * setting, const char * data);

CAPI int wkhtmltopdf_http_error_code(wkhtmltopdf_converter * converter);
</pre>
<p>And let's also look at the basic example provided with the wkhtmltopdf source distribution (again, only relevant lines shown):</p>
<pre class="brush: cpp; title: ;">
wkhtmltopdf_init(false);
gs = wkhtmltopdf_create_global_settings();
wkhtmltopdf_set_global_setting(gs, &quot;out&quot;, &quot;test.pdf&quot;);
os = wkhtmltopdf_create_object_settings();
wkhtmltopdf_set_object_setting(os, &quot;page&quot;, &quot;http://doc.trolltech.com/4.6/qstring.html&quot;);
c = wkhtmltopdf_create_converter(gs);
wkhtmltopdf_add_object(c, os, NULL);
wkhtmltopdf_convert(c);
wkhtmltopdf_destroy_converter(c);
wkhtmltopdf_deinit();
</pre>
<p>I found it helpful to try to identify which methods needed to be exposed to user-space and which ones could be safely abstracted behind a cleanly wrapped Pythonic API.  I wrote the following test script to work towards:</p>
<pre class="brush: python; title: ;">
import wkhtmltox

pdf = wkhtmltox.Pdf()
pdf.set_global_setting('out', 'test.pdf')
pdf.set_object_setting('path', 'http://www.google.com')
pdf.convert()
</pre>
<p>The initialization of a Pdf instance handles all of the internal libwkhtmltox initialization.  See below for the .pyx script.  Of interest is how C structs and functions are exposed to the Cython script and then wrapped, with appropriate names, as Python methods of a class.  Astute observers will also notice that some of the function declarations differ slightly from the original libwkhtmltox header file.  In most cases Cython doesn't need the const, CAPI and other specific declarations.  Also, <strong>bint</strong> hints that even though the return value is an int we should convert this to a boolean on the Python side.</p>
<pre class="brush: python; title: ;">
cdef extern from &quot;wkhtmltox/pdf.h&quot;:
    struct wkhtmltopdf_converter:
        pass

    struct wkhtmltopdf_object_settings:
        pass

    struct wkhtmltopdf_global_settings:
        pass

    bint wkhtmltopdf_init(int use_graphics)
    bint wkhtmltopdf_deinit()
    char *wkhtmltopdf_version()

    wkhtmltopdf_global_settings *wkhtmltopdf_create_global_settings()
    wkhtmltopdf_object_settings *wkhtmltopdf_create_object_settings()

    bint wkhtmltopdf_set_global_setting(wkhtmltopdf_global_settings *settings, char *name, char *value)
    bint wkhtmltopdf_get_global_setting(wkhtmltopdf_global_settings *settings, char *name, char *value, int vs)
    bint wkhtmltopdf_set_object_setting(wkhtmltopdf_object_settings *settings, char *name, char *value)
    bint wkhtmltopdf_get_object_setting(wkhtmltopdf_object_settings *settings, char *name, char *value, int vs)

    wkhtmltopdf_converter *wkhtmltopdf_create_converter(wkhtmltopdf_global_settings *settings)
    void wkhtmltopdf_destroy_converter(wkhtmltopdf_converter *converter)

    bint wkhtmltopdf_convert(wkhtmltopdf_converter *converter)
    void wkhtmltopdf_add_object(wkhtmltopdf_converter *converter, wkhtmltopdf_object_settings *setting, char *data)

    int wkhtmltopdf_http_error_code(wkhtmltopdf_converter *converter)

cdef extern from &quot;wkhtmltox/image.h&quot;:
    struct wkhtmltoimage_global_settings:
        pass

    struct wkhtmltoimage_converter:
        pass

    bint wkhtmltoimage_init(int use_graphics)
    bint wkhtmltoimage_deinit()
    char *wkhtmltoimage_version()

    wkhtmltoimage_global_settings *wkhtmltoimage_create_global_settings()

    bint wkhtmltoimage_set_global_setting(wkhtmltoimage_global_settings *settings, char *name, char *value)
    bint wkhtmltoimage_get_global_setting(wkhtmltoimage_global_settings *settings, char *name, char *value, int vs)

    wkhtmltoimage_converter *wkhtmltoimage_create_converter(wkhtmltoimage_global_settings *settings, char *data)
    void wkhtmltoimage_destroy_converter(wkhtmltoimage_converter *converter)

    bint wkhtmltoimage_convert(wkhtmltoimage_converter *converter)

    int wkhtmltoimage_http_error_code(wkhtmltoimage_converter *converter)

cdef class Pdf:
    cdef wkhtmltopdf_global_settings *_c_global_settings
    cdef wkhtmltopdf_object_settings *_c_object_settings
    cdef bint last_http_error_code

    def __cinit__(self):
        wkhtmltopdf_init(0)
        self._c_global_settings = wkhtmltopdf_create_global_settings()
        self._c_object_settings = wkhtmltopdf_create_object_settings()

    def __dealloc__(self):
        wkhtmltopdf_deinit();

    def set_global_setting(self, char *name, char *value):
        return wkhtmltopdf_set_global_setting(self._c_global_settings, name, value)

    def set_object_setting(self, char *name, char *value):
        return wkhtmltopdf_set_object_setting(self._c_object_settings, name, value)

    def convert(self):
        cdef wkhtmltopdf_converter *c
        c = wkhtmltopdf_create_converter(self._c_global_settings)
        wkhtmltopdf_add_object(c, self._c_object_settings, NULL)
        ret = wkhtmltopdf_convert(c)
        self.last_http_error_code = wkhtmltopdf_http_error_code(c)
        wkhtmltopdf_destroy_converter(c)
        return ret

    def http_error_code(self):
        return self.last_http_error_code

cdef class Image:
    cdef wkhtmltoimage_global_settings *_c_global_settings
    cdef bint last_http_error_code

    def __cinit__(self):
        wkhtmltoimage_init(0)
        self._c_global_settings = wkhtmltoimage_create_global_settings()

    def __dealloc__(self):
        wkhtmltoimage_deinit();

    def set_global_setting(self, char *name, char *value):
        return wkhtmltoimage_set_global_setting(self._c_global_settings, name, value)

    def convert(self):
        cdef wkhtmltoimage_converter *c
        c = wkhtmltoimage_create_converter(self._c_global_settings, NULL)
        ret = wkhtmltoimage_convert(c)
        self.last_http_error_code = wkhtmltoimage_http_error_code(c)
        wkhtmltoimage_destroy_converter(c)
        return ret

    def http_error_code(self):
        return self.last_http_error_code
</pre>
<p>This is really my first attempt to get something working.  I'm sure there are bugs and perhaps better ways to go about this.  I always welcome questions/feedback.</p>
<p>I'm going to continue to support this project at <a href="http://github.com/mreiferson/py-wkhtmltox">http://github.com/mreiferson/py-wkhtmltox</a> - watch it!</p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2010/09/15/convert-html-to-pdf-in-php-libwkhtmltox-extension/' rel='bookmark' title='Permanent Link: Convert HTML to PDF in PHP (libwkhtmltox extension)'>Convert HTML to PDF in PHP (libwkhtmltox extension)</a></li>
<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/2009/04/22/django-url-parameter-passing-and-python-strings/' rel='bookmark' title='Permanent Link: Django URL Parameter Passing and Python Strings'>Django URL Parameter Passing and Python Strings</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2010/09/09/python-libwkhtmltox-module-wrapping-a-c-library-using-cython/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<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>
		<item>
		<title>Python data sharing in the multiprocessing module</title>
		<link>http://blog.perplexedlabs.com/2010/03/04/python-data-sharing-in-the-multiprocessing-module/</link>
		<comments>http://blog.perplexedlabs.com/2010/03/04/python-data-sharing-in-the-multiprocessing-module/#comments</comments>
		<pubDate>Thu, 04 Mar 2010 13:00:08 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[multiprocessing]]></category>

		<guid isPermaLink="false">http://blog.perplexedlabs.com/?p=430</guid>
		<description><![CDATA[Python's multiprocessing module is a great tool that abstracts the details of forking and managing child processes in an interface inspired by the threading module. The benefit to using processes over threads is that you effectively avoid the issues of the GIL (Global Interpreter Lock). I wanted to share my experience with sharing static data [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2010/09/09/python-libwkhtmltox-module-wrapping-a-c-library-using-cython/' rel='bookmark' title='Permanent Link: Python libwkhtmltox module &#8211; wrapping a C library using Cython &#8211; convert HTML to PDF'>Python libwkhtmltox module &#8211; wrapping a C library using Cython &#8211; convert HTML to PDF</a></li>
<li><a href='http://blog.perplexedlabs.com/2010/07/01/pythons-tornado-has-swept-me-off-my-feet/' rel='bookmark' title='Permanent Link: Python&#8217;s Tornado has swept me off my feet'>Python&#8217;s Tornado has swept me off my feet</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/11/10/setup-python-25-mod_wsgi-and-django-10-on-centos-5-cpanel/' rel='bookmark' title='Permanent Link: Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel)'>Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel)</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>Python's <a href="http://docs.python.org/library/multiprocessing.html">multiprocessing</a> module is a great tool that abstracts the details of forking and managing child processes in an interface inspired by the <a href="http://docs.python.org/library/threading.html">threading</a> module.  The benefit to using processes over threads is that you effectively avoid the issues of the GIL (Global Interpreter Lock).</p>
<p>I wanted to share my experience with sharing static data between the parent and the forked children.  The solution I ultimately went with is trivially implemented and works well.  It takes advantage of the fact that the children import the same modules of the parent.  If you house your data in a shared module, it's accessible in both places.</p>
<p>The directory structure looks like this:</p>
<blockquote>
<pre>
mypackage/
    __init__.py
    mp.py
    myglobals.py
myscript.py
</pre>
</blockquote>
<p>Here's my light wrapper around the multiprocessing module, mp.py:</p>
<pre class="brush: python; title: ;">
import multiprocessing

import MySQLdb

import myglobals

# handles each unit of work, in this case a SQL query
def worker_do(sql):
    myglobals.cursor.execute(sql)

# called once upon worker initialization
def worker_init():
    myglobals.conn = MySQLdb.connect(**myglobals.config['db'])
    myglobals.cursor = myglobals.conn.cursor()
    myglobals.cursor.execute('SET AUTOCOMMIT=1')

# wrapper for multiprocessing module
def do_work(queue, num_processes):
    pool = multiprocessing.Pool(num_processes, initializer=worker_init)
    pool.map(worker_do, queue, 1)
    pool.close()
    pool.join()
</pre>
<p>And here's my example script, myscript.py:</p>
<pre class="brush: python; title: ;">
import os
import sys

import mp
import myglobals

def main():
   # anything in the myglobals module will be accessible by the child processes
   # we could then programatically retrieve this config info from a file
   # via ConfigParser
   #
   # for simplicity I hard-coded it here
   myglobals.config = {
      'db': {
         'host': 'db1',
         'user': 'dbuser',
         'passwd': 'dbpasswd',
         'db': 'dbase'
      }
   }

   # build a whole bunch of queries to perform via the workers
   queries = build_queries()

   # perform the multiprocessing operation
   mp.do_work(queries, 4)

   return 0

if __name__ == '__main__':
   sys.exit(main())
</pre>
<p>In this example the benefit would be to keep your database configuration code DRY - and share that data with the child processes.</p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2010/09/09/python-libwkhtmltox-module-wrapping-a-c-library-using-cython/' rel='bookmark' title='Permanent Link: Python libwkhtmltox module &#8211; wrapping a C library using Cython &#8211; convert HTML to PDF'>Python libwkhtmltox module &#8211; wrapping a C library using Cython &#8211; convert HTML to PDF</a></li>
<li><a href='http://blog.perplexedlabs.com/2010/07/01/pythons-tornado-has-swept-me-off-my-feet/' rel='bookmark' title='Permanent Link: Python&#8217;s Tornado has swept me off my feet'>Python&#8217;s Tornado has swept me off my feet</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/11/10/setup-python-25-mod_wsgi-and-django-10-on-centos-5-cpanel/' rel='bookmark' title='Permanent Link: Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel)'>Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel)</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2010/03/04/python-data-sharing-in-the-multiprocessing-module/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Django Up In Your CRON</title>
		<link>http://blog.perplexedlabs.com/2010/03/02/django-up-in-your-cron/</link>
		<comments>http://blog.perplexedlabs.com/2010/03/02/django-up-in-your-cron/#comments</comments>
		<pubDate>Tue, 02 Mar 2010 13:00:42 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[cron]]></category>

		<guid isPermaLink="false">http://blog.perplexedlabs.com/?p=354</guid>
		<description><![CDATA[For one off scripts for a particular project: #!/usr/bin/env python from django.core.management import setup_environ from myapp import settings setup_environ(settings) # do some stuff Related posts:Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel) Getting Started with Django and Python &#8211; First Impressions Adventures in Django and Python &#8211; Part III


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2008/11/10/setup-python-25-mod_wsgi-and-django-10-on-centos-5-cpanel/' rel='bookmark' title='Permanent Link: Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel)'>Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel)</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/02/08/getting-started-with-django-and-python-first-impressions/' rel='bookmark' title='Permanent Link: Getting Started with Django and Python &#8211; First Impressions'>Getting Started with Django and Python &#8211; First Impressions</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/08/13/adventures-in-django-and-python-part-iii/' rel='bookmark' title='Permanent Link: Adventures in Django and Python &#8211; Part III'>Adventures in Django and Python &#8211; Part III</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>For one off scripts for a particular project:</p>
<pre class="brush: python; title: ;">
#!/usr/bin/env python

from django.core.management import setup_environ
from myapp import settings
setup_environ(settings)

# do some stuff
</pre>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2008/11/10/setup-python-25-mod_wsgi-and-django-10-on-centos-5-cpanel/' rel='bookmark' title='Permanent Link: Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel)'>Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel)</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/02/08/getting-started-with-django-and-python-first-impressions/' rel='bookmark' title='Permanent Link: Getting Started with Django and Python &#8211; First Impressions'>Getting Started with Django and Python &#8211; First Impressions</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/08/13/adventures-in-django-and-python-part-iii/' rel='bookmark' title='Permanent Link: Adventures in Django and Python &#8211; Part III'>Adventures in Django and Python &#8211; Part III</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2010/03/02/django-up-in-your-cron/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Deployment Using Capistrano / Webistrano via Rails / Phusion Passenger</title>
		<link>http://blog.perplexedlabs.com/2010/02/08/deployment-using-capistrano-and-webistrano-via-rails-and-phusion-passenger/</link>
		<comments>http://blog.perplexedlabs.com/2010/02/08/deployment-using-capistrano-and-webistrano-via-rails-and-phusion-passenger/#comments</comments>
		<pubDate>Mon, 08 Feb 2010 13:00:32 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[Infrastructure]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Ruby on Rails]]></category>
		<category><![CDATA[deployment]]></category>
		<category><![CDATA[mod_wsgi]]></category>
		<category><![CDATA[mongrel]]></category>
		<category><![CDATA[passenger]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[phusion passenger]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[subversion]]></category>

		<guid isPermaLink="false">http://blog.perplexedlabs.com/?p=408</guid>
		<description><![CDATA[I finally got around to setting up a more sophisticated deployment system for some of my apps. These apps include some built on a custom PHP framework and others that are Python / Django apps. I figured I'd share my experience... Why is a high-level deployment infrastructure important? Deployment is something that should be simple, [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/01/13/installing-ruby-enterprise-edition-with-phusion-passenger/' rel='bookmark' title='Permanent Link: Installing Ruby Enterprise Edition with Phusion Passenger'>Installing Ruby Enterprise Edition with Phusion Passenger</a></li>
<li><a href='http://blog.perplexedlabs.com/2010/09/08/improved-deploycleanup-for-capistrano/' rel='bookmark' title='Permanent Link: Improved deploy:cleanup for capistrano'>Improved deploy:cleanup for capistrano</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/11/30/8-books-to-get-a-developer-for-the-holidays/' rel='bookmark' title='Permanent Link: 8 Books To Get A Developer For The Holidays'>8 Books To Get A Developer For The Holidays</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>I finally got around to setting up a more sophisticated deployment system for some of my apps.  These apps include some built on a custom PHP framework and others that are Python / Django apps.  I figured I'd share my experience...</p>
<p>Why is a high-level deployment infrastructure important?  Deployment is something that should be simple, accessible, and repeatable.  It should be as close to a "single click" as possible.  Previously, for me, it was a bash script that exported some SVN branches.  While this worked fine, as projects progress, you want some accountability, history, and the ability to roll back mission critical applications when something goes wrong with a deploy.</p>
<p><a href="http://www.capify.org/">Capistrano</a> is an open source, command line, deployment tool that provides all of these features.  It's written in Ruby.  You leverage a variety of built in "recipes" (Capistrano's term for a deployment script) that execute certain procedures to deploy an app.  Out-of-the-box it's ideally built to deploy a Rails app.  However, after some minor tweaks it can deploy most anything and do it well.  It can restart servers, update symlinks, change permissions - pretty much anything.  It assumes you access your POSIX compliant server via SSH via the same password (or have ssh keys setup).</p>
<p><a href="http://labs.peritor.com/webistrano">Webistrano</a> is an open source web front-end for Capistrano.  It's a convenience layer that abstracts the command line away and provides an interface to perform the same tasks.  This interface shows history as well as providing a convenient GUI for creating new deployment projects, stages, and recipes.  Highly recommended.</p>
<p>Let's get down to business.  This post makes a few assumptions about things you've already installed and used previously.</p>
<ul>
<li><a href="http://www.ruby-lang.org/en/">Ruby 1.8.5</a>+
<li><a href="http://rubyforge.org/projects/rubygems/">RubyGems</a>
<li><a href="http://www.mysql.com/">MySQL 5.0</a>+
<li><a href="http://www.apache.org/">Apache 2</a>+
<li><a href="http://subversion.tigris.org/">Subversion</a> and a repository containing the "production" branch of your app.
</ul>
<h3>Installing Capistrano</h3>
<p>Well, this is an easy one (you probably want to do this as root):</p>
<blockquote><p>
gem install capistrano
</p></blockquote>
<h3>Installing Webistrano</h3>
<p>Also fairly easy, with a little splash of configuration.</p>
<blockquote><p>
# wget http://labs.peritor.com/webistrano/attachment/wiki/Download/webistrano-1.4.zip<br />
# unzip webistrano-1.4.zip<br />
# mv webistrano-1.4 /path/to/where/you/want/webistrano
</p></blockquote>
<p>Setup the database tables and create a new webistrano user (obviously be conscious of your security preferences for access to your database in the host and password portions):</p>
<blockquote><p>
# mysql<br />
mysql> CREATE DATABASE `webistrano`;<br />
mysql> CREATE USER 'webistrano'@'localhost' IDENTIFIED BY 'password';<br />
mysql> GRANT ALL PRIVILEGES ON `webistrano`.* TO 'webistrano'@'localhost' WITH GRANT OPTION;
</p></blockquote>
<p>Now, in the directory where you placed webistrano you're going to want to copy <i>config/database.yml.sample</i> to <i>config/database.yml</i>.  Edit this file, in the production area, to match your database settings.  By default the file expects a socket to connect, you can chase this by specifying <i>host:</i> and <i>port:</i>.  (Keep in mind Webistrano is simply a Rails app).</p>
<p>You should now be able to have Rails migrate the new database you created.  In the webistrano directory:</p>
<blockquote><p>
# RAILS_ENV=production rake db:migrate
</p></blockquote>
<p>Finally, copy <i>config/webistrano_config.rb.sample</i> to <i>config/webistrano_config.rb</i> and edit according to your preferred mail settings.</p>
<p>We can now test to see if webistrano is working properly by serving it via mongrel:</p>
<blockquote><p>
# ruby script/server -d -e production -p 3000
</p></blockquote>
<p>This starts a single mongrel daemon, using the production environment, listening on port 3000.  You should now be able to hit http://127.0.0.1:3000/ and get the Webistrano login prompt.  If this is working, kill that mongrel instance.</p>
<p>For longer term serving I decided to go with Phusion Passenger (essentially mod_rails for Apache).  It's a nearly zero configuration solution for serving a rails app and will feel at home to anyone with experience serving PHP apps via Apache and mod_php.</p>
<h3>Installing Phusion Passenger</h3>
<p>Again, as root:</p>
<blockquote><p>
# gem install passenger<br />
# passenger-install-apache2-module
</p></blockquote>
<p>The second command will invoke an installer which compiled Passenger and provides instructions on integrating it into your Apache config.  Essentially, edit your httpd.conf as follows (<strong>these were specific to my install, make sure to use the ones provide by the installer for you</strong>):</p>
<blockquote><p>
LoadModule passenger_module /usr/lib/ruby/gems/1.8/gems/passenger-2.2.9/ext/apache2/mod_passenger.so<br />
PassengerRoot /usr/lib/ruby/gems/1.8/gems/passenger-2.2.9<br />
PassengerRuby /usr/bin/ruby
</p></blockquote>
<p>Now you can simply add VirtualHost entries to your httpd.conf for any of your Rails apps.  Let's add one for Webistrano:</p>
<blockquote><p>
&lt;VirtualHost *:80&gt;<br />
ServerName webistrano.mydomain.com<br />
DocumentRoot /path/to/webistrano/public<br />
&lt;/VirtualHost&gt;
</p></blockquote>
<p>Yes, Passenger makes it that simple.  Add configuration directives as needed for your environment.</p>
<p>Now Webistrano should be serving from the VirtualHost you specified, seamlessly, via Passenger.</p>
<h3>Deploying A Non-Rails App</h3>
<p>Now the fun stuff.</p>
<p>Capistrano breaks things down into projects, stages, and recipes.  Each app you want managed by capistrano should be it's own project.  Each project should have a stage for at least production and optionally staging and development.</p>
<p>Hosts are added globally and form the targets of a deploy for any given project.  Hosts can include web, app, and database servers.</p>
<p>Deployments in Capistrano are done to a child directory under "releases" named via the date and time of the deployment.  By default 5 releases are kept and available to rollback to.  Upon successful deployment a symlink (default is called "current" and can be modified via the <i>current_path</i> configuration variable) is updated to that release directory.  It is this symlink that should be targeted by your webserver (your DocumentRoot in Apache).</p>
<p>Capistrano also creates a "shared" directory that is symlinked to in each release useful for storing logs and other data that should be maintained through each deployment.</p>
<p>For non-rails apps you'll use the "Pure File" project type when creating your new project.  Upon project creation you can add configuration variables specific to your project.  I recommend using <i>:export</i> instead of <i>:checkout</i> for <i>deploy_via</i> for production subversion deployments as this doesn't expose .svn directories.  Use an SSH user that has enough permissions to create directories where your deploy will occur or, specify <i>use_sudo</i> to true and create a new configuration variable <i>admin_runner</i> and set it to the same user as <i>runner</i>.</p>
<p>Add a stage to your new project for "production".  In the "Manage Hosts" page add a new host for each of your application servers.  Then add each host as a target of your "production" stage of your project.</p>
<p>At this point you should be able to execute the "Setup" task for your "production" stage.  This is a one time task that simply creates the directories.</p>
<p>Assuming this went successfully, try doing a "Deploy" and see if that finishes without error.  You might have to play around with permissions and other minor issues - post a comment if you have any specific questions.</p>
<p>For my PHP framework there are a couple specific tasks I wanted to run in addition to the default Capistrano tasks.  You do this by creating custom recipes in the "Manage Recipes" page in Webistrano.  Recipes are simply procedures written in ruby.  Here's what my recipe looks like:</p>
<pre class="brush: ruby; title: ;">
namespace :deploy do
	task :setup, :except =&gt; { :no_release =&gt; true } do
		dirs = [deploy_to, releases_path, shared_path]
		dirs += shared_children.map { |d| File.join(shared_path, d) }
		run &quot;#{try_sudo} mkdir -p #{dirs.join(' ')} &amp;&amp; #{try_sudo} chmod g+w #{dirs.join(' ')}&quot;
		run &quot;chmod 777 #{shared_path}/log&quot;
	end

	task :finalize_update, :except =&gt; { :no_release =&gt; true } do
		run &quot;mkdir -p #{latest_release}/app/tmp&quot;
		run &quot;chmod -R 777 #{latest_release}/app/tmp&quot;
		run &quot;rm -rf #{latest_release}/app/logs&quot;
		run &quot;ln -s #{shared_path}/log #{latest_release}/app/logs&quot;
		run &quot;cp #{latest_release}/public_html/.htaccess-production #{latest_release}/public_html/.htaccess&quot;
		run &quot;cp #{latest_release}/app/config/config-production.php #{latest_release}/app/config/config.php&quot;
		run &quot;cp #{latest_release}/app/config/db-default.php #{latest_release}/app/config/db.php&quot;
		run &quot;cp #{latest_release}/app/config/memcache-default.php #{latest_release}/app/config/memcache.php&quot;
	end
end
</pre>
<p>If you're not familiar with Ruby - what this code is essentially doing is overwriting two tasks in the :deploy namespace with my custom code.</p>
<p>The first, :setup, simply duplicates the base :setup functionality discussed above (creating the releases and shared directories) and chmods the shared log directory to be writable.</p>
<p>The second, :finalize_update, performs a variety of configuration tasks for a PHP app built with my framework.  Also, you'll notice that I'm removing my app's logs directory and symlinking to the shared log directory.  This way all releases will log to the same directory, consistently.  </p>
<p>In my case all of these procedures are command line instructions.  Alternatively, you can do a variety of things leveraging the full breadth of the Ruby language and any gem you'd like to introduce.   Things such as accessing your CDN API to clear image, JS, or CSS caching, etc.</p>
<h3>Deploying Django Apps</h3>
<p>First off it's worth noting that I serve my Django apps via mod_wsgi.  To make the deployment process easier here's what my app.wsgi script looks like:</p>
<pre class="brush: python; title: ;">
import os
import sys

appdir = os.path.normpath(os.path.join(os.path.realpath(os.path.dirname(__file__)), '..'))
sys.path.insert(0, appdir)
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
os.environ['PYTHON_EGG_CACHE'] = os.path.join(appdir, '.python-eggs')
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()
</pre>
<p>This code allows us to avoid having to hardcode paths in the wsgi script (and thus avoid having to change them when we deploy).  It assumes the following directory structure:</p>
<blockquote><p>
.python-eggs (egg cache)<br />
apps (apps path is added to python system path in settings.py)<br />
public (where your .wsgi script resides)<br />
site_media<br />
templates<br />
settings.py<br />
settings-production.py (used for deploy)<br />
urls.py<br />
...
</p></blockquote>
<p>If you follow this convention, the following Capistrano recipe works great:</p>
<pre class="brush: ruby; title: ;">
namespace :deploy do
	task :setup, :except =&gt; { :no_release =&gt; true } do
		dirs = [deploy_to, releases_path, shared_path]
		dirs += shared_children.map { |d| File.join(shared_path, d) }
		run &quot;#{try_sudo} mkdir -p #{dirs.join(' ')} &amp;&amp; #{try_sudo} chmod g+w #{dirs.join(' ')}&quot;
		run &quot;chmod 777 #{shared_path}/log&quot;
	end

	task :finalize_update, :except =&gt; { :no_release =&gt; true } do
		run &quot;rm -rf #{latest_release}/logs&quot;
		run &quot;ln -s #{shared_path}/log #{latest_release}/logs&quot;
		run &quot;cp #{latest_release}/settings-production.py #{latest_release}/settings.py&quot;
		run &quot;mkdir -p #{latest_release}/.python-eggs&quot;
		run &quot;chmod 777 #{latest_release}/.python-eggs&quot;
	end
end
</pre>
<h3>Fin</h3>
<p>This should give you a nice intro to leveraging Capistrano via Webistrano.  Feel free to comment with questions, suggestions, or anything else!</p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/01/13/installing-ruby-enterprise-edition-with-phusion-passenger/' rel='bookmark' title='Permanent Link: Installing Ruby Enterprise Edition with Phusion Passenger'>Installing Ruby Enterprise Edition with Phusion Passenger</a></li>
<li><a href='http://blog.perplexedlabs.com/2010/09/08/improved-deploycleanup-for-capistrano/' rel='bookmark' title='Permanent Link: Improved deploy:cleanup for capistrano'>Improved deploy:cleanup for capistrano</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/11/30/8-books-to-get-a-developer-for-the-holidays/' rel='bookmark' title='Permanent Link: 8 Books To Get A Developer For The Holidays'>8 Books To Get A Developer For The Holidays</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2010/02/08/deployment-using-capistrano-and-webistrano-via-rails-and-phusion-passenger/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>8 Books To Get A Developer For The Holidays</title>
		<link>http://blog.perplexedlabs.com/2009/11/30/8-books-to-get-a-developer-for-the-holidays/</link>
		<comments>http://blog.perplexedlabs.com/2009/11/30/8-books-to-get-a-developer-for-the-holidays/#comments</comments>
		<pubDate>Mon, 30 Nov 2009 13:30:06 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Book Reviews]]></category>
		<category><![CDATA[Clojure]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[Infrastructure]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Ruby on Rails]]></category>
		<category><![CDATA[book review]]></category>
		<category><![CDATA[clojure]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[programmer]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://blog.perplexedlabs.com/?p=385</guid>
		<description><![CDATA[Send this to your significant other/parent/relative/friend so, instead of that sweater, you get one of these nuggets of awesome this Christmas. The Pragmatic Programmer: From Journeyman to Master Write better, cleaner, more maintainable code. Learn how to manage your projects and focus on shipping your product. With insight that covers the gamut of software development [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/06/01/be-language-agnostic-solve-the-problem/' rel='bookmark' title='Permanent Link: Be Language Agnostic &#8211; Solve the Problem!'>Be Language Agnostic &#8211; Solve the Problem!</a></li>
<li><a href='http://blog.perplexedlabs.com/2010/02/08/deployment-using-capistrano-and-webistrano-via-rails-and-phusion-passenger/' rel='bookmark' title='Permanent Link: Deployment Using Capistrano / Webistrano via Rails / Phusion Passenger'>Deployment Using Capistrano / Webistrano via Rails / Phusion Passenger</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/07/28/django-1-0-template-development-sample-chapter-serving-multiple-templates/' rel='bookmark' title='Permanent Link: Django 1.0 Template Development: Sample Chapter &#8220;Serving Multiple Templates&#8221;'>Django 1.0 Template Development: Sample Chapter &#8220;Serving Multiple Templates&#8221;</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>Send this to your significant other/parent/relative/friend so, instead of that sweater, you get one of these nuggets of awesome this Christmas.</p>
<h3><a href="http://www.amazon.com/gp/product/020161622X?ie=UTF8&#038;tag=perplabs-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=020161622X">The Pragmatic Programmer: From Journeyman to Master</a><img src="http://www.assoc-amazon.com/e/ir?t=perplabs-20&#038;l=as2&#038;o=1&#038;a=020161622X" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></h3>
<p>Write better, cleaner, more maintainable code.  Learn how to manage your projects and focus on shipping your product.  With insight that covers the gamut of software development from low level to management<br />
this one is a must have for anyone involved in this industry.</p>
<h3><a href="http://www.amazon.com/gp/product/1934356344?ie=UTF8&#038;tag=perplabs-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=1934356344">The Passionate Programmer: Creating a Remarkable Career in Software Development</a><img src="http://www.assoc-amazon.com/e/ir?t=perplabs-20&#038;l=as2&#038;o=1&#038;a=1934356344" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></h3>
<p>Highly recommended!  <a href="http://blog.perplexedlabs.com/2009/07/29/book-review-the-passionate-programmer/">Read my full review</a>.</p>
<h3><a href="http://www.amazon.com/gp/product/0735619670?ie=UTF8&#038;tag=perplabs-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=0735619670">Code Complete: A Practical Handbook of Software Construction</a><img src="http://www.assoc-amazon.com/e/ir?t=perplabs-20&#038;l=as2&#038;o=1&#038;a=0735619670" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></h3>
<p>Another classic "software construction" book.  Sharpen your saw with timeless information that can be applied to any project in any language.  Less bugs, more productivity, more programmer happiness.</p>
<h3><a href="http://www.amazon.com/gp/product/1430219483?ie=UTF8&#038;tag=perplabs-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=1430219483">Coders at Work</a><img src="http://www.assoc-amazon.com/e/ir?t=perplabs-20&#038;l=as2&#038;o=1&#038;a=1430219483" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></h3>
<p>This one is different.  Written as a set of interview transcripts with 15 legendary industry giants, this book is a fantastic insight into how some of the great minds think.  It's inspiring to hear it from the source, must have!</p>
<h3><a href="http://www.amazon.com/gp/product/1934356336?ie=UTF8&#038;tag=perplabs-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=1934356336">Programming Clojure</a><img src="http://www.assoc-amazon.com/e/ir?t=perplabs-20&#038;l=as2&#038;o=1&#038;a=1934356336" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></h3>
<p>A developer should learn at least one new language a year.  This year that language should be Clojure.  Clojure is a dynamic, general purpose, language targeting the Java virtual machine and designed for multi-threaded use.  It's growing popularity, ability to leverage the Java standard library, and its multi-threaded nature make this a must have.</p>
<h3><a href="http://www.amazon.com/gp/product/0201835959?ie=UTF8&#038;tag=perplabs-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=0201835959">The Mythical Man-Month: Essays on Software Engineering</a><img src="http://www.assoc-amazon.com/e/ir?t=perplabs-20&#038;l=as2&#038;o=1&#038;a=0201835959" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></h3>
<p>Another classic.  Primarily discusses project management from the perspective of Fred Brooks and his experiences at IBM.  Brooks' Law states that "adding manpower to a late software project makes it later".</p>
<h3><a href="http://www.amazon.com/gp/product/0321344758?ie=UTF8&#038;tag=perplabs-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=0321344758">Don't Make Me Think: A Common Sense Approach to Web Usability</a><img src="http://www.assoc-amazon.com/e/ir?t=perplabs-20&#038;l=as2&#038;o=1&#038;a=0321344758" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></h3>
<p>Web developers should always keep in mind the user of the product their creating.  Usability becomes increasingly important as applications move to the web.  The design and usability of your app can make or break its success.  This classic is a must read.</p>
<h3><a href="http://www.amazon.com/gp/product/0201633612?ie=UTF8&#038;tag=perplabs-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=0201633612">Design Patterns: Elements of Reusable Object-Oriented Software</a><img src="http://www.assoc-amazon.com/e/ir?t=perplabs-20&#038;l=as2&#038;o=1&#038;a=0201633612" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></h3>
<p>This classic known most commonly as the "gang of four" book is the definitive reference on design patterns.  Covering all of the most common cases and time and time again serving as an invaluable source of information.</p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/06/01/be-language-agnostic-solve-the-problem/' rel='bookmark' title='Permanent Link: Be Language Agnostic &#8211; Solve the Problem!'>Be Language Agnostic &#8211; Solve the Problem!</a></li>
<li><a href='http://blog.perplexedlabs.com/2010/02/08/deployment-using-capistrano-and-webistrano-via-rails-and-phusion-passenger/' rel='bookmark' title='Permanent Link: Deployment Using Capistrano / Webistrano via Rails / Phusion Passenger'>Deployment Using Capistrano / Webistrano via Rails / Phusion Passenger</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/07/28/django-1-0-template-development-sample-chapter-serving-multiple-templates/' rel='bookmark' title='Permanent Link: Django 1.0 Template Development: Sample Chapter &#8220;Serving Multiple Templates&#8221;'>Django 1.0 Template Development: Sample Chapter &#8220;Serving Multiple Templates&#8221;</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2009/11/30/8-books-to-get-a-developer-for-the-holidays/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Setup Python 2.6.4, mod_wsgi 2.6, and Django 1.1.1 on CentOS 5.3 (cPanel)</title>
		<link>http://blog.perplexedlabs.com/2009/11/15/setup-python-2-6-4-mod_wsgi-2-6-and-django-1-1-1-on-centos-5-3-cpanel/</link>
		<comments>http://blog.perplexedlabs.com/2009/11/15/setup-python-2-6-4-mod_wsgi-2-6-and-django-1-1-1-on-centos-5-3-cpanel/#comments</comments>
		<pubDate>Sun, 15 Nov 2009 20:37:38 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[Infrastructure]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[centos]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[mod_wsgi]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[mysql-python]]></category>
		<category><![CDATA[mysqldb]]></category>
		<category><![CDATA[setuptools]]></category>

		<guid isPermaLink="false">http://blog.perplexedlabs.com/?p=378</guid>
		<description><![CDATA[This is an update to my previous how-to Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel). The biggest reason why I chose to go with Python 2.5 at the time was because the MySQL Python (MySQLdb) package didn't support Python 2.6. The 1.2.3c1 release does so that roadblock is lifted. The instructions [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2008/11/10/setup-python-25-mod_wsgi-and-django-10-on-centos-5-cpanel/' rel='bookmark' title='Permanent Link: Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel)'>Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel)</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/02/04/building-a-rails-capable-slice-from-scratch/' rel='bookmark' title='Permanent Link: Ruby On Rails and SliceHost Part 1: Initial Setup'>Ruby On Rails and SliceHost Part 1: Initial Setup</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/02/08/getting-started-with-django-and-python-first-impressions/' rel='bookmark' title='Permanent Link: Getting Started with Django and Python &#8211; First Impressions'>Getting Started with Django and Python &#8211; First Impressions</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>This is an update to my previous how-to <a href="http://blog.perplexedlabs.com/2008/11/10/setup-python-25-mod_wsgi-and-django-10-on-centos-5-cpanel/">Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel)</a>.</p>
<p>The biggest reason why I chose to go with Python 2.5 at the time was because the MySQL Python (MySQLdb) package didn't support Python 2.6.  The 1.2.3c1 release does so that roadblock is lifted.</p>
<p>The instructions are identical - nothing has really changed in that regard.  Just change the references from Python 2.5 to 2.6.  Here are the links to the versions I'm using successfully:</p>
<blockquote><p>
Python 2.6.4: <a href="http://www.python.org/ftp/python/2.6.4/Python-2.6.4.tgz">http://www.python.org/ftp/python/2.6.4/Python-2.6.4.tgz</a></p>
<p>setuptools 0.6c11: <a href="http://pypi.python.org/packages/2.6/s/setuptools/setuptools-0.6c11-py2.6.egg#md5=bfa92100bd772d5a213eedd356d64086">http://pypi.python.org/packages/2.6/s/setuptools/setuptools-0.6c11-py2.6.egg#md5=bfa92100bd772d5a213eedd356d64086</a></p>
<p>MySQLdb 1.2.3c1: <a href="http://sourceforge.net/projects/mysql-python/files/mysql-python-test/1.2.3c1/MySQL-python-1.2.3c1.tar.gz/download">http://sourceforge.net/projects/mysql-python/files/mysql-python-test/1.2.3c1/MySQL-python-1.2.3c1.tar.gz/download</a></p>
<p>mod_wsgi 2.6: <a href="http://modwsgi.googlecode.com/files/mod_wsgi-2.6.tar.gz">http://modwsgi.googlecode.com/files/mod_wsgi-2.6.tar.gz</a></p>
<p>Django 1.1.1: <a href="http://www.djangoproject.com/download/1.1.1/tarball/">http://www.djangoproject.com/download/1.1.1/tarball/</a>
</p></blockquote>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2008/11/10/setup-python-25-mod_wsgi-and-django-10-on-centos-5-cpanel/' rel='bookmark' title='Permanent Link: Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel)'>Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel)</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/02/04/building-a-rails-capable-slice-from-scratch/' rel='bookmark' title='Permanent Link: Ruby On Rails and SliceHost Part 1: Initial Setup'>Ruby On Rails and SliceHost Part 1: Initial Setup</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/02/08/getting-started-with-django-and-python-first-impressions/' rel='bookmark' title='Permanent Link: Getting Started with Django and Python &#8211; First Impressions'>Getting Started with Django and Python &#8211; First Impressions</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2009/11/15/setup-python-2-6-4-mod_wsgi-2-6-and-django-1-1-1-on-centos-5-3-cpanel/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Adventures in Django and Python &#8211; Part III</title>
		<link>http://blog.perplexedlabs.com/2009/08/13/adventures-in-django-and-python-part-iii/</link>
		<comments>http://blog.perplexedlabs.com/2009/08/13/adventures-in-django-and-python-part-iii/#comments</comments>
		<pubDate>Thu, 13 Aug 2009 14:00:38 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[session]]></category>
		<category><![CDATA[ternary]]></category>

		<guid isPermaLink="false">http://www.perplexedlabs.com/?p=307</guid>
		<description><![CDATA[Read my previous two posts on Django and Python - Part I and Part II I've been working on a project management tool suite in Django. It's been a great side project to really experiment with Django in real-world scenarios. Forms At times I feel like I fight with newforms. In particular, it lacks the [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/04/22/django-url-parameter-passing-and-python-strings/' rel='bookmark' title='Permanent Link: Django URL Parameter Passing and Python Strings'>Django URL Parameter Passing and Python Strings</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/03/20/django-and-python-first-impressions-part-ii/' rel='bookmark' title='Permanent Link: Django and Python First Impressions &#8211; Part II'>Django and Python First Impressions &#8211; Part II</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/02/08/getting-started-with-django-and-python-first-impressions/' rel='bookmark' title='Permanent Link: Getting Started with Django and Python &#8211; First Impressions'>Getting Started with Django and Python &#8211; First Impressions</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p><em>Read my previous two posts on Django and Python - <a href="http://blog.perplexedlabs.com/2009/02/08/getting-started-with-django-and-python-first-impressions/">Part I</a> and <a href="http://blog.perplexedlabs.com/2009/03/20/django-and-python-first-impressions-part-ii/">Part II</a></em></p>
<p>I've been working on a project management tool suite in Django.  It's been a great side project to really experiment with Django in real-world scenarios.</p>
<h3>Forms</h3>
<p>At times I feel like I fight with newforms.  In particular, it lacks the ability to specify basic class or style attributes for a given form field from within the template.  I'd like to be able to more finely tune the display of the field, directly within the template, with a style attribute or a class.  Is it suggested you write your own custom form field widget for a single element?  I've been getting around this by doing the following:</p>
<pre class="brush: python; title: ;">
&lt;input type=&quot;text&quot; name=&quot;{{ todo_form.item.name }}&quot; style=&quot;width: 720px;&quot;/&gt;
</pre>
<p>This gets more complicated if you want to set a style attribute for a form field that's a select box (for a ForeignKey model field, for example).  </p>
<pre class="brush: python; title: ;">
&lt;label for=&quot;category&quot;&gt;Category:&lt;/label&gt; &lt;select name=&quot;{{ category_form.project.name }}&quot; style=&quot;width: 221px;&quot;&gt;
{% for choice_val, choice_label in category_form.project.field.choices %}
	&lt;option value=&quot;{{ choice_val }}&quot;&gt;{{ choice_label }}&lt;/option&gt;
{% endfor %}
</pre>
<p>Is this a good use case for template tags?  I feel like I'm missing something here.</p>
<p>On the positive side, it was an absolute pleasure to work with multiple forms on a single page submitted to and processed by a single view.  This is primarily thanks to prefixes.  Excellent, that's how easy it should be.</p>
<h3>Ternary Operator</h3>
<p><strong>Update:</strong> <em>It's been pointed out in comments (thanks!) that Python 2.5 introduced a ternary operator.  It's syntax is as follows:</em></p>
<pre class="brush: python; title: ;">
label = &quot;true&quot; if booleanVariable else &quot;false&quot;
</pre>
<p>I also ran into a minor Python syntax issue.  I love the ternary operator in languages that offer it.  It's a concise, one-line, syntax for an if-else clause.  Consider the following PHP code:</p>
<pre class="brush: php; title: ;">
$label = $booleanVariable ? 'true' : 'false';

// the above is identical to the following block:
if($booleanVariable) {
   $label = 'true';
} else {
   $label = 'false';
}
</pre>
<p>Python unfortunately lacks this syntactic sugar.  Fortunately, however, you can effectively accomplish the same thing by doing this:</p>
<pre class="brush: python; title: ;">
label = (booleanVariable and 'true' or 'false')

# the above is equivalent to the following block:
if booleanVariable:
   label = 'true'
else:
   label = 'false'
</pre>
<h3>Sessions</h3>
<p>Django has built in support for sessions.  By default, sessions last longer than the lifecycle of the user's browser.  I personally think it should be the other way around.  It's easily changed though (in your settings.py):</p>
<pre class="brush: python; title: ;">
SESSION_EXPIRE_AT_BROWSER_CLOSE = False
</pre>
<h3>Views</h3>
<p>In one of my views I wanted to test whether a filtered result set was empty or not.  I was curious whether this was the "pythonic" way to accomplish this:</p>
<pre class="brush: python; title: ;">
account = get_object_or_404(Account, pk=account_id)
if account.useraccount_set.filter(user__exact=request.user) != []:
</pre>
<p>Also, with respect to views and passing context to the response, sometimes it's an efficient shortcut to use <strong>locals()</strong> instead of explicitly typing out all the variables you'd like to expose.  locals() returns a dictionary of all variables defined within the local scope.</p>
<pre class="brush: python; title: ;">
def myview(request, id):
    account = get_object_or_404(Account, pk=id)
    new_account_form = NewAccountForm()

    return render_to_response('myview.html', locals())
</pre>
<p>More soon!</p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/04/22/django-url-parameter-passing-and-python-strings/' rel='bookmark' title='Permanent Link: Django URL Parameter Passing and Python Strings'>Django URL Parameter Passing and Python Strings</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/03/20/django-and-python-first-impressions-part-ii/' rel='bookmark' title='Permanent Link: Django and Python First Impressions &#8211; Part II'>Django and Python First Impressions &#8211; Part II</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/02/08/getting-started-with-django-and-python-first-impressions/' rel='bookmark' title='Permanent Link: Getting Started with Django and Python &#8211; First Impressions'>Getting Started with Django and Python &#8211; First Impressions</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2009/08/13/adventures-in-django-and-python-part-iii/feed/</wfw:commentRss>
		<slash:comments>18</slash:comments>
		</item>
		<item>
		<title>Django 1.1 Released</title>
		<link>http://blog.perplexedlabs.com/2009/07/29/django-1-1-released/</link>
		<comments>http://blog.perplexedlabs.com/2009/07/29/django-1-1-released/#comments</comments>
		<pubDate>Wed, 29 Jul 2009 13:18:12 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.perplexedlabs.com/?p=327</guid>
		<description><![CDATA[The Django project just announced that v1.1 is ready! Go here for the full release notes. Some notable changes include support for aggregate functions from within the ORM. This includes COUNT(), MAX(), MIN(), etc. Also, the concept of "unmanaged" models which allow you to easily support tables that pre-existed or were created by some other [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/11/15/setup-python-2-6-4-mod_wsgi-2-6-and-django-1-1-1-on-centos-5-3-cpanel/' rel='bookmark' title='Permanent Link: Setup Python 2.6.4, mod_wsgi 2.6, and Django 1.1.1 on CentOS 5.3 (cPanel)'>Setup Python 2.6.4, mod_wsgi 2.6, and Django 1.1.1 on CentOS 5.3 (cPanel)</a></li>
<li><a href='http://blog.perplexedlabs.com/2010/01/14/jquery-1-4-released/' rel='bookmark' title='Permanent Link: jQuery 1.4 Released'>jQuery 1.4 Released</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/08/13/adventures-in-django-and-python-part-iii/' rel='bookmark' title='Permanent Link: Adventures in Django and Python &#8211; Part III'>Adventures in Django and Python &#8211; Part III</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>The Django project just <a href="http://www.djangoproject.com/weblog/2009/jul/29/1-point-1/">announced</a> that v1.1 is ready!  Go <a href="http://docs.djangoproject.com/en/dev/releases/1.1/">here</a> for the full release notes.</p>
<p>Some notable changes include support for aggregate functions from within the ORM.  This includes COUNT(), MAX(), MIN(), etc.</p>
<p>Also, the concept of "unmanaged" models which allow you to easily support tables that pre-existed or were created by some other means to not be maintained by Django.</p>
<p>Another useful addition is deferred fields.  This is useful in instances where you store large amounts of text or binary data in a table and may not necessarily want to retrieve that data in a query.</p>
<p>Looks like an excellent point release.  Go grab it now!</p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/11/15/setup-python-2-6-4-mod_wsgi-2-6-and-django-1-1-1-on-centos-5-3-cpanel/' rel='bookmark' title='Permanent Link: Setup Python 2.6.4, mod_wsgi 2.6, and Django 1.1.1 on CentOS 5.3 (cPanel)'>Setup Python 2.6.4, mod_wsgi 2.6, and Django 1.1.1 on CentOS 5.3 (cPanel)</a></li>
<li><a href='http://blog.perplexedlabs.com/2010/01/14/jquery-1-4-released/' rel='bookmark' title='Permanent Link: jQuery 1.4 Released'>jQuery 1.4 Released</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/08/13/adventures-in-django-and-python-part-iii/' rel='bookmark' title='Permanent Link: Adventures in Django and Python &#8211; Part III'>Adventures in Django and Python &#8211; Part III</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2009/07/29/django-1-1-released/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

