<?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>krondo</title>
	<atom:link href="http://krondo.com/blog/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://krondo.com</link>
	<description>The website of Dave Peticolas</description>
	<lastBuildDate>Wed, 01 Sep 2010 01:49:27 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Book: You Are Not a Gadget: A Manifesto</title>
		<link>http://krondo.com/?p=2620</link>
		<comments>http://krondo.com/?p=2620#comments</comments>
		<pubDate>Sat, 28 Aug 2010 06:14:52 +0000</pubDate>
		<dc:creator>dave</dc:creator>
				<category><![CDATA[Books]]></category>

		<guid isPermaLink="false">http://krondo.com/?p=2620</guid>
		<description><![CDATA[A generally thoughtful critique of web and open culture. Lanier always has an interesting perspective on things. He&#8217;s at his best when articulating his alternative vision, rather than attacking the present, I would say.]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.amazon.com/exec/obidos/ASIN/0307269647/krondonet-20"><img src="http://ecx.images-amazon.com/images/I/41mTgUd-d7L._SL160_.jpg" alt="" /></a></p>
<p>A generally thoughtful critique of web and open culture. Lanier always has an interesting perspective on things. He&#8217;s at his best when articulating his alternative vision, rather than attacking the present, I would say.</p>
]]></content:encoded>
			<wfw:commentRss>http://krondo.com/?feed=rss2&amp;p=2620</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>An Introduction to Asynchronous Programming and Twisted</title>
		<link>http://krondo.com/?p=2571</link>
		<comments>http://krondo.com/?p=2571#comments</comments>
		<pubDate>Sun, 22 Aug 2010 21:17:01 +0000</pubDate>
		<dc:creator>dave</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://krondo.com/?p=2571</guid>
		<description><![CDATA[Part 18: Deferreds En Masse This continues the introduction started here. You can find an index to the entire series here. Introduction In the last Part we learned a new way of structuring sequential asynchronous callbacks using a generator. Thus, including deferreds, we now have two techniques for chaining asynchronous operations together. Sometimes, though, we [...]]]></description>
			<content:encoded><![CDATA[<h2>Part 18: Deferreds En Masse</h2>
<p>This continues the introduction started <a href="?p=1209">here</a>. You can find an index to the entire series <a href="?p=1327">here</a>.</p>
<h3>Introduction</h3>
<p>In the last Part we learned a new way of structuring sequential asynchronous callbacks using a generator. Thus, including deferreds, we now have two techniques for chaining asynchronous operations together.</p>
<p>Sometimes, though, we want to run a group of asynchronous operations in &#8220;parallel&#8221;. Since Twisted is single-threaded they won&#8217;t really run concurrently, but the point is we want to use asynchronous I/O to work on a group of tasks as fast as possible. Our poetry clients, for example, download poems from multiple servers at the same time, rather than one server after another. That was the whole point of using Twisted for getting poetry, after all.</p>
<p>And, as a result, all our poetry clients have had to solve this problem: how do you know when all the asynchronous operations you have started are done? So far we have solved this by collecting our results into a list (like the <a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-client-7/get-poetry.py#L160"><code>results</code></a> list in client 7.0) and checking the length of the list. We have to be careful to collect failures as well as successful results, otherwise a single failure will cause the program to run forever, thinking there&#8217;s still work left to do.</p>
<p>As you might expect, Twisted includes an abstraction you can use to solve this problem and we&#8217;re going to take a look at it today.</p>
<h3>The DeferredList</h3>
<p>The <a href="http://twistedmatrix.com/trac/browser/tags/releases/twisted-10.1.0/twisted/internet/defer.py#593"><code>DeferredList</code></a> class allows us to treat a list of deferred objects as a single deferred. That way we can start a bunch of asynchronous operations and get notified only when all of them have finished (regardless of whether they succeeded or failed). Let&#8217;s look at some examples.</p>
<p>In <a href="http://github.com/jdavisp3/twisted-intro/blob/master/deferred-list/deferred-list-1.py#L1"><tt>deferred-list/deferred-list-1.py</tt></a> you will find this code:</p>
<pre>from twisted.internet import defer

def got_results(res):
    print 'We got:', res

print 'Empty List.'
d = defer.DeferredList([])
print 'Adding Callback.'
d.addCallback(got_results)</pre>
<p>And if you run it, you will get this output:</p>
<pre>Empty List.
Adding Callback.
We got: []</pre>
<p>Some things to notice:</p>
<ul>
<li>A <code>DeferredList</code> is created from a Python <code>list</code>. In this case the list is empty, but we&#8217;ll soon see that the list elements must all be <code>Deferred</code> objects.</li>
<li>A <code>DeferredList</code> is itself a deferred (it inherits from <code>Deferred</code>). That means you can add callbacks and errbacks to it just like you would a regular deferred.</li>
<li>In the example above, our callback was fired as soon as we added it, so the <code>DeferredList</code> must have fired right away. We&#8217;ll discuss that more in a second.</li>
<li>The result of the deferred list was itself a list (empty).</li>
</ul>
<p>Now look at <a href="http://github.com/jdavisp3/twisted-intro/blob/master/deferred-list/deferred-list-2.py#L1"><tt>deferred-list/deferred-list-2.py</tt></a>:</p>
<pre>from twisted.internet import defer

def got_results(res):
    print 'We got:', res

print 'One Deferred.'
d1 = defer.Deferred()
d = defer.DeferredList([d1])
print 'Adding Callback.'
d.addCallback(got_results)
print 'Firing d1.'
d1.callback('d1 result')</pre>
<p>Now we are creating our <code>DeferredList</code> with a 1-element list containing a single deferred. Here&#8217;s the output we get:</p>
<pre>One Deferred.
Adding Callback.
Firing d1.
We got: [(True, 'd1 result')]</pre>
<p>More things to notice:</p>
<ul>
<li>This time the <code>DeferredList</code> didn&#8217;t fire its callback until we fired the deferred in the list.</li>
<li>The result is still a list, but now it has one element.</li>
<li>The element is a tuple whose second value is the result of the deferred in the list.</li>
</ul>
<p>Let&#8217;s try putting two deferreds in the list (<a href="http://github.com/jdavisp3/twisted-intro/blob/master/deferred-list/deferred-list-1.py#L3"><tt>deferred-list/deferred-list-3.py</tt></a>):</p>
<pre>from twisted.internet import defer

def got_results(res):
    print 'We got:', res

print 'Two Deferreds.'
d1 = defer.Deferred()
d2 = defer.Deferred()
d = defer.DeferredList([d1, d2])
print 'Adding Callback.'
d.addCallback(got_results)
print 'Firing d1.'
d1.callback('d1 result')
print 'Firing d2.'
d2.callback('d2 result')</pre>
<p>And here&#8217;s the output:</p>
<pre>Two Deferreds.
Adding Callback.
Firing d1.
Firing d2.
We got: [(True, 'd1 result'), (True, 'd2 result')]</pre>
<p>At this point it&#8217;s pretty clear the result of a <code>DeferredList</code>, at least for the way we&#8217;ve been using it, is a list with the same number of elements as the list of deferreds we passed to the constructor. And the elements of that result list contain the results of the original deferreds, at least if the deferreds succeed. That means the <code>DeferredList</code> itself doesn&#8217;t fire until all the deferreds in the original list have fired. And a <code>DeferredList</code> created with an empty list fires right away since there aren&#8217;t any deferreds to wait for.</p>
<p>What about the order of the results in the final list? Consider <a href="http://github.com/jdavisp3/twisted-intro/blob/master/deferred-list/deferred-list-4.py#L1"><tt>deferred-list/deferred-list-4.py</tt></a>:</p>
<pre>from twisted.internet import defer

def got_results(res):
    print 'We got:', res

print 'Two Deferreds.'
d1 = defer.Deferred()
d2 = defer.Deferred()
d = defer.DeferredList([d1, d2])
print 'Adding Callback.'
d.addCallback(got_results)
print 'Firing d2.'
d2.callback('d2 result')
print 'Firing d1.'
d1.callback('d1 result')</pre>
<p>Now we are firing <code>d2</code> first and then <code>d1</code>. Note the deferred list is still constructed with <code>d1<code> and <code>d2</code> in their original order. Here's the output:</p>
<pre>Two Deferreds.
Adding Callback.
Firing d2.
Firing d1.
We got: [(True, 'd1 result'), (True, 'd2 result')]</pre>
<p>The output list has the results in the same order as the original list of deferreds, not the order those deferreds happened to fire in. Which is very nice, because we can easily associate each individual result with the operation that generated it (for example, which poem came from which server).</p>
<p>Alright, what happens if one or more of the deferreds in the list fails? And what are those <code>True</code> values doing there? Let's try the example in <a href="http://github.com/jdavisp3/twisted-intro/blob/master/deferred-list/deferred-list-5.py#L1"><tt>deferred-list/deferred-list-5.py</tt></a>:</p>
<pre>from twisted.internet import defer

def got_results(res):
    print 'We got:', res

d1 = defer.Deferred()
d2 = defer.Deferred()
d = defer.DeferredList([d1, d2], consumeErrors=True)
d.addCallback(got_results)
print 'Firing d1.'
d1.callback('d1 result')
print 'Firing d2 with errback.'
d2.errback(Exception('d2 failure'))</pre>
<p>Now we are firing <code>d1</code> with a normal result and <code>d2</code> with an error. Ignore the <code>consumerErrors</code> option for now, we'll get back to it. Here's the output:</p>
<pre>Firing d1.
Firing d2 with errback.
We got: [(True, 'd1 result'), (False, &lt;twisted.python.failure.Failure &lt;type 'exceptions.Exception'&gt;&gt;)]</pre>
<p>Now the tuple corresponding to <code>d2</code> has a <code>Failure</code> in slot two, and <code>False</code> in slot one. At this point it should be pretty clear how a <code>DeferredList</code> works (but see the Discussion below):</p>
<ul>
<li>A <code>DeferredList</code> is constructed with a list of deferred objects.</li>
<li>A <code>DeferredList</code> is itself a deferred whose result is a list of the same length as the list of deferreds.</li>
<li>The <code>DeferredList</code> fires after all the deferreds in the original list have fired.</li>
<li>Each element of the result list corresponds to the deferred in the same position as the original list. If that deferred succeeded, the element is <code>(True, result)</code> and if the deferred failed, the element is <code>(False, failure)</code>.</li>
<li>A <code>DeferredList</code> never fails, since the result of each individual deferred is collected into the list no matter what (but again, see the Discussion below).</li>
</ul>
<p>Now let's talk about that <code>consumeErrors</code> option we passed to the <code>DeferredList</code>. If we run the same code but without passing the option (<a href="http://github.com/jdavisp3/twisted-intro/blob/master/deferred-list/deferred-list-6.py#L1"><tt>deferred-list/deferred-list-6.py</tt></a>), we get this output:</p>
<pre>Firing d1.
Firing d2 with errback.
We got: [(True, 'd1 result'), (False, &gt;twisted.python.failure.Failure &gt;type 'exceptions.Exception'&lt;&lt;)]
Unhandled error in Deferred:
Traceback (most recent call last):
Failure: exceptions.Exception: d2 failure</pre>
<p>If you recall, the "Unhandled error in Deferred" message is generated when a deferred is garbage collected and the last callback in that deferred failed. The message is telling us we haven't caught all the potential asynchronous failures in our program. So where is it coming from in our example? It's clearly not coming from the <code>DeferredList</code>, since that succeeds. So it must be coming from <code>d2</code>.</p>
<p>A <code>DeferredList</code> needs to know when each deferred it is monitoring fires. And the <code>DeferredList</code> does that in the usual way &mdash; by adding a callback and errback to each deferred. And by default, the callback (and errback) return the original result (or failure) after putting it in the final list. And since returning the original failure from the errback triggers the next errback, <code>d2</code> remains in the failed state after it fires.</p>
<p>But if we pass <code>consumeErrors=True</code> to the <code>DeferredList</code>, the errback added by the <code>DeferredList</code> to each deferred will instead return <code>None</code>, thus "consuming" the error and eliminating the warning message. We could also handle the error by adding our own errback to <code>d2</code>, as in <a href="http://github.com/jdavisp3/twisted-intro/blob/master/deferred-list/deferred-list-7.py#L1"><tt>deferred-list/deferred-list-7.py</tt></a>.</p>
<h3>Client 8.0</h3>
<p>Version 8.0 of our Get Poetry Now! client uses a <code>DeferredList</code> to find out when all the poetry has finished (or failed). You can find the new client in <a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-client-8/get-poetry.py#L1"><tt>twisted-client-8/get-poetry.py</tt></a>. Once again the only change is in <a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-client-8/get-poetry.py#L151"><code>poetry_main</code></a>. Let's look at the important changes:</p>
<pre>    ...
    ds = []

    for (host, port) in addresses:
        d = get_transformed_poem(host, port)
        d.addCallbacks(got_poem)
        ds.append(d)

    dlist = defer.DeferredList(ds, consumeErrors=True)
    dlist.addCallback(lambda res : reactor.stop())</pre>
<p>You may wish to compare it to the same section of <a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-client-7/get-poetry.py#L180"><code>client 7.0</code></a>.</p>
<p>In client 8.0, we don't need the <code>poem_done</code> callback or the <code>results</code> list. Instead, we put each deferred we get back from <code>get_transformed_poem</code> into a list (<code>ds</code>) and then create a <code>DeferredList</code>. Since the <code>DeferredList</code> won't fire until all the poems have finished or failed, we just add a callback to the <code>DeferredList</code> to shutdown the reactor. In this case, we aren't using the result from the <code>DeferredList</code>, we just need to know when everything is finished. And that's it!</p>
<h3>Discussion</h3>
<p>We can visualize how a <code>DeferredList</code> works in Figure 37:<br />
<div id="attachment_2590" class="wp-caption alignnone" style="width: 615px"><a href="http://krondo.com/wp-content/uploads/2010/08/deferred-list.png"><img src="http://krondo.com/wp-content/uploads/2010/08/deferred-list.png" alt="Figure 37: the result of a DeferredList" title="Figure 37: the result of a DeferredList" width="605" height="340" class="size-full wp-image-2590" /></a><p class="wp-caption-text">Figure 37: the result of a DeferredList</p></div></p>
<p>Pretty simple, really. There are a couple options to <code>DeferredList</code> we haven't covered, and which change the behavior from what we have described above. We will leave them for you to explore in the Exercises below.</p>
<p>In the next Part we will cover one more feature of the <code>Deferred</code> class, a feature recently introduced in Twisted 10.1.0.</p>
<h3>Suggested Exercises</h3>
<ol>
<li>Read the <a href="http://twistedmatrix.com/trac/browser/tags/releases/twisted-10.1.0/twisted/internet/defer.py#5933">source code</a> for the <code>DeferredList</code>.</li>
<li>Modify the examples in <tt>deferred-list</tt> to experiment with the optional constructor arguments <code>fireOnOneCallback</code> and <code>fireOnOneErrback</code>. Come up with scenarios where you would use one or the other (or both).</li>
<li>Can you create a <code>DeferredList</code> using a list of <code>DeferredList</code>s? If so, what would the result look like?</li>
<li>Modify client 8.0 so that it doesn't print out anything until all the poems have finished downloading. This time you will use the result from the <code>DeferredList</code>.</li>
<li>Define the semantics of a <code>DeferredDict</code> and then implement it.</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://krondo.com/?feed=rss2&amp;p=2571</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>An Introduction to Asynchronous Programming and Twisted</title>
		<link>http://krondo.com/?p=2441</link>
		<comments>http://krondo.com/?p=2441#comments</comments>
		<pubDate>Sun, 15 Aug 2010 17:00:43 +0000</pubDate>
		<dc:creator>dave</dc:creator>
				<category><![CDATA[Blather]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://krondo.com/blog/?p=2441</guid>
		<description><![CDATA[Part 17: Just Another Way to Spell &#8220;Callback&#8221; This continues the introduction started here. You can find an index to the entire series here. Introduction In this Part we&#8217;re going to return to the subject of callbacks. We&#8217;ll introduce another technique for writing callbacks in Twisted that uses generators. We&#8217;ll show how the technique works [...]]]></description>
			<content:encoded><![CDATA[<h2>Part 17: Just Another Way to Spell &#8220;Callback&#8221;</h2>
<p>This continues the introduction started <a href="?p=1209">here</a>. You can find an index to the entire series <a href="?page_id=1327">here</a>.</p>
<h3>Introduction</h3>
<p>In this Part we&#8217;re going to return to the subject of callbacks. We&#8217;ll introduce another technique for writing callbacks in Twisted that uses <a href="http://docs.python.org/tutorial/classes.html#generators">generators</a>. We&#8217;ll show how the technique works and contrast it with using &#8220;pure&#8221; Deferreds. Finally we&#8217;ll rewrite one of our poetry clients using this technique. But first let&#8217;s review how generators work so we can see why they are a candidate for creating callbacks.</p>
<h4>A Brief Review of Generators</h4>
<p>As you probably know, a Python generator is a &#8220;restartable function&#8221; that you create by using the <code>yield</code> expression in the body of your function. By doing so, the function becomes a &#8220;generator function&#8221; that returns an <a href="http://docs.python.org/tutorial/classes.html#iterators">iterator</a> you can use to run the function in a series of steps. Each cycle of the iterator restarts the function, which proceeds to execute until it reaches the next <code>yield</code>.</p>
<p>Generators (and iterators) are often used to represent lazily-created sequences of values. Take a look at the example code in <a href="http://github.com/jdavisp3/twisted-intro/blob/master/inline-callbacks/gen-1.py#L1"><tt>inline-callbacks/gen-1.py</tt></a>:</p>
<pre>def my_generator():
    print 'starting up'
    yield 1
    print "workin'"
    yield 2
    print "still workin'"
    yield 3
    print 'done'

for n in my_generator():
    print n</pre>
<p>Here we have a generator that creates the sequence 1, 2, 3. If you run the code, you will see the <code>print</code> statements in the generator interleaved with the <code>print</code> statement in the <code>for</code> loop as the loop cycles through the generator.</p>
<p>We can make this code more explicit by creating the generator ourselves (<a href="http://github.com/jdavisp3/twisted-intro/blob/master/inline-callbacks/gen-2.py#L1"><tt>inline-callbacks/gen-2.py</tt></a>):</p>
<pre>def my_generator():
    print 'starting up'
    yield 1
    print "workin'"
    yield 2
    print "still workin'"
    yield 3
    print 'done'

gen = my_generator()

while True:
    try:
        n = gen.next()
    except StopIteration:
        break
    else:
        print n</pre>
<p>Considered as a sequence, the generator is just an object for getting successive values. But we can also view things from the point of view of the generator itself:</p>
<ol>
<li>The generator function doesn&#8217;t start running until &#8220;called&#8221; by the loop (using the <code>next</code> method).</li>
<li>Once the generator is running, it keeps running until it &#8220;returns&#8221; to the loop (using <code>yield</code>).</li>
<li>When the loop is running other code (like the <code>print</code> statement), the generator is not running.</li>
<li>When the generator is running, the loop is not running (it&#8217;s &#8220;blocked&#8221; waiting for the generator).</li>
<li>Once a generator <code>yield</code>s control to the loop, an arbitrary amount of time may pass (and an arbitrary amount of other code may execute) until the generator runs again.</li>
</ol>
<p>This is very much like the way callbacks work in an asynchronous system. We can think of the <code>while</code> loop as the reactor, and the generator as a series of callbacks separated by <code>yield</code> statements, with the interesting fact that all the callbacks share the same local variable namespace, and the namespace persists from one callback to the next.</p>
<p>Furthermore, you can have multiple generators active at once (see the example in <a href="http://github.com/jdavisp3/twisted-intro/blob/master/inline-callbacks/gen-3.py#L1"><tt>inline-callbacks/gen-3.py</tt></a>), with their &#8220;callbacks&#8221; interleaved with each other, just as you can have independent asynchronous tasks running in a system like Twisted.</p>
<p>Something is still missing, though. Callbacks aren&#8217;t just called by the reactor, they also receive information. When part of a deferred&#8217;s chain, a callback either receives a result, in the form of a single Python value, or an error, in the form of a <code>Failure</code>.</p>
<p>Starting with Python 2.5, generators were extended in a way that allows you to send information to a generator when you restart it, as illustrated in <a href="http://github.com/jdavisp3/twisted-intro/blob/master/inline-callbacks/gen-4.py#L1"><tt>inline-callbacks/gen-4.py</tt></a>:</p>
<pre>class Malfunction(Exception):
    pass

def my_generator():
    print 'starting up'

    val = yield 1
    print 'got:', val

    val = yield 2
    print 'got:', val

    try:
        yield 3
    except Malfunction:
        print 'malfunction!'

    yield 4

    print 'done'

gen = my_generator()

print gen.next() # start the generator
print gen.send(10) # send the value 10
print gen.send(20) # send the value 20
print gen.throw(Malfunction()) # raise an exception inside the generator

try:
    gen.next()
except StopIteration:
    pass</pre>
<p>In Python 2.5 and later versions, the <code>yield</code> statement is an expression that evaluates to a value. And the code that restarts the generator can determine that value using the <code>send</code> method instead of <code>next</code> (if you use <code>next</code> the value is <code>None</code>). What&#8217;s more, you can actually raise an arbitrary exception <em>inside</em> the generator using the <code>throw</code> method. How cool is that?</p>
<h3>Inline Callbacks</h3>
<p>Given what we just reviewed about <code>send</code>ing and <code>throw</code>ing values and exceptions into a generator, we can envision a generator as a series of callbacks, like the ones in a deferred, which receive either results or failures. The callbacks are separated by <code>yield</code>s and the value of each <code>yield</code> expression is the result for the next callback (or the <code>yield</code> raises an exception and that&#8217;s the failure). Figure 35 shows the correspondence:</p>
<div id="attachment_2461" class="wp-caption aligncenter" style="width: 438px"><a href="http://krondo.com/blog/wp-content/uploads/2010/07/generator-callbacks1.png"><img class="size-full wp-image-2461" title="Figure 35: generator as a callback sequence" src="http://krondo.com/blog/wp-content/uploads/2010/07/generator-callbacks1.png" alt="Figure 35: generator as a callback sequence" width="428" height="235" /></a><p class="wp-caption-text">Figure 35: generator as a callback sequence</p></div>
<p>Now when a series of callbacks is chained together in a deferred, each callback receives the result from the one prior. That&#8217;s easy enough to do with a generator — just <code>send</code> the value you got from the previous run of the generator (the value it <code>yield</code>ed) the next time you restart it. But that also seems a bit silly. Since the generator computed the value to begin with, why bother sending it back? The generator could just save the value in a variable for the next time it&#8217;s needed. So what&#8217;s the point?</p>
<p>Recall the fact we learned in <a href="?p=2159">Part 13</a>, that the callbacks in a deferred can return deferreds themselves. And when that happens, the outer deferred is paused until the inner deferred fires, and then the next callback (or errback) in the outer deferred&#8217;s chain is called with the result (or failure) from the inner deferred.</p>
<p>So imagine that our generator <code>yield</code>s a deferred object instead of an ordinary Python value. The generator is now &#8220;paused&#8221;, and that&#8217;s automatic; generators always pause after every <code>yield</code> statement until they are explicitly restarted. So we can delay restarting the generator until the deferred fires, at which point we either <code>send</code> the value (if the deferred succeeds) or <code>throw</code> the exception (if the deferred fails). That would make our generator a genuine sequence of asynchronous callbacks and that&#8217;s the idea behind the <a href="http://twistedmatrix.com/trac/browser/tags/releases/twisted-10.1.0/twisted/internet/defer.py#L973"><code>inlineCallbacks</code></a> function in <a href="http://twistedmatrix.com/trac/browser/tags/releases/twisted-10.1.0/twisted/internet/defer.py"><code>twisted.internet.defer</code></a>.</p>
<h4>inlineCallbacks</h4>
<p>Consider the example program in <a href="http://github.com/jdavisp3/twisted-intro/blob/master/inline-callbacks/inline-callbacks-1.py#L1"><tt>inline-callbacks/inline-callbacks-1.py</tt></a>:</p>
<pre>from twisted.internet.defer import inlineCallbacks, Deferred

@inlineCallbacks
def my_callbacks():
    from twisted.internet import reactor

    print 'first callback'
    result = yield 1 # yielded values that aren't deferred come right back

    print 'second callback got', result
    d = Deferred()
    reactor.callLater(5, d.callback, 2)
    result = yield d # yielded deferreds will pause the generator

    print 'third callback got', result # the result of the deferred

    d = Deferred()
    reactor.callLater(5, d.errback, Exception(3))

    try:
        yield d
    except Exception, e:
        result = e

    print 'fourth callback got', repr(result) # the exception from the deferred

    reactor.stop()

from twisted.internet import reactor
reactor.callWhenRunning(my_callbacks)
reactor.run()</pre>
<p>Run the example and you will see the generator execute to the end and then stop the reactor. The example illustrates several aspects of the <code>inlineCallbacks</code> function. First, <code>inlineCallbacks</code> is a decorator and it always decorates generator functions, i.e., functions that use <code>yield</code>. The whole purpose of <code>inlineCallbacks</code> is turn a generator into a series of asynchronous callbacks according to the scheme we outlined before.</p>
<p>Second, when we invoke an <code>inlineCallbacks</code>-decorated function, we don&#8217;t need to call <code>next</code> or <code>send</code> or <code>throw</code> ourselves. The decorator takes care of those details for us and ensures the generator will run to the end (assuming it doesn&#8217;t raise an exception).</p>
<p>Third, if we <code>yield</code> a non-deferred value from the generator, it is immediately restarted with that same value as the result of the <code>yield</code>.</p>
<p>And finally, if we <code>yield</code> a deferred from the generator, it will not be restarted until that deferred fires. If the deferred succeeds, the result of the <code>yield</code> is just the result from the deferred. And if the deferred fails, the <code>yield</code> statement raises the exception. Note the exception is just an ordinary <code>Exception</code> object, rather than a <code>Failure</code>, and we can catch it with a <code>try</code>/<code>except</code> statement around the <code>yield</code> expression.</p>
<p>In the example we are just using <code>callLater</code> to fire the deferreds after a short period of time. While that&#8217;s a handy way to put in a non-blocking delay into our callback chain, normally we would be <code>yield</code>ing a deferred returned by some other asynchronous operation (i.e., <code>get_poetry</code>) invoked from our generator.</p>
<p>Ok, now we know how an <code>inlineCallbacks</code>-decorated function runs, but what return value do you get if you actually call one? As you might have guessed, you get a deferred. Since we can&#8217;t know exactly when that generator will stop running (it might <code>yield</code> one or more deferreds), the decorated function itself is asynchronous and a deferred is the appropriate return value. Note the deferred that is returned isn&#8217;t one of the deferreds the generator may <code>yield</code>. Rather, it&#8217;s a deferred that fires only after the generator has completely finished (or throws an exception).</p>
<p>If the generator throws an exception, the returned deferred will fire its errback chain with that exception wrapped in a <code>Failure</code>. But if we want the generator to return a normal value, we must &#8220;return&#8221; it using the <code>defer.returnValue</code> function. Like the ordinary <code>return</code> statement, it will also stop the generator (it actually raises a special exception). The <a href="http://github.com/jdavisp3/twisted-intro/blob/master/inline-callbacks/inline-callbacks-2.py#L1"><tt>inline-callbacks/inline-callbacks-2.py</tt></a> example illustrates both possibilities.</p>
<h3>Client 7.0</h3>
<p>Let&#8217;s put <code>inlineCallbacks</code> to work with a new version of our poetry client. You can see the code in <a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-client-7/get-poetry.py#L1"><tt>twisted-client-7/get-poetry.py</tt></a>. You may wish to compare it to client 6.0 in <a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-client-6/get-poetry.py#L151"><tt>twisted-client-6/get-poetry.py</tt></a>. The relevant changes are in <a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-client-7/get-poetry.py#L151"><code>poetry_main</code></a>:</p>
<pre>def poetry_main():
    addresses = parse_args()

    xform_addr = addresses.pop(0)

    proxy = TransformProxy(*xform_addr)

    from twisted.internet import reactor

    results = []

    @defer.inlineCallbacks
    def get_transformed_poem(host, port):
        try:
            poem = yield get_poetry(host, port)
        except Exception, e:
            print &gt;&gt;sys.stderr, 'The poem download failed:', e
            raise

        try:
            poem = yield proxy.xform('cummingsify', poem)
        except Exception:
            print &gt;&gt;sys.stderr, 'Cummingsify failed!'

        defer.returnValue(poem)

    def got_poem(poem):
        print poem

    def poem_done(_):
        results.append(_)
        if len(results) == len(addresses):
            reactor.stop()

    for address in addresses:
        host, port = address
        d = get_transformed_poem(host, port)
        d.addCallbacks(got_poem)
        d.addBoth(poem_done)

    reactor.run()</pre>
<p>In our new version the <code>inlineCallbacks</code> generator function <code>get_transformed_poem</code> is responsible for both fetching the poem and then applying the transformation (via the transform service). Since both operations are asynchronous, we yield a deferred each time and then (implicitly) wait for the result. As in client 6.0, if the transformation fails we just return the original poem. Notice we can use <code>try</code>/<code>except</code> statements to handle asynchronous errors inside the generator.</p>
<p>We can test the new client out in the same way as before. First start up a transform server:</p>
<pre>python twisted-server-1/tranformedpoetry.py --port 10001</pre>
<p>Then start a couple of poetry servers:</p>
<pre>python twisted-server-1/fastpoetry.py --port 10002 poetry/fascination.txt
python twisted-server-1/fastpoetry.py --port 10003 poetry/science.txt</pre>
<p>Now you can run the new client:</p>
<pre>python twisted-client-7/get-poetry.py 10001 10002 10003</pre>
<p>Try turning off one or more of the servers to see how the client handles errors.</p>
<h3>Discussion</h3>
<p>Like the <code>Deferred</code> object, the <code>inlineCallbacks</code> function gives us a new way of organizing our asynchronous callbacks. And, as with deferreds, <code>inlineCallbacks</code> doesn&#8217;t change the rules of the game. Specifically, our callbacks still run one at a time, and they are still invoked by the reactor. We can confirm that fact in our usual way by printing out a traceback from an inline callback, as in the example script <a href="http://github.com/jdavisp3/twisted-intro/blob/master/inline-callbacks/inline-callbacks-tb.py#L1"><tt>inline-callbacks/inline-callbacks-tb.py</tt></a>. Run that code and you will get a traceback with <code>reactor.run()</code> at the top, lots of helper functions in between, and our callback at the bottom.</p>
<p>We can adapt Figure 29, which explains what happens when one callback in a deferred returns another deferred, to show what happens when an <code>inlineCallbacks</code> generator <code>yield</code>s a deferred. See Figure 36:</p>
<div id="attachment_2533" class="wp-caption alignnone" style="width: 639px"><a href="http://krondo.com/wp-content/uploads/2010/07/inline-callbacks1.png"><img class="size-full wp-image-2533" title="Figure 36: flow control in an inlineCallbacks function" src="http://krondo.com/wp-content/uploads/2010/07/inline-callbacks1.png" alt="Figure 36: flow control in an inlineCallbacks function" width="629" height="582" /></a><p class="wp-caption-text">Figure 36: flow control in an inlineCallbacks function</p></div>
<p>The same figure works in both cases because the idea being illustrated is the same — one asynchronous operation is waiting for another.</p>
<p>Since <code>inlineCallbacks</code> and deferreds solve many of the same problems, why choose one over the other? Here are some potential advantages of <code>inlineCallbacks</code>:</p>
<ul>
<li>Since the callbacks share a namespace, there is no need to pass extra state around.</li>
<li>The callback order is easier to see, as they just execute from top to bottom.</li>
<li>With no function declarations for individual callbacks and implicit flow-control, there is generally less typing.</li>
<li>Errors are handled with the familiar <code>try</code>/<code>except</code> statement.</li>
</ul>
<p>And here are some potential pitfalls:</p>
<ul>
<li>The callbacks inside the generator cannot be invoked individually, which could make code re-use difficult. With a deferred, the code constructing the deferred is free to add arbitrary callbacks in an arbitrary order.</li>
<li>The compact form of a generator can obscure the fact that an asynchronous callback is even involved. Despite its visually similar appearance to an ordinary sequential function, a generator behaves in a very different manner. The <code>inlineCallbacks</code> function is not a way to avoid learning the asynchronous programming model.</li>
</ul>
<p>As with any technique, practice will provide the experience necessary to make an informed choice.</p>
<h3>Summary</h3>
<p>In this Part we learned about the <code>inlineCallbacks</code> decorator and how it allows us to express a sequence of asynchronous callbacks in the form of a Python generator.</p>
<p>In <a href="?p=2571">Part 18</a> we will learn a technique for managing a set of &#8220;parallel&#8221; asynchronous operations.</p>
<h3>Suggested Exercises</h3>
<ol>
<li>Why is the <code>inlineCallbacks</code> function plural?</li>
<li>Study the implementation of <a href="http://twistedmatrix.com/trac/browser/tags/releases/twisted-10.1.0/twisted/internet/defer.py#973"><code>inlineCallbacks</code></a> and its helper function <a href="http://twistedmatrix.com/trac/browser/tags/releases/twisted-10.1.0/twisted/internet/defer.py#L874"><code>_inlineCallbacks</code></a>. Ponder the phrase &#8220;the devil is in the details&#8221;.</li>
<li>How many callbacks are contained in a generator with <strong>N</strong> <code>yield</code> statements, assuming it has no loops or <code>if</code> statements?</li>
<li>Poetry client 7.0 might have three generators running at once. Conceptually, how many different ways might they be interleaved with one another? Considering the way they are invoked in the poetry client and the implementation of <code>inlineCallbacks</code>, how many ways do you think are actually possible?</li>
<li>Move the <code>got_poem</code> callback in client 7.0 inside the generator.</li>
<li>Then move the <code>poem_done</code> callback inside the generator. Be careful! Make sure to handle all the failure cases so the reactor gets shutdown no matter what. How does the resulting code compare to using a deferred to shutdown the reactor?</li>
<li>A generator with <code>yield</code> statements inside a <code>while</code> loop can represent a conceptually infinite sequence. What does such a generator decorated with <code>inlineCallbacks</code> represent?</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://krondo.com/?feed=rss2&amp;p=2441</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Book: Blood of the Liberals</title>
		<link>http://krondo.com/?p=2542</link>
		<comments>http://krondo.com/?p=2542#comments</comments>
		<pubDate>Sat, 14 Aug 2010 05:09:05 +0000</pubDate>
		<dc:creator>dave</dc:creator>
				<category><![CDATA[Books]]></category>

		<guid isPermaLink="false">http://krondo.com/?p=2542</guid>
		<description><![CDATA[Packer reviews the last century or so of liberalism through the biographies of his father and grandfather.]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.amazon.com/exec/obidos/ASIN/0374527784/krondonet-20"><img src="http://ecx.images-amazon.com/images/I/51TT9BNRBYL._SL160_.jpg" alt="" /></a></p>
<p>Packer reviews the last century or so of liberalism through the biographies of his father and grandfather.</p>
]]></content:encoded>
			<wfw:commentRss>http://krondo.com/?feed=rss2&amp;p=2542</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Site reorganization</title>
		<link>http://krondo.com/?p=2520</link>
		<comments>http://krondo.com/?p=2520#comments</comments>
		<pubDate>Mon, 09 Aug 2010 04:25:04 +0000</pubDate>
		<dc:creator>dave</dc:creator>
				<category><![CDATA[Blather]]></category>

		<guid isPermaLink="false">http://krondo.com/?p=2520</guid>
		<description><![CDATA[Today I made my blog my main website, since the old one was getting kind of crufty. I moved the stuff I wanted to keep into WordPress, including my collection of links to programmer&#8217;s editors. The old /blog URLs will continue to work.]]></description>
			<content:encoded><![CDATA[<p>Today I made my blog my main website, since the old one was getting kind of crufty. I moved the stuff I wanted to keep into WordPress, including my collection of links to <a href="http://krondo.com/?page_id=2511">programmer&#8217;s editors</a>. The old <tt>/blog</tt> URLs will continue to work.</p>
]]></content:encoded>
			<wfw:commentRss>http://krondo.com/?feed=rss2&amp;p=2520</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Book: Sacred Games</title>
		<link>http://krondo.com/?p=2476</link>
		<comments>http://krondo.com/?p=2476#comments</comments>
		<pubDate>Thu, 05 Aug 2010 01:56:00 +0000</pubDate>
		<dc:creator>dave</dc:creator>
				<category><![CDATA[Books]]></category>

		<guid isPermaLink="false">http://krondo.com/blog/?p=2476</guid>
		<description><![CDATA[Wonderful.]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.amazon.com/exec/obidos/ASIN/0061130362/krondonet-20"><img src="http://ecx.images-amazon.com/images/I/516c4G6G0JL._SL160_.jpg" alt="" /></a></p>
<p>Wonderful.</p>
]]></content:encoded>
			<wfw:commentRss>http://krondo.com/?feed=rss2&amp;p=2476</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Book: The Autumn of the Middle Ages</title>
		<link>http://krondo.com/?p=2473</link>
		<comments>http://krondo.com/?p=2473#comments</comments>
		<pubDate>Sat, 31 Jul 2010 18:24:25 +0000</pubDate>
		<dc:creator>dave</dc:creator>
				<category><![CDATA[Books]]></category>

		<guid isPermaLink="false">http://krondo.com/blog/?p=2473</guid>
		<description><![CDATA[This book is my favorite sort of history: an exploration of the minds and viewpoints of the people who lived it. The medieval world-view is so alien to us, and Huizinga lets us glimpse it.]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.amazon.com/exec/obidos/ASIN/0226359948/krondonet-20"><img src="http://ecx.images-amazon.com/images/I/51HYY3C7J3L._SL160_.jpg" alt="" /></a></p>
<p>This book is my favorite sort of history: an exploration of the minds and viewpoints of the people who lived it. The medieval world-view is so alien to us, and Huizinga lets us glimpse it.</p>
]]></content:encoded>
			<wfw:commentRss>http://krondo.com/?feed=rss2&amp;p=2473</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>3-D Shmee-D</title>
		<link>http://krondo.com/?p=2464</link>
		<comments>http://krondo.com/?p=2464#comments</comments>
		<pubDate>Mon, 26 Jul 2010 05:01:48 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Blather]]></category>

		<guid isPermaLink="false">http://krondo.com/blog/?p=2464</guid>
		<description><![CDATA[I watched a 1-D film yesterday. Good acting, but I thought the plot was too linear.]]></description>
			<content:encoded><![CDATA[<p>I watched a 1-D film yesterday. Good acting, but I thought the plot was too linear.</p>
]]></content:encoded>
			<wfw:commentRss>http://krondo.com/?feed=rss2&amp;p=2464</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Book: Mohammed and Charlemagne</title>
		<link>http://krondo.com/?p=2444</link>
		<comments>http://krondo.com/?p=2444#comments</comments>
		<pubDate>Tue, 13 Jul 2010 02:56:38 +0000</pubDate>
		<dc:creator>dave</dc:creator>
				<category><![CDATA[Books]]></category>

		<guid isPermaLink="false">http://krondo.com/blog/?p=2444</guid>
		<description><![CDATA[Pirenne&#8217;s posthumous work where he advances the thesis that Medieval Europe was made possible by the rise of Islam, which severed many of the East-West ties that kept the West under the influence of Byzantium.]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.amazon.com/exec/obidos/ASIN/0486420116/krondonet-20"><img src="http://ecx.images-amazon.com/images/I/51PBHS5JDTL._SL160_.jpg" alt="" /></a></p>
<p>Pirenne&#8217;s posthumous work where he advances the thesis that Medieval Europe was made possible by the rise of Islam, which severed many of the East-West ties that kept the West under the influence of Byzantium.</p>
]]></content:encoded>
			<wfw:commentRss>http://krondo.com/?feed=rss2&amp;p=2444</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>An Introduction to Asynchronous Programming and Twisted</title>
		<link>http://krondo.com/?p=2345</link>
		<comments>http://krondo.com/?p=2345#comments</comments>
		<pubDate>Sun, 11 Jul 2010 04:20:16 +0000</pubDate>
		<dc:creator>dave</dc:creator>
				<category><![CDATA[Blather]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://krondo.com/blog/?p=2345</guid>
		<description><![CDATA[Part 16: Twisted Daemonologie This continues the introduction started here. You can find an index to the entire series here. Introduction The servers we have written so far have just run in a terminal window, with output going to the screen via print statements. This works alright for development, but it&#8217;s hardly a way to [...]]]></description>
			<content:encoded><![CDATA[<h2>Part 16: Twisted Daemonologie</h2>
<p>This continues the introduction started <a href="?p=1209">here</a>. You can find an index to the entire series <a href="?page_id=1327">here</a>.</p>
<h3>Introduction</h3>
<p>The servers we have written so far have just run in a terminal window, with output going to the screen via <code>print</code> statements. This works alright for development, but it&#8217;s hardly a way to deploy services in production. A well-behaved production server ought to:</p>
<ol>
<li>Run as a <a href="http://en.wikipedia.org/wiki/Daemon_%28computer_software%29">daemon</a> process, unconnected with any terminal or user session. You don&#8217;t want a service to shut down just because the administrator logs out.</li>
<li>Send debugging and error output to a set of rotated log files, or to the <a href="http://en.wikipedia.org/wiki/Syslog"><tt>syslog</tt></a> service.</li>
<li>Drop excessive privileges, e.g., switching to a lower-privileged user before running.</li>
<li>Record its <a href="http://en.wikipedia.org/wiki/Process_ID"><tt>pid</tt></a> in a file so that the administrator can easily <a href="http://en.wikipedia.org/wiki/Kill%28%29">send signals</a> to the daemon.</li>
</ol>
<p>We can get all of those features by using the <tt>twistd</tt> script provided by Twisted. But first we&#8217;ll have to change our code a bit.</p>
<h3>The Concepts</h3>
<p>Understanding <tt>twistd</tt> will require learning a few new concepts in Twisted, the most important being a <code>Service</code>. As usual, several of the new concepts are accompanied by new <code>Interface</code>s.</p>
<h4>IService</h4>
<p>The <a href="http://twistedmatrix.com/trac/browser/tags/releases/twisted-10.0.0/twisted/application/service.py#L87"><code>IService</code></a> interface defines a named service that can be started and stopped. What does the service do? Whatever you like — rather than define the specific function of the service, the interface requires only that it provide a small set of generic attributes and methods.</p>
<p>There are two required attributes: <code>name</code> and <code>running</code>. The <code>name</code> attribute is just a string, like <code>'fastpoetry'</code>. The <code>running</code> attribute is a Boolean value and is true if the service has been successfully started.</p>
<p>We&#8217;re only going to touch on some of the methods of <code>IService</code>. We&#8217;ll skip some that are obvious, and others that are more advanced and often go unused in simpler Twisted programs. The two principle methods of <code>IService</code> are <a href="http://twistedmatrix.com/trac/browser/tags/releases/twisted-10.0.0/twisted/application/service.py#L130"><code>startService</code></a> and <a href="http://twistedmatrix.com/trac/browser/tags/releases/twisted-10.0.0/twisted/application/service.py#L135"><code>stopService</code></a>:</p>
<pre>    def startService():
        """
        Start the service.
        """

    def stopService():
        """
        Stop the service.

        @rtype: L{Deferred}
        @return: a L{Deferred} which is triggered when the service has
            finished shutting down. If shutting down is immediate, a
            value can be returned (usually, C{None}).
        """</pre>
<p>Again, what these methods actually do will depend on the service in question. For example, the <code>startService</code> method might:</p>
<ul>
<li>Load some configuration data, or</li>
<li>Initialize a database, or</li>
<li>Start listening on a port, or</li>
<li>Do nothing at all.</li>
</ul>
<p>And the <code>stopService</code> method might:</p>
<ul>
<li>Persist some state, or</li>
<li>Close open database connections, or</li>
<li>Stop listening on a port, or</li>
<li>Do nothing at all.</li>
</ul>
<p>When we write our own custom services we&#8217;ll need to implement these methods appropriately. For some common behaviors, like listening on a port, Twisted provides ready-made services we can use instead.</p>
<p>Notice that <code>stopService</code> may optionally return a deferred, which is required to fire when the service has completely shut down. This allows our services to finish cleaning up after themselves before the entire application terminates. If your service shuts down immediately you can just return <code>None</code> instead of a deferred.</p>
<p>Services can be organized into collections that get started and stopped together. The last <code>IService</code> method we&#8217;re going to look at, <a href="http://twistedmatrix.com/trac/browser/tags/releases/twisted-10.0.0/twisted/application/service.py#L107"><code>setServiceParent</code></a>, adds a Service to a collection:</p>
<pre>    def setServiceParent(parent):
        """
        Set the parent of the service.

        @type parent: L{IServiceCollection}
        @raise RuntimeError: Raised if the service already has a parent
            or if the service has a name and the parent already has a child
            by that name.
        """</pre>
<p>Any service can have a parent, which means services can be organized in a hierarchy. And that brings us to the next <code>Interface</code> we&#8217;re going to look at today.</p>
<h4>IServiceCollection</h4>
<p>The <a href="http://twistedmatrix.com/trac/browser/tags/releases/twisted-10.0.0/twisted/application/service.py#L203"><code>IServiceCollection</code></a> interface defines an object which can contain <code>IService</code> objects. A service collection is a just plain container class with methods to:</p>
<ul>
<li>Look up a service by name (<a href="http://twistedmatrix.com/trac/browser/tags/releases/twisted-10.0.0/twisted/application/service.py#L212"><code>getServiceNamed</code></a>)</li>
<li>Iterate over the services in the collection (<a href="http://twistedmatrix.com/trac/browser/tags/releases/twisted-10.0.0/twisted/application/service.py#L222"><code>__iter__</code></a>)</li>
<li>Add a service to the collection (<a href="http://twistedmatrix.com/trac/browser/tags/releases/twisted-10.0.0/twisted/application/service.py#L227"><code>addService</code></a>)</li>
<li>Remove a service from the collection (<a href="http://twistedmatrix.com/trac/browser/tags/releases/twisted-10.0.0/twisted/application/service.py#L236"><code>removeService</code></a>)</li>
</ul>
<p>Note that an implementation of <code>IServiceCollection</code> isn&#8217;t automatically an implementation of <code>IService</code>, but there&#8217;s no reason why one class can&#8217;t implement both interfaces (and we&#8217;ll see an example of that shortly).</p>
<h4>Application</h4>
<p>A Twisted <code>Application</code> is not defined by a separate interface. Rather, an <code>Application</code> object is required to implement both <code>IService</code> and <code>IServiceCollection</code>, as well as a few other interfaces we aren&#8217;t going to cover.</p>
<p>An <code>Application</code> is the top-level service that represents your entire Twisted application. All the other services in your daemon will be children (or grandchildren, etc.) of the <code>Application</code> object.</p>
<p>It is rare to actually implement your own <code>Application</code>. Twisted provides an implementation that we&#8217;ll use today.</p>
<h4>Twisted Logging</h4>
<p>Twisted includes its own logging infrastructure in the module <a href="http://twistedmatrix.com/trac/browser/tags/releases/twisted-10.0.0/twisted/python/log.py"><code>twisted.python.log</code></a>. The basic API for writing to the log is simple, so we&#8217;ll just include a short example located in <tt>basic-twisted/log.py</tt>, and you can skim the Twisted module for details if you are interested.</p>
<p>We won&#8217;t bother showing the API for installing logging handlers, since <code>twistd</code> will do that for us.</p>
<h3>FastPoetry 2.0</h3>
<p>Alright, let&#8217;s look at some code. We&#8217;ve updated the fast poetry server to run with <tt>twistd</tt>. The source is located in <a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-server-3/fastpoetry.py#L1"><tt>twisted-server-3/fastpoetry.py</tt></a>. First we have the <a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-server-3/fastpoetry.py#L9">poetry protocol</a>:</p>
<pre>class PoetryProtocol(Protocol):

    def connectionMade(self):
        poem = self.factory.service.poem
        log.msg('sending %d bytes of poetry to %s'
                % (len(poem), self.transport.getPeer()))
        self.transport.write(poem)
        self.transport.loseConnection()</pre>
<p>Notice instead of using a <code>print</code> statement, we&#8217;re using the <code>twisted.python.log.msg</code> function to record each new connection.<br />
Here&#8217;s the <a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-server-3/fastpoetry.py#L19">factory class</a>:</p>
<pre>class PoetryFactory(ServerFactory):

    protocol = PoetryProtocol

    def __init__(self, service):
        self.service = service</pre>
<p>As you can see, the poem is no longer stored on the factory, but on a service object referenced by the factory. Notice how the protocol gets the poem from the service via the factory. Finally, here&#8217;s the <a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-server-3/fastpoetry.py#L27">service class itself</a>:</p>
<pre>class PoetryService(service.Service):

    def __init__(self, poetry_file):
        self.poetry_file = poetry_file

    def startService(self):
        service.Service.startService(self)
        self.poem = open(self.poetry_file).read()
        log.msg('loaded a poem from: %s' % (self.poetry_file,))</pre>
<p>As with many other <code>Interface</code> classes, Twisted provides a base class we can use to make our own implementations, with helpful default behaviors. Here we use the <a href="http://twistedmatrix.com/trac/browser/tags/releases/twisted-10.0.0/twisted/application/service.py#L154"><code>twisted.application.service.Service</code></a> class to implement our <code>PoetryService</code>.</p>
<p>The base class provides default implementations of all required methods, so we only need to implement the ones with custom behavior. In this case, we just override <code>startService</code> to load the poetry file. Note we still call the base class method (which sets the <code>running</code> attribute for us).</p>
<p>Another point is worth mentioning. The <code>PoetryService</code> object doesn&#8217;t know anything about the details of the <code>PoetryProtocol</code>. The service&#8217;s only job is to load the poem and provide access to it for any object that might need it. In other words, the <code>PoetryService</code> is entirely concerned with the higher-level details of providing poetry, rather than the lower-level details of sending a poem down a TCP connection. So this same service could be used by another protocol, say UDP or XML-RPC. While the benefit is rather small for our simple service, you can imagine the advantage for a more realistic service implementation.</p>
<p>If this were a typical Twisted program, all the code we&#8217;ve looked at so far wouldn&#8217;t actually be in this file. Rather, it would be in some other module(s) (perhaps <code>fastpoetry.protocol</code> and <code>fastpoetry.service</code>). But following our usual practice of making these examples self-contained, we&#8217;ve including everything we need in a single script.</p>
<h4>Twisted <tt>tac</tt> files</h4>
<p>The rest of the script contains what would normally be the entire content — a Twisted <tt>tac</tt> file. A <tt>tac</tt> file is a Twisted Application Configuration file that tells <code>twistd</code> how to construct an application. As a configuration file it is responsible for choosing settings (like port numbers, poetry file locations, etc.) to run the application in some particular way. In other words, a <tt>tac</tt> file represents a specific deployment of our service (serve <em>that</em> poem on <em>this</em> port) rather than a general script for starting any poetry server.</p>
<p>If we were running multiple poetry servers on the same host, we would have a <tt>tac</tt> file for each one (so you can see why <tt>tac</tt> files normally don&#8217;t contain any general-purpose code). In our example, the <tt>tac</tt> file is configured to serve <tt>poetry/ecstasy.txt</tt> run on port <code>10000</code> of the loopback interface:</p>
<pre># configuration parameters
port = 10000
iface = 'localhost'
poetry_file = 'poetry/ecstasy.txt'</pre>
<p>Note that <tt>twistd</tt> doesn&#8217;t know anything about these particular variables, we just define them here to keep all our configuration values in one place. In fact, <tt>twistd</tt> only really cares about one variable in the entire file, as we&#8217;ll see shortly. Next we <a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-server-3/fastpoetry.py#L44">begin</a> building up our application:</p>
<pre># this will hold the services that combine to form the poetry server
top_service = service.MultiService()</pre>
<p>Our poetry server is going to consist of two services, the <code>PoetryService</code> we defined above, and a Twisted built-in service that creates the listening socket our poem will be served from. Since these two services are clearly related to each other, we&#8217;ll group them together using a <a href="http://twistedmatrix.com/trac/browser/tags/releases/twisted-10.0.0/twisted/application/service.py#L253"><code>MultiService</code></a>, a Twisted class which implements both <code>IService</code> and <code>IServiceCollection</code>.</p>
<p>As a service collection, the <code>MultiService</code> will group our two poetry services together. And as a service, the <code>MultiService</code> will start both child services when the <code>MultiService</code> itself is started, and stop both child services when it is stopped. Let&#8217;s <a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-server-3/fastpoetry.py#L48">add</a> the first poetry service to the collection:</p>
<pre># the poetry service holds the poem. it will load the poem when it is
# started
poetry_service = PoetryService(poetry_file)
poetry_service.setServiceParent(top_service)</pre>
<p>This is pretty simple stuff. We just create the <code>PoetryService</code> and then add it to the collection with <code>setServiceParent</code>, a method we inherited from the Twisted base class. Next we <a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-server-3/fastpoetry.py#L53">add</a> the TCP listener:</p>
<pre># the tcp service connects the factory to a listening socket. it will
# create the listening socket when it is started
factory = PoetryFactory(poetry_service)
tcp_service = internet.TCPServer(port, factory, interface=iface)
tcp_service.setServiceParent(top_service)</pre>
<p>Twisted provides the <code>TCPServer</code> service for creating a TCP listening socket connected to an arbitrary factory (in this case our <code>PoetryFactory</code>). We don&#8217;t call <code>reactor.listenTCP</code> directly because the job of a <tt>tac</tt> file is to get our application ready to start, without actually starting it. The <code>TCPServer</code> will create the socket after it is started by <tt>twistd</tt>.</p>
<p>You might have noticed we didn&#8217;t bother to give any of our services names. Naming services is not required, but only an optional feature you can use if you want to &#8216;look up&#8217; services at runtime. Since we don&#8217;t need to do that in our little application, we don&#8217;t bother with it here.</p>
<p>Ok, now we&#8217;ve got both our services combined into a collection. Now we just make our <code>Application</code> and <a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-server-3/fastpoetry.py#L58">add</a> our collection to it:</p>
<pre># this variable has to be named 'application'
application = service.Application("fastpoetry")

# this hooks the collection we made to the application
top_service.setServiceParent(application)</pre>
<p>The only variable in this script that <tt>twistd</tt> really cares about is the <code>application</code> variable. That is how <tt>twistd</tt> will find the application it&#8217;s supposed to start (and so the variable has to be named &#8216;application&#8217;). And when the application is started, all the services we added to it will be started as well.</p>
<p>Figure 34 shows the structure of the application we just built:</p>
<div id="attachment_2400" class="wp-caption aligncenter" style="width: 361px"><a href="http://krondo.com/blog/wp-content/uploads/2010/05/application.png"><img class="size-full wp-image-2400 " title="Figure 34: the structure of our fastpoetry application" src="http://krondo.com/blog/wp-content/uploads/2010/05/application.png" alt="Figure 34: the structure of our fastpoetry application" width="351" height="277" /></a><p class="wp-caption-text">Figure 34: the structure of our fastpoetry application</p></div>
<h4>Running the Server</h4>
<p>Let&#8217;s take our new server for a spin. As a <tt>tac</tt> file, we need to start it with <tt>twistd</tt>. Of course, it&#8217;s also just a regular Python file, too. So let&#8217;s run it with Python first and see what happens:</p>
<pre>python twisted-server-3/fastpoetry.py</pre>
<p>If you do this, you&#8217;ll find that what happens is nothing! As we said before, the job of a <tt>tac</tt> file is to get an application ready to run, without actually running it. As a reminder of this special purpose of <tt>tac</tt> files, some people name them with a <tt>.tac</tt> extension instead of <tt>.py</tt>. But the <tt>twistd</tt> script doesn&#8217;t actually care about the extension.</p>
<p>Let&#8217;s run our server for real, using <tt>twistd</tt>:</p>
<pre>twistd --nodaemon --python twisted-server-3/fastpoetry.py</pre>
<p>After running that command, you should see some output like this:</p>
<pre>2010-06-23 20:57:14-0700 [-] Log opened.
2010-06-23 20:57:14-0700 [-] twistd 10.0.0 (/usr/bin/python 2.6.5) starting up.
2010-06-23 20:57:14-0700 [-] reactor class: twisted.internet.selectreactor.SelectReactor.
2010-06-23 20:57:14-0700 [-] __builtin__.PoetryFactory starting on 10000
2010-06-23 20:57:14-0700 [-] Starting factory &lt;__builtin__.PoetryFactory instance at 0x14ae8c0&gt;
2010-06-23 20:57:14-0700 [-] loaded a poem from: poetry/ecstasy.txt</pre>
<p>Here&#8217;s a few things to notice:</p>
<ol>
<li>You can see the output of the Twisted logging system, including the <code>PoetryFactory</code>&#8216;s call to <code>log.msg</code>. But we didn&#8217;t install a logger in our <tt>tac</tt> file, so <tt>twistd</tt> must have installed one for us.</li>
<li>You can also see our two main services, the <code>PoetryService</code> and the <code>TCPServer</code> starting up.</li>
<li>The shell prompt never came back. That means our server isn&#8217;t running as a daemon. By default, <tt>twistd</tt> does run a server as a daemon process (that&#8217;s the main reason <tt>twistd</tt> exists), but if you include the <tt>--nodaemon</tt> option then <tt>twistd</tt> will run your server as a regular shell process instead, and will direct the log output to standard output as well. This is useful for debugging your <tt>tac</tt> files.</li>
</ol>
<p>Now test out the server by fetching a poem, either with one of our poetry clients or just <tt>netcat</tt>:</p>
<pre>netcat localhost 10000</pre>
<p>That should fetch the poem from the server and you should see a new log line like this:</p>
<pre>2010-06-27 22:17:39-0700 [__builtin__.PoetryFactory] sending 3003 bytes of poetry to IPv4Address(TCP, '127.0.0.1', 58208)</pre>
<p>That&#8217;s from the call to <code>log.msg</code> in <code>PoetryProtocol.connectionMade</code>. As you make more requests to the server, you will see additional log entries for each request.</p>
<p>Now stop the server by pressing <tt>Ctrl-C</tt>. You should see some output like this:</p>
<pre>^C2010-06-29 21:32:59-0700 [-] Received SIGINT, shutting down.
2010-06-29 21:32:59-0700 [-] (Port 10000 Closed)
2010-06-29 21:32:59-0700 [-] Stopping factory &lt;__builtin__.PoetryFactory instance at 0x28d38c0&gt;
2010-06-29 21:32:59-0700 [-] Main loop terminated.
2010-06-29 21:32:59-0700 [-] Server Shut Down.</pre>
<p>As you can see, Twisted does not simply crash, but shuts itself down cleanly and tells you about it with log messages. Notice our two main services shutting themselves down as well.</p>
<p>Ok, now start the server up once more:</p>
<pre>twistd --nodaemon --python twisted-server-3/fastpoetry.py</pre>
<p>Then open another shell and change to the <tt>twisted-intro</tt> directory. A directory listing should show a file called <tt>twistd.pid</tt>. This file is created by <tt>twistd</tt> and contains the process ID of our running server. Try executing this alternative command to shut down the server:</p>
<pre>kill `cat twistd.pid`</pre>
<p>Notice that <tt>twistd</tt> cleans up the process ID file when our server shuts down.</p>
<h4>A Real Daemon</h4>
<p>Now let&#8217;s start our server as an actual daemon process, which is even simpler to do as it&#8217;s <tt>twistd</tt>&#8216;s default behavior:</p>
<pre>twistd --python twisted-server-3/fastpoetry.py</pre>
<p>This time we get our shell prompt back almost immediately. And if you list the contents of your directory you will see, in addition to the <tt>twistd.pid</tt> file for the server we just ran, a <tt>twistd.log</tt> file with the log entries that were formerly displayed at the shell prompt.</p>
<p>When starting a daemon process, <tt>twistd</tt> installs a log handler that writes entries to a file instead of standard output. The default log file is <tt>twistd.log</tt>, located in the same directory where you ran <tt>twistd</tt>, but you can change that with the <tt>--logfile</tt> option if you wish. The handler that <tt>twistd</tt> installs also rotates the log whenever the size exceeds one megabyte.</p>
<p>You should be able to see the server running by listing all the processes on your system. Go ahead and test out the server by fetching another poem. You should see new entries appear in the log file for each poem you request.</p>
<p>Since the server is no longer connected to the shell (or any other process except <a href="http://en.wikipedia.org/wiki/Init"><tt>init</tt></a>), you cannot shut it down with <tt>Ctrl-C</tt>. As a true daemon process, it will continue to run even if you log out. But we can still use the <tt>twistd.pid</tt> file to stop the process:</p>
<pre>kill `cat twistd.pid`</pre>
<p>And when that happens the shutdown messages appear in the log, the <tt>twistd.pid</tt> file is removed, and our server stops running. Neato.</p>
<p>It&#8217;s a good idea to check out some of the other <tt>twistd</tt> startup options. For example, you can tell <tt>twistd</tt> to switch to a different user or group account before starting the daemon (typically a way to drop privileges your server doesn&#8217;t need as a security precaution). We won&#8217;t bother going into those extra options, you can find them using the <tt>--help</tt> switch to <tt>twistd</tt>.</p>
<h3>The Twisted Plugin System</h3>
<p>Ok, now we can use <tt>twistd</tt> to start up our servers as genuine daemon processes. This is all very nice, and the fact that our &#8220;configuration&#8221; files are really just Python source files gives us a great deal of flexibility in how we set things up. But we don&#8217;t always need that much flexibility. For our poetry servers, we typically only have a few options we might care about:</p>
<ol>
<li>The poem to serve.</li>
<li>The port to serve it from.</li>
<li>The interface to listen on.</li>
</ol>
<p>Making new <tt>tac</tt> files for simple variations on those values seems rather excessive. It would be nice if we could just specify those values as options on the <tt>twistd</tt> command line. The Twisted plugin system allows us to do just that.</p>
<p>Twisted plugins provide a way of defining named Applications, with a custom set of command-line options, that <tt>twistd</tt> can dynamically discover and run. Twisted itself comes with a set of built-in plugins. You can see them all by running <tt>twistd</tt> without any arguments. Try running it now, but outside of the <tt>twisted-intro</tt> directory. After the help section, you should see some output like this:</p>
<pre>    ...
    ftp                An FTP server.
    telnet             A simple, telnet-based remote debugging service.
    socks              A SOCKSv4 proxy service.
    ...</pre>
<p>Each line shows one of the built-in plugins that come with Twisted. And you can run any of them using <tt>twistd</tt>.<br />
Each plugin also comes with its own set of options, which you can discover using <tt>--help</tt>. Let&#8217;s see what the options for the <tt>ftp</tt> plugin are:</p>
<pre>twistd ftp --help</pre>
<p>Note that you need to put the <tt>--help</tt> switch after the <tt>ftp</tt> command, since you want the options for the <tt>ftp</tt> plugin rather than for <tt>twistd</tt> itself.<br />
We can run the <tt>ftp</tt> server with <tt>twistd</tt> just like we ran our poetry server. But since it&#8217;s a plugin, we just run it by name:</p>
<pre>twistd --nodaemon ftp --port 10001</pre>
<p>That command runs the <tt>ftp</tt> plugin in non-daemon mode on port 10001. Note the <tt>twistd</tt> option <tt>nodaemon</tt> comes before the plugin name, while the plugin-specific option <tt>port</tt> comes after the plugin name. As with our poetry server, you can stop that plugin with <tt>Ctrl-C</tt>.</p>
<p>Ok, let&#8217;s turn our poetry server into a Twisted plugin. First we need to introduce a couple of new concepts.</p>
<h4>IPlugin</h4>
<p>Any Twisted plugin must implement the <a href="http://twistedmatrix.com/trac/browser/tags/releases/twisted-10.0.0/twisted/plugin.py#L38"><code>twisted.plugin.IPlugin</code></a> interface. If you look at the declaration of that <code>Interface</code>, you&#8217;ll find it doesn&#8217;t actually specify any methods. Implementing <code>IPlugin</code> is simply a way for a plugin to say &#8220;Hello, I&#8217;m a plugin!&#8221; so <tt>twistd</tt> can find it. Of course, to be of any use, it will have to implement some other interface and we&#8217;ll get to that shortly.</p>
<p>But how do you know if an object actually implements an empty interface? The <code>zope.interface</code> package includes a function called <code>implements</code> that you can use to declare that a particular class implements a particular interface. We&#8217;ll see an example of that in the plugin version of our poetry server.</p>
<h4>IServiceMaker</h4>
<p>In addition to <code>IPlugin</code>, our plugin will implement the <a href="http://twistedmatrix.com/trac/browser/tags/releases/twisted-10.0.0/twisted/application/service.py#L25"><code>IServiceMaker</code></a> interface. An object which implements <code>IServiceMaker</code> knows how to create an <code>IService</code> that will form the heart of a running application. <code>IServiceMaker</code> specifies three attributes and a method:</p>
<ol>
<li><code>tapname</code>: a string name for our plugin. The &#8220;tap&#8221; stands for Twisted Application Plugin. Note: an older version of Twisted also made use of pickled application files called &#8220;tapfiles&#8221;, but that functionality is deprecated.</li>
<li><code>description</code>: a description of the plugin, which <tt>twistd</tt> will display as part of its help text.</li>
<li><code>options</code>: an object which describes the command-line options this plugin accepts.</li>
<li><code>makeService</code>: a method which creates a new <code>IService</code> object, given a specific set of command-line options</li>
</ol>
<p>We&#8217;ll see how all this gets put together in the next version of our poetry server.</p>
<h3>Fast Poetry 3.0</h3>
<p>Now we&#8217;re ready to take a look at the plugin version of Fast Poetry, located in <a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted/plugins/fastpoetry_plugin.py#L1"><tt>twisted/plugins/fastpoetry_plugin.py</tt></a>.</p>
<p>You might notice we&#8217;ve named these directories differently than any of the other examples. That&#8217;s because <tt>twistd</tt> requires plugin files to be located in a <tt>twisted/plugins</tt> directory, located in your Python module search path. The directory doesn&#8217;t have to be a package (i.e., you don&#8217;t need any <tt>__init__.py</tt> files) and you can have multiple <tt>twisted/plugins</tt> directories on your path and <tt>twistd</tt> will find them all. The actual filename you use for the plugin doesn&#8217;t matter either, but it&#8217;s still a good idea to name it according to the application it represents, like we have done here.</p>
<p>The first part of our plugin contains the same poetry protocol, factory, and service implementations as our <tt>tac</tt> file. And as before, this code would normally be in a separate module but we&#8217;ve placed it in the plugin to make the example self-contained.</p>
<p>Next comes the <a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted/plugins/fastpoetry_plugin.py#L45">declaration</a> of the plugin&#8217;s command-line options:</p>
<pre>class Options(usage.Options):

    optParameters = [
        ['port', 'p', 10000, 'The port number to listen on.'],
        ['poem', None, None, 'The file containing the poem.'],
        ['iface', None, 'localhost', 'The interface to listen on.'],
        ]</pre>
<p>This code specifies the plugin-specific options that a user can place after the plugin name on the <tt>twistd</tt> command line. We won&#8217;t go into details here as it should be fairly clear what is going on. Now we get to the main part of our plugin, the <a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted/plugins/fastpoetry_plugin.py#L56">service maker class</a>:</p>
<pre>class PoetryServiceMaker(object):

    implements(service.IServiceMaker, IPlugin)

    tapname = "fastpoetry"
    description = "A fast poetry service."
    options = Options

    def makeService(self, options):
        top_service = service.MultiService()

        poetry_service = PoetryService(options['poem'])
        poetry_service.setServiceParent(top_service)

        factory = PoetryFactory(poetry_service)
        tcp_service = internet.TCPServer(int(options['port']), factory,
                                         interface=options['iface'])
        tcp_service.setServiceParent(top_service)

        return top_service</pre>
<p>Here you can see how the <code>zope.interface.implements</code> function is used to declare that our class implements both <code>IServiceMaker</code> and <code>IPlugin</code>.</p>
<p>You should recognize the code in <code>makeService</code> from our earlier <tt>tac</tt> file implementation. But this time we don&#8217;t need to make an <code>Application</code> object ourselves, we just create and return the top level service that our application will run and <tt>twistd</tt> will take care of the rest. Notice how we use the <code>options</code> argument to retrieve the plugin-specific command-line options given to <tt>twistd</tt>.</p>
<p>After declaring that class, there&#8217;s only on thing left <a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted/plugins/fastpoetry_plugin.py#L81">to do</a>:</p>
<pre>service_maker = PoetryServiceMaker()</pre>
<p>The <tt>twistd</tt> script will discover that instance of our plugin and use it to construct the top level service. Unlike the <tt>tac</tt> file, the variable name we choose is irrelevant.  What matters is that our object implements both <code>IPlugin</code> and <code>IServiceMaker</code>.</p>
<p>Now that we&#8217;ve created our plugin, let&#8217;s run it. Make sure that you are in the <tt>twisted-intro</tt> directory, or that the <tt>twisted-intro</tt> directory is in your python module search path. Then try running <tt>twistd</tt> by itself. You should now see that &#8220;fastpoetry&#8221; is one of the plugins listed, along with the description text from our plugin file.</p>
<p>You will also notice that a new file called <tt>dropin.cache</tt> has appeared in the <tt>twisted/plugins</tt> directory. This file is created by <tt>twistd</tt> to speed up subsequent scans for plugins.</p>
<p>Now let&#8217;s get some help on using our plugin:</p>
<pre>twistd fastpoetry --help</pre>
<p>You should see the options that are specific to the fastpoetry plugin in the help text. Finally, let&#8217;s run our plugin:</p>
<pre>twistd fastpoetry --port 10000 --poem poetry/ecstasy.txt</pre>
<p>That will start a fastpoetry server running as a daemon. As before, you should see both <tt>twistd.pid</tt> and <tt>twistd.log</tt> files in the current directory. After testing out the server, you can shut it down:</p>
<pre>kill `cat twistd.pid`</pre>
<p>And that&#8217;s how you make a Twisted plugin.</p>
<h3>Summary</h3>
<p>In this Part we learned about turning our Twisted servers into long-running daemons. We touched on the Twisted logging system and on how to use <tt>twistd</tt> to start a Twisted application as a daemon process, either from a <tt>tac</tt> configuration file or a Twisted plugin. In <a href="?p=2441">Part 17</a> we&#8217;ll return to the more fundamental topic of asynchronous programming and look at another way of structuring our callbacks in Twisted.</p>
<h3>Suggested Exercises</h3>
<ol>
<li>Modify the <tt>tac</tt> file to serve a second poem on another port. Keep the services for each poem separate by using another <code>MultiService</code> object.</li>
<li>Create a new <tt>tac</tt> file that starts a poetry proxy server.</li>
<li>Modify the plugin file to accept an optional second poetry file and second port to serve it on.</li>
<li>Create a new plugin for the poetry proxy server.</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://krondo.com/?feed=rss2&amp;p=2345</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	</channel>
</rss>
