<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Python | krondo</title><link>https://krondo.com/category/python/</link><description>Posts in Python on krondo</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><copyright>Dave Peticolas</copyright><lastBuildDate>Sat, 30 Jul 2011 14:48:14 +0000</lastBuildDate><atom:link href="https://krondo.com/category/python/" rel="self" type="application/rss+xml"/><item><title>More Chinese Translation</title><link>https://krondo.com/more-chinese-translation/</link><pubDate>Sat, 30 Jul 2011 14:48:14 +0000</pubDate><guid isPermaLink="true">https://krondo.com/more-chinese-translation/</guid><category>Programming</category><category>Python</category><category>Software</category><description>Cheng Luo has translated an additional portion of the Twisted Introduction into Chinese. You can find it on github. Thank you, Cheng Luo!</description><content:encoded>&lt;p&gt;Cheng Luo has translated an additional portion of the Twisted Introduction into Chinese. You can find it on &lt;a href="https://github.com/luocheng/twisted-intro-cn"&gt;github&lt;/a&gt;. Thank you, Cheng Luo!&lt;/p&gt;</content:encoded></item><item><title>Twisted Intro: PDF version</title><link>https://krondo.com/twisted-intro-pdf-version/</link><pubDate>Thu, 14 Apr 2011 04:04:48 +0000</pubDate><guid isPermaLink="true">https://krondo.com/twisted-intro-pdf-version/</guid><category>Blather</category><category>Programming</category><category>Python</category><category>Software</category><description>Piotr Dobrogost has converted the entire series into complete html and pdf versions. Thank you, Piotr!</description><content:encoded>&lt;p&gt;&lt;a href="http://stackoverflow.com/users/95735/"&gt;Piotr Dobrogost&lt;/a&gt; has converted the entire series into complete &lt;a href="https://krondo.com/uploads/2009/08/twisted-intro.html"&gt;html&lt;/a&gt; and &lt;a href="https://krondo.com/uploads/2009/08/twisted-intro.pdf"&gt;pdf&lt;/a&gt; versions. Thank you, Piotr!&lt;/p&gt;</content:encoded></item><item><title>Twisted Introduction: Chinese Translation</title><link>https://krondo.com/twisted-introduction-chinese-translation/</link><pubDate>Fri, 08 Apr 2011 06:32:22 +0000</pubDate><guid isPermaLink="true">https://krondo.com/twisted-introduction-chinese-translation/</guid><category>Blather</category><category>Programming</category><category>Python</category><category>Software</category><description>Yang Xiaowei has translated a large portion of the Twisted Introduction into Chinese. Thank you, Yang!</description><content:encoded>&lt;p&gt;Yang Xiaowei has translated a large portion of the &lt;a href="https://krondo.com/an-introduction-to-asynchronous-programming-and-twisted/"&gt;Twisted Introduction&lt;/a&gt; into &lt;a href="http://blog.sina.com.cn/s/blog_704b6af70100py9n.html"&gt;Chinese&lt;/a&gt;. Thank you, Yang!&lt;/p&gt;</content:encoded></item><item><title>Twisted Introduction Russian Translation</title><link>https://krondo.com/twisted-introduction-russian-translation/</link><pubDate>Tue, 22 Feb 2011 21:45:38 +0000</pubDate><guid isPermaLink="true">https://krondo.com/twisted-introduction-russian-translation/</guid><category>Blather</category><category>Programming</category><category>Python</category><category>Software</category><description>Nina Evseenko has kindly translated the Twisted Introduction into Russian. Thank you Nina!
The index page also has links to earlier translations into Japanese and Estonian by Shigeru Kitazaki and Tim Gluz respectively.</description><content:encoded>&lt;p&gt;Nina Evseenko has kindly translated the &lt;a href="https://krondo.com/an-introduction-to-asynchronous-programming-and-twisted/"&gt;Twisted Introduction&lt;/a&gt; into &lt;a href="http://ninaevseenko.github.com/async_twisted_ru/"&gt;Russian&lt;/a&gt;. Thank you Nina!&lt;/p&gt;
&lt;p&gt;The index page also has links to earlier translations into Japanese and Estonian by Shigeru Kitazaki and Tim Gluz respectively.&lt;/p&gt;</content:encoded></item><item><title>The End</title><link>https://krondo.com/part-22-the-end/</link><pubDate>Sun, 21 Nov 2010 05:45:22 +0000</pubDate><guid isPermaLink="true">https://krondo.com/part-22-the-end/</guid><category>Blather</category><category>Programming</category><category>Python</category><category>Software</category><description>Part 22: The End This concludes the introduction started here. You can find an index to the entire series here.
All Done Whew! Thank you for sticking with me. When I started this series I didn’t realize it was going to be this long, or take this much time to make. But I have enjoyed creating it and hope you have enjoyed reading it.
Now that I have finished, I will look into the possibility of generating a PDF format. No promises, though.</description><content:encoded>&lt;h2 id="part-22-the-end"&gt;Part 22: The End&lt;/h2&gt;
&lt;p&gt;This concludes the introduction started &lt;a href="https://krondo.com/in-which-we-begin-at-the-beginning/"&gt;here&lt;/a&gt;. You can find an index to the entire series &lt;a href="https://krondo.com/an-introduction-to-asynchronous-programming-and-twisted/"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="all-done"&gt;All Done&lt;/h3&gt;
&lt;p&gt;Whew! Thank you for sticking with me. When I started this series I didn&amp;rsquo;t realize it was going to be this long, or take this much time to make. But I have enjoyed creating it and hope you have enjoyed reading it.&lt;/p&gt;
&lt;p&gt;Now that I have finished, I will look into the possibility of generating a PDF format. No promises, though.&lt;/p&gt;
&lt;p&gt;I would like to conclude with a few suggestions on how to continue your Twisted education.&lt;/p&gt;
&lt;h3 id="further-reading"&gt;Further Reading&lt;/h3&gt;
&lt;p&gt;First, I would recommend reading the Twisted &lt;a href="http://twistedmatrix.com/trac/wiki/Documentation"&gt;online documentation&lt;/a&gt;. Although it is much-maligned, I think it&amp;rsquo;s better than it is often given credit for.&lt;/p&gt;
&lt;p&gt;If you want to use Twisted for web programming, then Jean-Paul Calderone has a well-regarded series called &amp;ldquo;&lt;a href="http://jcalderone.livejournal.com/50562.html"&gt;Twisted Web in 60 Seconds&lt;/a&gt;&amp;rdquo;. I suspect it will take a little longer than that to read, though.&lt;/p&gt;
&lt;p&gt;There is also a &lt;a href="http://www.amazon.com/gp/product/0596100329?ie=UTF8&amp;amp;tag=jpcalsjou-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=0596100329"&gt;Twisted Book&lt;/a&gt;, which I can&amp;rsquo;t say much about as I haven&amp;rsquo;t read it.&lt;/p&gt;
&lt;p&gt;But more important than any of those, I think, is to read the &lt;a href="http://twistedmatrix.com/trac/browser/trunk"&gt;Twisted source code&lt;/a&gt;. Since that code is written by people who know Twisted very well, it is an excellent source of examples for how do to things the &amp;ldquo;Twisted Way&amp;rdquo;.&lt;/p&gt;
&lt;h3 id="suggested-exercises"&gt;Suggested Exercises&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Port a synchronous program you wrote to Twisted.&lt;/li&gt;
&lt;li&gt;Write a new Twisted program from scratch.&lt;/li&gt;
&lt;li&gt;Pick a bug from the Twisted &lt;a href="http://twistedmatrix.com/trac/report"&gt;bug database&lt;/a&gt; and fix it. Submit a patch to the Twisted developers. Don&amp;rsquo;t forget to read about the &lt;a href="http://twistedmatrix.com/trac/wiki/ContributingToTwistedLabs"&gt;process&lt;/a&gt; for making your contribution.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="the-end-really"&gt;The End, Really&lt;/h3&gt;
&lt;p&gt;Happy Hacking!&lt;/p&gt;
&lt;figure id="figure47" class="wp-caption aligncenter"&gt;
&lt;a href="https://krondo.com/uploads/2010/11/theend1.png"&gt;&lt;img src="https://krondo.com/part-22-the-end/images/theend1.png" alt="Figure 47: The End" width="465" height="284" loading="lazy" decoding="async"&gt;&lt;/a&gt;
&lt;figcaption&gt;Figure 47: The End&lt;/figcaption&gt;
&lt;/figure&gt;</content:encoded></item><item><title>Lazy is as Lazy Doesn't: Twisted and Haskell</title><link>https://krondo.com/twisted-and-haskell/</link><pubDate>Fri, 19 Nov 2010 04:00:38 +0000</pubDate><guid isPermaLink="true">https://krondo.com/twisted-and-haskell/</guid><category>Blather</category><category>Programming</category><category>Python</category><category>Software</category><description>Part 21: Lazy is as Lazy Doesn’t: Twisted and Haskell This continues the introduction started here. You can find an index to the entire series here.
Introduction In the last Part we compared Twisted with Erlang, giving most of our attention to some ideas they have in common. And that ended up being pretty simple, as asynchronous I/O and reactive programming are key components of the Erlang runtime and process model.
Today we are going to range further afield and look at Haskell, another functional language that is nevertheless quite different from Erlang (and, of course, Python). There won’t be as many parallels, but we will nevertheless find some asynchronous I/O hiding under the covers.</description><content:encoded>&lt;h2 id="part-21-lazy-is-as-lazy-doesnt-twisted-and-haskell"&gt;Part 21: Lazy is as Lazy Doesn&amp;rsquo;t: Twisted and Haskell&lt;/h2&gt;
&lt;p&gt;This continues the introduction started &lt;a href="https://krondo.com/in-which-we-begin-at-the-beginning/"&gt;here&lt;/a&gt;. You can find an index to the entire series &lt;a href="https://krondo.com/an-introduction-to-asynchronous-programming-and-twisted/"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;
&lt;p&gt;In the last Part we compared Twisted with &lt;a href="http://erlang.org"&gt;Erlang&lt;/a&gt;, giving most of our attention to some ideas they have in common. And that ended up being pretty simple, as asynchronous I/O and reactive programming are key components of the Erlang runtime and process model.&lt;/p&gt;
&lt;p&gt;Today we are going to range further afield and look at &lt;a href="http://haskell.org"&gt;Haskell&lt;/a&gt;, another functional language that is nevertheless quite different from Erlang (and, of course, Python). There won&amp;rsquo;t be as many parallels, but we will nevertheless find some asynchronous I/O hiding under the covers.&lt;/p&gt;
&lt;h3 id="functional-with-a-capital-f"&gt;Functional with a Capital F&lt;/h3&gt;
&lt;p&gt;Although Erlang is also a functional language, its main focus is a reliable concurrency model. Haskell, on the other hand, is functional through and through, making unabashed use of concepts from &lt;a href="http://en.wikipedia.org/wiki/Category_theory"&gt;category theory&lt;/a&gt; like &lt;a href="http://en.wikipedia.org/wiki/Functor"&gt;functors&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Monad_%28category_theory%29"&gt;monads&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t worry, we&amp;rsquo;re not going into any of that here (as if we could). Instead we&amp;rsquo;ll focus on one of Haskell&amp;rsquo;s more traditionally functional features: laziness. Like many functional languages (but unlike Erlang), Haskell supports &lt;a href="http://en.wikipedia.org/wiki/Lazy_evaluation"&gt;lazy evaluation&lt;/a&gt;. In a lazily evaluated language the text of a program doesn&amp;rsquo;t so much describe how to compute something as what to compute. The details of actually performing the computation are generally left to the compiler and runtime system.&lt;/p&gt;
&lt;p&gt;And, more to the point, as a lazily-evaluated computation proceeds the runtime may evaluate expressions only partially (lazily) instead of all at once. In general, the runtime will evaluate only as much of an expression as is needed to make progress on the current computation.&lt;/p&gt;
&lt;p&gt;Here is a simple Haskell statement applying &lt;code&gt;head&lt;/code&gt;, a function that retrieves the first element of a list, to the list &lt;code&gt;[1,2,3]&lt;/code&gt; (Haskell and Python share some of their list syntax):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-haskell" data-lang="haskell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you install the &lt;a href="http://www.haskell.org/ghc/"&gt;GHC&lt;/a&gt; Haskell runtime, you can try this out yourself:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[~] ghci
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GHCi, version 6.12.1: http://www.haskell.org/ghc/ : ? for help
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Loading package ghc-prim ... linking ... done.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Loading package integer-gmp ... linking ... done.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Loading package base ... linking ... done.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Prelude&amp;gt; head [1,2,3]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Prelude&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The result is the number 1, as expected.&lt;/p&gt;
&lt;p&gt;The Haskell list syntax includes the handy ability to define a list from its first couple of elements. For example, the list &lt;code&gt;[2,4 ..]&lt;/code&gt; is the sequence of even numbers starting with 2. Where does it end? Well, it doesn&amp;rsquo;t. The Haskell list [2,4 ..] and others like it represent (conceptually) infinite lists. You can see this if you try to evaluate one at the interactive Haskell prompt, which will attempt to print out the result of your expression:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Prelude&amp;gt; [2,4 ..]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140,142,144,146,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You&amp;rsquo;ll have to press Ctrl-C to stop that computation as it will never actually terminate. But because of lazy evaluation, it is possible to use these infinite lists in Haskell with no trouble:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Prelude&amp;gt; head [2,4 ..]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Prelude&amp;gt; head (tail [2,4 ..])
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;4
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Prelude&amp;gt; head (tail (tail [2,4 ..]))
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;6
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here we are accessing the first, second, and third elements of this infinite list respectively, with no infinite loop anywhere in sight. This is the essence of lazy evaluation. Instead of first evaluating the entire list (which would cause an infinite loop) and then giving that list to the &lt;code&gt;head&lt;/code&gt; function, the Haskell runtime only constructs as much of the list as it needs for &lt;code&gt;head&lt;/code&gt; to finish its work. The rest of the list is never constructed at all, because it is not needed to proceed with the computation.&lt;/p&gt;
&lt;p&gt;When we bring the &lt;code&gt;tail&lt;/code&gt; function into play, Haskell is forced to construct the list further, but again only as much as it needs to evaluate the next step of the computation. And once the computation is done, the (unfinished) list can be discarded.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s some Haskell code that partially consumes three different infinite lists:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Prelude&amp;gt; let x = [1..]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Prelude&amp;gt; let y = [2,4 ..]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Prelude&amp;gt; let z = [3,6 ..]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Prelude&amp;gt; head (tail (tail (zip3 x y z)))
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;(3,6,9)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here we zip all the lists together, then grab the head of the tail of the tail. Once again, Haskell has no trouble with this and only constructs as much of each list as it needs to finish evaluating our code. We can visualize the Haskell runtime &amp;ldquo;consuming&amp;rdquo; these infinite lists in Figure 46:&lt;/p&gt;
&lt;figure id="figure46" class="wp-caption aligncenter"&gt;
&lt;a href="https://krondo.com/uploads/2010/10/haskell.png"&gt;&lt;img src="https://krondo.com/twisted-and-haskell/images/haskell.png" alt="Figure 46: Haskell consuming some infinite lists" width="467" height="177" loading="lazy" decoding="async"&gt;&lt;/a&gt;
&lt;figcaption&gt;Figure 46: Haskell consuming some infinite lists&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Although we&amp;rsquo;ve drawn the Haskell runtime as a simple loop, it might be implemented with multiple threads (and probably is if you are using the GHC version of Haskell). But the main point to notice is how this figure looks like a reactor loop consuming bits of data as they come in on network sockets.&lt;/p&gt;
&lt;p&gt;You can think of asynchronous I/O and the reactor pattern as a very limited form of lazy evaluation. The asynchronous I/O motto is: &amp;ldquo;Only process as much data as you have&amp;rdquo;. And the lazy evaluation motto is: &amp;ldquo;Only process as much data as you need&amp;rdquo;. Furthermore, a lazily-evaluated language applies that motto almost everywhere, not just in the limited scope of I/O.&lt;/p&gt;
&lt;p&gt;But the point is that, for a lazily-evaluated language, making use of asynchronous I/O is no big deal. The compiler and runtime are already designed to process data structures bit by bit, so lazily processing the incoming chunks of an I/O stream is just par for the course. And thus the Haskell runtime, like the Erlang runtime, simply incorporates asynchronous I/O as part of its socket abstractions. And we can show that by implementing a poetry client in Haskell.&lt;/p&gt;
&lt;h3 id="haskell-poetry"&gt;Haskell Poetry&lt;/h3&gt;
&lt;p&gt;Our first Haskell poetry client is located in &lt;a href="https://github.com/jdavisp3/twisted-intro/blob/master/haskell-client-1/get-poetry.hs"&gt;haskell-client-1/get-poetry.hs&lt;/a&gt;. As with Erlang, we&amp;rsquo;re going to jump straight to a finished client, and then suggest further reading if you&amp;rsquo;d like to learn more.&lt;/p&gt;
&lt;p&gt;Haskell also supports light-weight threads or processes, though they aren&amp;rsquo;t as central to Haskell as they are to Erlang, and our Haskell client creates one process for each poem we want to download. The key function there is &lt;a href="https://github.com/jdavisp3/twisted-intro/blob/master/haskell-client-1/get-poetry.hs#L64"&gt;&lt;code&gt;runTask&lt;/code&gt;&lt;/a&gt; which connects to a socket and starts the &lt;a href="https://github.com/jdavisp3/twisted-intro/blob/master/haskell-client-1/get-poetry.hs#L48"&gt;&lt;code&gt;getPoetry&lt;/code&gt;&lt;/a&gt; function in a light-weight thread.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll notice a lot of type declarations in this code. Haskell, unlike Python or Erlang, is statically typed. We don&amp;rsquo;t declare types for each and every variable because Haskell will automatically infer types not explicitly declared (or report an error if it can&amp;rsquo;t). A number of the functions include the &lt;code&gt;IO&lt;/code&gt; type (technically a monad) because Haskell requires us to cleanly separate code with side-effects (i.e., code that performs I/O) from pure functions.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;getPoetry&lt;/code&gt; function includes this line:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-haskell" data-lang="haskell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;poem&lt;/span&gt; &lt;span class="ow"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;hGetContents&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;which appears to be reading the entire poem from the handle (i.e., the TCP socket) at once. But Haskell, as usual, is lazy. And the Haskell runtime includes one or more actual threads which perform asynchronous I/O in a &lt;code&gt;select&lt;/code&gt; loop, thus preserving the possibilities for lazy evaluation of I/O streams.&lt;/p&gt;
&lt;p&gt;Just to illustrate that asynchronous I/O is really going on, we have included a &amp;ldquo;callback&amp;rdquo; function, &lt;a href="https://github.com/jdavisp3/twisted-intro/blob/master/haskell-client-1/get-poetry.hs#L60"&gt;&lt;code&gt;gotLine&lt;/code&gt;&lt;/a&gt;, that prints out some task information for each line in the poem. But it&amp;rsquo;s not really a callback function at all, and the program would use asynchronous I/O whether we included it or not. Even calling it &amp;ldquo;gotLine&amp;rdquo; reflects an imperative-language mindset that is out of place in a Haskell program. No matter, we&amp;rsquo;ll clean it up in a bit, but let&amp;rsquo;s take our first Haskell client out for a spin. Start up some slow poetry servers:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python blocking-server/slowpoetry.py --port &lt;span class="m"&gt;10001&lt;/span&gt; poetry/fascination.txt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python blocking-server/slowpoetry.py --port &lt;span class="m"&gt;10002&lt;/span&gt; poetry/science.txt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python blocking-server/slowpoetry.py --port &lt;span class="m"&gt;10003&lt;/span&gt; poetry/ecstasy.txt --num-bytes &lt;span class="m"&gt;30&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now compile the Haskell client:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; haskell-client-1/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ghc --make get-poetry.hs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will create a binary called &lt;code&gt;get-poetry&lt;/code&gt;. Finally, run the client against our servers:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./get-poetry &lt;span class="m"&gt;10001&lt;/span&gt; &lt;span class="m"&gt;10002&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And you should see some output like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Task 3: got 12 bytes of poetry from localhost:10003
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Task 3: got 1 bytes of poetry from localhost:10003
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Task 3: got 30 bytes of poetry from localhost:10003
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Task 2: got 20 bytes of poetry from localhost:10002
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Task 3: got 44 bytes of poetry from localhost:10003
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Task 2: got 1 bytes of poetry from localhost:10002
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Task 3: got 29 bytes of poetry from localhost:10003
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Task 1: got 36 bytes of poetry from localhost:10001
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Task 1: got 1 bytes of poetry from localhost:10001
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The output is slightly different than previous asynchronous clients because we are printing one line for each line of poetry instead of each arbitrary chunk of data. But, as you can see, the client is clearly processing data from all the servers together, instead of one after the other. You&amp;rsquo;ll also notice that the client prints out the first poem as soon as it&amp;rsquo;s finished, without waiting for the others, which continue on at their own pace.&lt;/p&gt;
&lt;p&gt;Alright, let&amp;rsquo;s clean the remaining bits of imperative cruft from our client and present a version which just grabs the poetry without bothering with task numbers. You can find it in &lt;a href="https://github.com/jdavisp3/twisted-intro/blob/master/haskell-client-2/get-poetry.hs"&gt;haskell-client-2/get-poetry.hs&lt;/a&gt;. Notice that it&amp;rsquo;s much shorter and, for each server, just connects to the socket, grabs all the data, and sends it back.&lt;/p&gt;
&lt;p&gt;Ok, let&amp;rsquo;s compile a new client:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; haskell-client-2/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ghc --make get-poetry.hs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And run it against the same set of poetry servers:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./get-poetry &lt;span class="m"&gt;10001&lt;/span&gt; &lt;span class="m"&gt;10002&lt;/span&gt; &lt;span class="m"&gt;10003&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And you should see the text of each poem appear, eventually, on the screen.&lt;/p&gt;
&lt;p&gt;You will notice from the server output that each server is sending data to the client simultaneously. What&amp;rsquo;s more, the client prints out each line of the first poem as soon as possible, without waiting for the rest of the poem, even while it&amp;rsquo;s working on the other two. And then it quickly prints out the second poem, which it has been accumulating all along.&lt;/p&gt;
&lt;p&gt;And all of that happens without us having to do much of anything. There are no callbacks, no messages being passed back and forth, just a concise description of what we want the program to do, and very little in the way of how it should go about doing it. The rest is taken care of by the Haskell compiler and runtime. Nifty.&lt;/p&gt;
&lt;h3 id="discussion-and-further-reading"&gt;Discussion and Further Reading&lt;/h3&gt;
&lt;p&gt;In moving from Twisted to Erlang to Haskell we can see a parallel movement, from the foreground to the background, of the ideas behind asynchronous programming. In Twisted, asynchronous programming is the central motivating idea behind Twisted&amp;rsquo;s existence. And Twisted&amp;rsquo;s implementation as a framework separate from Python (and Python&amp;rsquo;s lack of core asynchronous abstractions like lightweight threads) keeps the asynchronous model front and center when you write programs using Twisted.&lt;/p&gt;
&lt;p&gt;In Erlang, asynchronicity is still very visible to the programmer, but the details are now part of the fabric of the language and runtime system, enabling an abstraction in which asynchronous messages are exchanged between synchronous processes.&lt;/p&gt;
&lt;p&gt;And finally, in Haskell, asynchronous I/O is just another technique inside the runtime, largely unseen by the programmer, for providing the lazy evaluation that is one of Haskell&amp;rsquo;s central ideas.&lt;/p&gt;
&lt;p&gt;We don&amp;rsquo;t have any profound insight into this situation, we&amp;rsquo;re just pointing out the many and interesting places where the asynchronous model shows up, and the many different ways it can be expressed.&lt;/p&gt;
&lt;p&gt;And if any of this has piqued your interest in Haskell, then we can recommend &lt;a href="http://www.amazon.com/exec/obidos/ASIN/0596514980/krondonet-20"&gt;Real World Haskell&lt;/a&gt; to continue your studies. The book is a model of what a good language introduction should be. And while I haven&amp;rsquo;t read it, I&amp;rsquo;ve heard good things about &lt;a href="http://learnyouahaskell.com/"&gt;Learn You a Haskell&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This brings us to the end of our tour of asynchronous systems outside of Twisted, and the penultimate part in our series. In &lt;a href="https://krondo.com/part-22-the-end/"&gt;Part 22&lt;/a&gt; we will conclude, and suggest ways to learn more about Twisted.&lt;/p&gt;
&lt;h3 id="suggested-exercises-for-the-startlingly-motivated"&gt;Suggested Exercises for the Startlingly Motivated&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Compare the Twisted, Erlang, and Haskell clients with each other.&lt;/li&gt;
&lt;li&gt;Modify the Haskell clients to handle failures to connect to a poetry server so they download all the poetry they can and output reasonable error messages for the poems they can&amp;rsquo;t.&lt;/li&gt;
&lt;li&gt;Write Haskell versions of the poetry servers we made with Twisted.&lt;/li&gt;
&lt;/ol&gt;</content:encoded></item><item><title>Wheels within Wheels: Twisted and Erlang</title><link>https://krondo.com/twisted-and-erlang/</link><pubDate>Sun, 03 Oct 2010 04:40:10 +0000</pubDate><guid isPermaLink="true">https://krondo.com/twisted-and-erlang/</guid><category>Blather</category><category>Erlang</category><category>Programming</category><category>Python</category><category>Software</category><description>Part 20: Wheels within Wheels: Twisted and Erlang This continues the introduction started here. You can find an index to the entire series here.
Introduction One fact we’ve uncovered in this series is that mixing synchronous “plain Python” code with asynchronous Twisted code is not a straightforward task, since blocking for an indeterminate amount of time in a Twisted program will eliminate many of the benefits you are trying to achieve using the asynchronous model.</description><content:encoded>&lt;h2 id="part-20-wheels-within-wheels-twisted-and-erlang"&gt;Part 20: Wheels within Wheels: Twisted and Erlang&lt;/h2&gt;
&lt;p&gt;This continues the introduction started &lt;a href="https://krondo.com/in-which-we-begin-at-the-beginning/"&gt;here&lt;/a&gt;. You can find an index to the entire series &lt;a href="https://krondo.com/an-introduction-to-asynchronous-programming-and-twisted/"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;
&lt;p&gt;One fact we&amp;rsquo;ve uncovered in this series is that mixing synchronous &amp;ldquo;plain Python&amp;rdquo; code with asynchronous Twisted code is not a straightforward task, since blocking for an indeterminate amount of time in a Twisted program will eliminate many of the benefits you are trying to achieve using the asynchronous model.&lt;/p&gt;
&lt;p&gt;If this is your first introduction to asynchronous programming it may seem as if the knowledge you have gained is of somewhat limited applicability. You can use these new techniques inside of Twisted, but not in the much larger world of general Python code. And when working with Twisted, you are generally limited to libraries written specifically for use as part of a Twisted program, at least if you want to call them directly from the thread running the reactor.&lt;/p&gt;
&lt;p&gt;But asynchronous programming techniques have been around for quite some time and are hardly confined to Twisted. There are in fact a startling number of asynchronous programming frameworks in Python alone. A bit of &lt;a href="http://www.google.com/search?q=python+async+frameworks"&gt;searching around&lt;/a&gt; will probably yield a couple dozen of them. They differ from Twisted in their details, but the basic ideas (asynchronous I/O, processing data in small chunks across multiple data streams) are the same. So if you need, or choose, to use an alternative framework you will already have a head start having learned Twisted.&lt;/p&gt;
&lt;p&gt;And moving outside of Python, there are plenty of other languages and systems that are either based around, or make use of, the asynchronous programming model. Your knowledge of Twisted will continue to serve you as you explore the wider areas of this subject.&lt;/p&gt;
&lt;p&gt;In this Part we&amp;rsquo;re going to take a very brief look at &lt;a href="http://erlang.org"&gt;Erlang&lt;/a&gt;, a programming language and runtime system that makes extensive use of asynchronous programming concepts, but does so in a unique way. Please note this is not meant as a general introduction to Erlang. Rather, it is a short exploration of some of the ideas embedded in Erlang and how they connect with the ideas in Twisted. The basic theme is the knowledge you have gained learning Twisted can be applied when learning other technologies.&lt;/p&gt;
&lt;h3 id="callbacks-reimagined"&gt;Callbacks Reimagined&lt;/h3&gt;
&lt;p&gt;Consider &lt;a href="https://krondo.com/our-eye-beams-begin-to-twist/#figure6"&gt;Figure 6&lt;/a&gt;, a graphical representation of a callback. The principle callback in &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-client-3/get-poetry.py#L1"&gt;Poetry Client 3.0&lt;/a&gt;, introduced in &lt;a href="https://krondo.com/and-then-we-took-it-higher/"&gt;Part 6&lt;/a&gt;, and all subsequent poetry clients is the &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-client-3/get-poetry.py#L56"&gt;&lt;code&gt;dataReceived&lt;/code&gt;&lt;/a&gt; method. That callback is invoked each time we get a bit more poetry from one of the poetry servers we have connected to.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s say our client is downloading three poems from three different servers. Looking at things from the point of view of the reactor (and that&amp;rsquo;s the viewpoint we&amp;rsquo;ve emphasized the most in this series), we&amp;rsquo;ve got a single big loop which makes one or more callbacks each time it goes around. See Figure 40:&lt;/p&gt;
&lt;figure id="figure40" class="wp-caption alignnone"&gt;
&lt;a href="https://krondo.com/uploads/2010/09/reactor-2.png"&gt;&lt;img src="https://krondo.com/twisted-and-erlang/images/reactor-2.png" alt="Figure 40: callbacks from the reactor viewpoint" width="203" height="286" loading="lazy" decoding="async"&gt;&lt;/a&gt;
&lt;figcaption&gt;Figure 40: callbacks from the reactor viewpoint&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This figure shows the reactor happily spinning around, calling &lt;code&gt;dataReceived&lt;/code&gt; as the poetry comes in. Each invocation of &lt;code&gt;dataReceived&lt;/code&gt; is applied to one particular instance of our &lt;code&gt;PoetryProtocol&lt;/code&gt; class. And we know there are three instances because we are downloading three poems (and so there must be three connections).&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s think about this picture from the point of view of &lt;em&gt;one&lt;/em&gt; of those Protocol instances. Remember each Protocol is only concerned with a single connection (and thus a single poem). That instance &amp;ldquo;sees&amp;rdquo; a stream of method calls, each one bearing the next piece of the poem, like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;dataReceived&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;When I have fears&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;dataReceived&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34; that I may cease to be&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;dataReceived&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Before my pen has glea&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;dataReceived&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;n&amp;#39;d my teeming brain&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;While this isn&amp;rsquo;t strictly speaking an actual Python loop, we can conceptualize it as one:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;poetry_stream&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="c1"&gt;# pseudo-code&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;dataReceived&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can envision this &amp;ldquo;callback loop&amp;rdquo; in Figure 41:&lt;/p&gt;
&lt;figure id="figure41" class="wp-caption alignnone"&gt;
&lt;a href="https://krondo.com/uploads/2010/09/callback-loop.png"&gt;&lt;img src="https://krondo.com/twisted-and-erlang/images/callback-loop.png" alt="Figure 41: A virtual callback loop" width="192" height="168" loading="lazy" decoding="async"&gt;&lt;/a&gt;
&lt;figcaption&gt;Figure 41: A virtual callback loop&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Again, this is not a &lt;code&gt;for&lt;/code&gt; loop or a &lt;code&gt;while&lt;/code&gt; loop. The only significant Python loop in our poetry clients is the reactor. But we can think of each Protocol as a virtual loop that ticks around once each time some poetry for that particular poem comes in. With that in mind we can re-imagine the entire client in Figure 42:&lt;/p&gt;
&lt;figure id="figure42" class="wp-caption alignnone"&gt;
&lt;a href="https://krondo.com/uploads/2010/09/reactor-3.png"&gt;&lt;img src="https://krondo.com/twisted-and-erlang/images/reactor-3.png" alt="Figure 42: the reactor spinning some virtual loops" width="207" height="260" loading="lazy" decoding="async"&gt;&lt;/a&gt;
&lt;figcaption&gt;Figure 42: the reactor spinning some virtual loops&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In this figure we have one big loop, the reactor, and three virtual loops, the individual poetry protocol instances. The big loop spins around and, in so doing, causes the virtual loops to tick over as well, like a set of interlocking gears.&lt;/p&gt;
&lt;h3 id="enter-erlang"&gt;Enter Erlang&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://erlang.org"&gt;Erlang&lt;/a&gt;, like Python, is a general purpose dynamically typed programming language originally created in the 80&amp;rsquo;s. Unlike Python, Erlang is functional rather than object-oriented, and has a syntax reminiscent of &lt;a href="http://en.wikipedia.org/wiki/Prolog"&gt;Prolog&lt;/a&gt;, the language in which Erlang was originally implemented. Erlang was designed for building highly reliable distributed telephony systems, and thus Erlang contains extensive networking support.&lt;/p&gt;
&lt;p&gt;One of Erlang&amp;rsquo;s most distinctive features is a concurrency model involving lightweight processes. An Erlang process is neither an operating system process nor an operating system thread. Rather, it is an independently running function inside the Erlang runtime with its own stack. Erlang processes are not lightweight threads because Erlang processes cannot share state (and most data types are immutable anyway, Erlang being a functional programming language). An Erlang process can interact with other Erlang processes only by sending messages, and messages are always, at least conceptually, copied and never shared.&lt;/p&gt;
&lt;p&gt;So an Erlang program might look like Figure 43:&lt;/p&gt;
&lt;figure id="figure43" class="wp-caption alignnone"&gt;
&lt;a href="https://krondo.com/uploads/2010/09/erlang-11.png"&gt;&lt;img src="https://krondo.com/twisted-and-erlang/images/erlang-11.png" alt="Figure 43: An Erlang program with three processes" width="203" height="290" loading="lazy" decoding="async"&gt;&lt;/a&gt;
&lt;figcaption&gt;Figure 43: An Erlang program with three processes&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In this figure the individual processes have become &amp;ldquo;real&amp;rdquo;, since processes are first-class constructs in Erlang, just like objects are in Python. And the runtime has become &amp;ldquo;virtual&amp;rdquo;, not because it isn&amp;rsquo;t there, but because it&amp;rsquo;s not necessarily a simple loop. The Erlang runtime may be multi-threaded and, as it has to implement a full-blown programming language, it&amp;rsquo;s in charge of a lot more than handling asynchronous I/O. Furthermore, a language runtime is not so much an extra construct, like the reactor in Twisted, as the medium in which the Erlang processes and code execute.&lt;/p&gt;
&lt;p&gt;So an even better picture of an Erlang program might be Figure 44:&lt;/p&gt;
&lt;figure id="figure44" class="wp-caption alignnone"&gt;
&lt;a href="https://krondo.com/uploads/2010/09/erlang-2.png"&gt;&lt;img src="https://krondo.com/twisted-and-erlang/images/erlang-2.png" alt="Figure 44: An Erlang program with several processes" width="333" height="239" loading="lazy" decoding="async"&gt;&lt;/a&gt;
&lt;figcaption&gt;Figure 44: An Erlang program with several processes&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Of course, the Erlang runtime does have to use asynchronous I/O and one or more select loops, because Erlang allows you to create &lt;em&gt;lots&lt;/em&gt; of processes. Large Erlang programs can start tens or hundreds of thousands of Erlang processes, so allocating an actual OS thread to each one is simply out of the question. If Erlang is going to allow multiple processes to perform I/O, and still allow other processes to run even if that I/O blocks, then asynchronous I/O will have to be involved.&lt;/p&gt;
&lt;p&gt;Note that our picture of an Erlang program has each process running &amp;ldquo;under its own power&amp;rdquo;, rather than being spun around by callbacks. And that is very much the case. With the job of the reactor subsumed into the fabric of the Erlang runtime, the callback no longer has a central role to play. What would, in Twisted, be solved by using a callback would, in Erlang, be solved by sending an asynchronous message from one Erlang process to another.&lt;/p&gt;
&lt;h3 id="an-erlang-poetry-client"&gt;An Erlang Poetry Client&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s look at an Erlang poetry client. We&amp;rsquo;re going to jump straight to a working version instead of building up slowly like we did with Twisted. Again, this isn&amp;rsquo;t meant as a complete Erlang introduction. But if it piques your interest, we suggest some more in-depth reading at the end of this Part.&lt;/p&gt;
&lt;p&gt;The Erlang client is listed in &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/erlang-client-1/get-poetry#L1"&gt;erlang-client-1/get-poetry&lt;/a&gt;. In order to run it you will, of course, need &lt;a href="http://erlang.org"&gt;Erlang&lt;/a&gt; installed. Here&amp;rsquo;s the code for the &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/erlang-client-1/get-poetry#L96"&gt;&lt;code&gt;main&lt;/code&gt;&lt;/a&gt; function, which serves a similar purpose as the main functions in our Python clients:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-erlang" data-lang="erlang"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;([])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;Addresses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;Main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nn"&gt;erlang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;spawn_monitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;get_poetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;TaskNum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;TaskNum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Addr&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Addresses&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;collect_poems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Addresses&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[]).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you&amp;rsquo;ve never seen Prolog or a similar language before then Erlang syntax is going to seem a little odd. But some people say that about Python, too. The main function is defined by two separate clauses, separated by a semicolon. Erlang chooses which clause to run by matching the arguments, so the first clause only runs if we execute the client without providing any command line arguments, and it just prints out a help message. The second clause is where all the action is.&lt;/p&gt;
&lt;p&gt;Individual statements in an Erlang function are separated by commas, and all functions end with a period. Let&amp;rsquo;s take each line in the second clause one at a time. The first line is just parsing the command line arguments and binding them to a variable (all variables in Erlang must be capitalized). The second line is using the Erlang &lt;code&gt;self&lt;/code&gt; function to get the process ID of the currently running Erlang process (not OS process). Since this is the main function you can kind of think of it as the equivalent of the &lt;code&gt;__main__&lt;/code&gt; module in Python. The third line is the most interesting:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-erlang" data-lang="erlang"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nn"&gt;erlang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;spawn_monitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;get_poetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;TaskNum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;TaskNum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Addr&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Addresses&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This statement is an Erlang list comprehension, with a syntax similar to that in Python. It is spawning new Erlang processes, one for each poetry server we need to contact. And each process will run the same function (&lt;code&gt;get_poetry&lt;/code&gt;) but with different arguments specific to that server. We also pass the PID of the main process so the new processes can send the poetry back (you generally need the PID of a process to send a message to it).&lt;/p&gt;
&lt;p&gt;The last statement in &lt;code&gt;main&lt;/code&gt; calls the &lt;code&gt;collect_poems&lt;/code&gt; function which waits for the poetry to come back and for the &lt;code&gt;get_poetry&lt;/code&gt; processes to finish. We&amp;rsquo;ll look at the other functions in a bit, but first you might compare this Erlang &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/erlang-client-1/get-poetry#L96"&gt;&lt;code&gt;main&lt;/code&gt;&lt;/a&gt; function to the &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-client-4/get-poetry.py#L96"&gt;equivalent main&lt;/a&gt; in one of our Twisted clients.&lt;/p&gt;
&lt;p&gt;Now let&amp;rsquo;s look at the Erlang &lt;code&gt;get_poetry&lt;/code&gt; function. There are actually two functions in our script called &lt;code&gt;get_poetry&lt;/code&gt;. In Erlang, a function is identified by both name and arity, so our script contains two separate functions, &lt;code&gt;get_poetry/3&lt;/code&gt; and &lt;code&gt;get_poetry/4&lt;/code&gt; which accept three and four arguments respectively. Here&amp;rsquo;s &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/erlang-client-1/get-poetry#L79"&gt;&lt;code&gt;get_poetry/3&lt;/code&gt;&lt;/a&gt;, which is spawned by &lt;code&gt;main&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-erlang" data-lang="erlang"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;get_poetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Tasknum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;Addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;gen_tcp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;binary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;false&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;packet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}]),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;get_poetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Tasknum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This function first makes a TCP connection, just like the Twisted client &lt;code&gt;get_poetry&lt;/code&gt;. But then, instead of returning, it proceeds to use that TCP connection by calling &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/erlang-client-1/get-poetry#L85"&gt;&lt;code&gt;get_poetry/4&lt;/code&gt;&lt;/a&gt;, listed below:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-erlang" data-lang="erlang"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;get_poetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Tasknum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Packets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nn"&gt;gen_tcp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Packet&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Task &lt;/span&gt;&lt;span class="si"&gt;~w&lt;/span&gt;&lt;span class="s"&gt;: got &lt;/span&gt;&lt;span class="si"&gt;~w&lt;/span&gt;&lt;span class="s"&gt; bytes of poetry from &lt;/span&gt;&lt;span class="si"&gt;~s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Tasknum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Packet&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;peername&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;)]),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;get_poetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Tasknum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Packet&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="nv"&gt;Packets&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;_}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;Main&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;poem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;list_to_binary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;lists&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Packets&lt;/span&gt;&lt;span class="p"&gt;))}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This Erlang function is doing the work of the &lt;code&gt;PoetryProtocol&lt;/code&gt; from our Twisted client, except it does so using blocking function calls. The &lt;code&gt;gen_tcp:recv&lt;/code&gt; function waits until some data arrives on the socket (or the socket is closed), however long that might be. But a &amp;ldquo;blocking&amp;rdquo; function in Erlang only blocks the process running the function, not the entire Erlang runtime. That TCP socket isn&amp;rsquo;t really a blocking socket (you can&amp;rsquo;t make a true blocking socket in pure Erlang code). For each of those Erlang sockets there is, somewhere inside the Erlang runtime, a &amp;ldquo;real&amp;rdquo; TCP socket set to non-blocking mode and used as part of a select loop.&lt;/p&gt;
&lt;p&gt;But the Erlang process doesn&amp;rsquo;t have to know about any of that. It just waits for some data to arrive and, if it blocks, some other Erlang process can run instead. And even if a process never blocks, the Erlang runtime is free to switch execution from that process to another at any time. In other words, Erlang has a non-cooperative concurrency model.&lt;/p&gt;
&lt;p&gt;Notice that &lt;code&gt;get_poetry/4&lt;/code&gt;, after receiving a bit of poem, proceeds by recursively calling itself. To an imperative language programmer this might seem like a recipe for running out of memory, but the Erlang compiler can optimize &amp;ldquo;tail&amp;rdquo; calls (function calls that are the last statement in a function) into loops. And this highlights another curious parallel between the Erlang and Twisted clients. In the Twisted client, the &amp;ldquo;virtual&amp;rdquo; loops are created by the reactor calling the same function (&lt;code&gt;dataReceived&lt;/code&gt;) over and over again. And in the Erlang client, the &amp;ldquo;real&amp;rdquo; processes running (&lt;code&gt;get_poetry/4&lt;/code&gt;) form loops by calling &lt;em&gt;themselves&lt;/em&gt; over and over again via &lt;a href="http://stackoverflow.com/questions/310974/what-is-tail-call-optimization"&gt;tail-call optimization&lt;/a&gt;. How about that.&lt;/p&gt;
&lt;p&gt;If the connection is closed, the last thing &lt;code&gt;get_poetry&lt;/code&gt; does is send the poem to the main process. That also ends the process that &lt;code&gt;get_poetry&lt;/code&gt; is running, as there is nothing left for it to do.&lt;/p&gt;
&lt;p&gt;The remaining key function in our Erlang client is &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/erlang-client-1/get-poetry#L58"&gt;&lt;code&gt;collect_poems&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-erlang" data-lang="erlang"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;collect_poems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Poems&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;~s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;P&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="nv"&gt;P&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nv"&gt;Poems&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;collect_poems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;N&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Poems&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;receive&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;&amp;#39;DOWN&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;_,&lt;/span&gt; &lt;span class="p"&gt;_,&lt;/span&gt; &lt;span class="p"&gt;_,&lt;/span&gt; &lt;span class="p"&gt;_}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;collect_poems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;N&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Poems&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;poem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Poem&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;collect_poems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;N&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Poem&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="nv"&gt;Poems&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This function is run by the main process and, like &lt;code&gt;get_poetry&lt;/code&gt;, it recursively loops on itself. It also blocks. The &lt;code&gt;receive&lt;/code&gt; statement tells the process to wait for a message to arrive that matches one of the given patterns, and then extract the message from its &amp;ldquo;mailbox&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;collect_poems&lt;/code&gt; function waits for two kinds of messages: poems and &amp;ldquo;DOWN&amp;rdquo; notifications. The latter is a message sent to the main process when one of the &lt;code&gt;get_poetry&lt;/code&gt; processes dies for any reason (this is the &lt;code&gt;monitor&lt;/code&gt; part of &lt;code&gt;spawn_monitor&lt;/code&gt;). By counting &lt;code&gt;DOWN&lt;/code&gt; messages, we know when all the poetry has finished. The former is a message from one of the &lt;code&gt;get_poetry&lt;/code&gt; processes containing one complete poem.&lt;/p&gt;
&lt;p&gt;Ok, let&amp;rsquo;s take the Erlang client out for a spin. First start up three slow poetry servers:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python blocking-server/slowpoetry.py --port &lt;span class="m"&gt;10001&lt;/span&gt; poetry/fascination.txt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python blocking-server/slowpoetry.py --port &lt;span class="m"&gt;10002&lt;/span&gt; poetry/science.txt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python blocking-server/slowpoetry.py --port &lt;span class="m"&gt;10003&lt;/span&gt; poetry/ecstasy.txt --num-bytes &lt;span class="m"&gt;30&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we can run the Erlang client, which has a similar command-line syntax as the Python clients. If you are on a Linux or other UNIX-like system, then you should be able to run the client directly (assuming you have Erlang installed and available in your PATH). On Windows you will probably need to run the escript program, with the path to the Erlang client as the first argument (with the remaining arguments for the Erlang client itself).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./erlang-client-1/get-poetry &lt;span class="m"&gt;10001&lt;/span&gt; &lt;span class="m"&gt;10002&lt;/span&gt; &lt;span class="m"&gt;10003&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After that you should see output like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Task 3: got 30 bytes of poetry from 127:0:0:1:10003
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Task 2: got 10 bytes of poetry from 127:0:0:1:10002
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Task 1: got 10 bytes of poetry from 127:0:0:1:10001
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is just like one of our earlier Python clients where we print a message for each little bit of poetry we get. When all the poems have finished the client should print out the complete text of each one. Notice the client is switching back and forth between all the servers depending on which one has some poetry to send.&lt;/p&gt;
&lt;p&gt;Figure 45 shows the process structure of our Erlang client:&lt;/p&gt;
&lt;figure id="figure45" class="wp-caption alignnone"&gt;
&lt;a href="https://krondo.com/uploads/2010/09/erlang-3.png"&gt;&lt;img src="https://krondo.com/twisted-and-erlang/images/erlang-3.png" alt="Figure 45: Erlang poetry client" width="274" height="236" loading="lazy" decoding="async"&gt;&lt;/a&gt;
&lt;figcaption&gt;Figure 45: Erlang poetry client&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This figure shows three &lt;code&gt;get_poetry&lt;/code&gt; processes (one per server) and one main process. You can also see the messages that flow from the poetry processes to main process.&lt;/p&gt;
&lt;p&gt;So what happens if one of those servers is down? Let&amp;rsquo;s try it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./erlang-client-1/get-poetry &lt;span class="m"&gt;10001&lt;/span&gt; &lt;span class="m"&gt;10005&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The above command contains one active port (assuming you left all the earlier poetry servers running) and one inactive port (assuming you aren&amp;rsquo;t running any server on port 10005). And we get some output like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Task 1: got 10 bytes of poetry from 127:0:0:1:10001
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;=ERROR REPORT==== 25-Sep-2010::21:02:10 ===
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Error in process &amp;lt;0.33.0&amp;gt; with exit value: {{badmatch,{error,econnrefused}},[{erl_eval,expr,3}]}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Task 1: got 10 bytes of poetry from 127:0:0:1:10001
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Task 1: got 10 bytes of poetry from 127:0:0:1:10001
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And eventually the client finishes downloading the poem from the active server, prints out the poem, and exits. So how did the &lt;code&gt;main&lt;/code&gt; function know that both processes were done? That error message is the clue. The error happens when &lt;code&gt;get_poetry&lt;/code&gt; tries to connect to the server and gets a connection refused error instead of the expected value (&lt;code&gt;{ok, Socket}&lt;/code&gt;). The resulting exception is called badmatch because Erlang &amp;ldquo;assignment&amp;rdquo; statements are really pattern-matching operations.&lt;/p&gt;
&lt;p&gt;An unhandled exception in an Erlang process causes the process to &amp;ldquo;crash&amp;rdquo;, which means the process stops running and all of its resources are garbage collected. But the &lt;code&gt;main&lt;/code&gt; process, which is monitoring all of the &lt;code&gt;get_poetry&lt;/code&gt; processes, will receive a &lt;code&gt;DOWN&lt;/code&gt; message when any of those processes stops running for any reason. And thus our client exits when it should instead of running forever.&lt;/p&gt;
&lt;h3 id="discussion"&gt;Discussion&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s take stock of some of the parallels between the Twisted and Erlang clients:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Both clients connect (or try to connect) to all the poetry servers at once.&lt;/li&gt;
&lt;li&gt;Both clients receive data from the servers as soon as it comes in, regardless of which server delivers the data.&lt;/li&gt;
&lt;li&gt;Both clients process the poetry in little bits, and thus have to save the portion of the poems received thus far.&lt;/li&gt;
&lt;li&gt;Both clients create an &amp;ldquo;object&amp;rdquo; (either a Python object or an Erlang process) to handle all the work for one particular server.&lt;/li&gt;
&lt;li&gt;Both clients have to carefully determine when all the poetry has finished, regardless of whether a particular download succeeded or failed.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And finally, the &lt;code&gt;main&lt;/code&gt; functions in both clients asynchronously receive poems and &amp;ldquo;task done&amp;rdquo; notifications. In the Twisted client this information is delivered via a &lt;code&gt;Deferred&lt;/code&gt; while the Erlang client receives inter-process messages.&lt;/p&gt;
&lt;p&gt;Notice how similar both clients are, in both their overall strategy and the structure of their code. The mechanics are a bit different, with objects, deferreds, and callbacks on the one hand and processes and messages on the other. But the high-level mental models of both clients are quite similar, and it&amp;rsquo;s pretty easy to move from one to the other once you are familiar with both.&lt;/p&gt;
&lt;p&gt;Even the reactor pattern reappears in the Erlang client in miniaturized form. Each Erlang process in our poetry client eventually turns into a recursive loop that:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Waits for something to happen (a bit of poetry comes in, a poem is delivered, another process finishes), and&lt;/li&gt;
&lt;li&gt;Takes some appropriate action.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can think of an Erlang program as a big collection of little reactors, each spinning around and occasionally sending a message to another little reactor (which will process that message as just another event).&lt;/p&gt;
&lt;p&gt;And if you delve deeper into Erlang you will find callbacks making an appearance. The Erlang &lt;a href="http://www.erlang.org/doc/man/gen_server.html"&gt;&lt;code&gt;gen_server&lt;/code&gt;&lt;/a&gt; process is a generic reactor loop that you &amp;ldquo;instantiate&amp;rdquo; by providing a fixed set of callback functions, a pattern repeated elsewhere in the Erlang system.&lt;/p&gt;
&lt;p&gt;So if, having learned Twisted, you ever decide to give Erlang a try I think you will find yourself in familiar mental territory.&lt;/p&gt;
&lt;h3 id="further-reading"&gt;Further Reading&lt;/h3&gt;
&lt;p&gt;In this Part we&amp;rsquo;ve focused on the similarities between Twisted and Erlang, but there are of course many differences. One particularly unique feature of Erlang is its approach to error handling. A large Erlang program is structured as a tree of processes, with &amp;ldquo;supervisors&amp;rdquo; in the higher branches and &amp;ldquo;workers&amp;rdquo; in the leaves. And if a worker process crashes, a supervisor process will notice and take some action (typically restarting the failed worker).&lt;/p&gt;
&lt;p&gt;If you are interested in learning more Erlang then you are in luck. Several Erlang books have either been published recently, or will be published shortly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.amazon.com/exec/obidos/ASIN/193435600X/krondonet-20"&gt;Programming Erlang&lt;/a&gt; — written by one of Erlang&amp;rsquo;s inventors. A great introduction to the language.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.amazon.com/exec/obidos/ASIN/0596518188/krondonet-20"&gt;Erlang Programming&lt;/a&gt; — this complements the Armstrong book and goes into more detail in several key areas.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.amazon.com/exec/obidos/ASIN/1933988789/krondonet-20"&gt;Erlang and OTP in Action&lt;/a&gt; — this hasn&amp;rsquo;t been published yet, but I am eagerly awaiting my copy. Neither of the first two books really addresses OTP, the Erlang framework for building large apps. Full disclosure: two of the authors are friends of mine.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Well that&amp;rsquo;s it for Erlang. In the &lt;a href="https://krondo.com/twisted-and-haskell/"&gt;next Part&lt;/a&gt; we will look at Haskell, another functional language with a very different feel from either Python or Erlang. Nevertheless, we shall endeavor to find some common ground.&lt;/p&gt;
&lt;h3 id="suggested-exercises-for-the-highly-motivated"&gt;Suggested Exercises for the Highly Motivated&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Go through the Erlang and Python clients and identify where they are similar and where they differ. How do they each handle errors (like a failure to connect to a poetry server)?&lt;/li&gt;
&lt;li&gt;Simplify the Erlang client so it no longer prints out each bit of poetry that comes in (so you don&amp;rsquo;t need to keep track of task numbers either).&lt;/li&gt;
&lt;li&gt;Modify the Erlang client to measure the time it takes to download each poem.&lt;/li&gt;
&lt;li&gt;Modify the Erlang client to print out the poems in the same order as they were given on the command line.&lt;/li&gt;
&lt;li&gt;Modify the Erlang client to print out a more readable error message when we can&amp;rsquo;t connect to a poetry server.&lt;/li&gt;
&lt;li&gt;Write Erlang versions of the poetry servers we made with Twisted.&lt;/li&gt;
&lt;/ol&gt;</content:encoded></item><item><title>I Thought I Wanted It But I Changed My Mind</title><link>https://krondo.com/i-thought-i-wanted-it-but-i-changed-my-mind/</link><pubDate>Sun, 12 Sep 2010 19:55:56 +0000</pubDate><guid isPermaLink="true">https://krondo.com/i-thought-i-wanted-it-but-i-changed-my-mind/</guid><category>Blather</category><category>Programming</category><category>Python</category><category>Software</category><description>Part 19: I Thought I Wanted It But I Changed My Mind This continues the introduction started here. You can find an index to the entire series here.
Introduction Twisted is an ongoing project and the Twisted developers regularly add new features and extend old ones. With the release of Twisted 10.1.0, the developers added a new capability — cancellation — to the Deferred class which we’re going to investigate today.
Asynchronous programming decouples requests from responses and thus raises a new possibility: between asking for the result and getting it back you might decide you don’t want it anymore. Consider the poetry proxy server from Part 14. Here’s how the proxy worked, at least for the first request of a poem:</description><content:encoded>&lt;h2 id="part-19-i-thought-i-wanted-it-but-i-changed-my-mind"&gt;Part 19: I Thought I Wanted It But I Changed My Mind&lt;/h2&gt;
&lt;p&gt;This continues the introduction started &lt;a href="https://krondo.com/in-which-we-begin-at-the-beginning/"&gt;here&lt;/a&gt;. You can find an index to the entire series &lt;a href="https://krondo.com/an-introduction-to-asynchronous-programming-and-twisted/"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;
&lt;p&gt;Twisted is an ongoing project and the Twisted developers regularly add new features and extend old ones. With the release of Twisted 10.1.0, the developers added a new capability — cancellation — to the &lt;code&gt;Deferred&lt;/code&gt; class which we&amp;rsquo;re going to investigate today.&lt;/p&gt;
&lt;p&gt;Asynchronous programming decouples requests from responses and thus raises a new possibility: between asking for the result and getting it back you might decide you don&amp;rsquo;t want it anymore. Consider the poetry proxy server from &lt;a href="https://krondo.com/when-a-deferred-isnt/"&gt;Part 14&lt;/a&gt;. Here&amp;rsquo;s how the proxy worked, at least for the first request of a poem:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A request for a poem comes in.&lt;/li&gt;
&lt;li&gt;The proxy contacts the real server to get the poem.&lt;/li&gt;
&lt;li&gt;Once the poem is complete, send it to the original client.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Which is all well and good, but what if the client hangs up before getting the poem? Maybe they requested the complete text of &lt;a href="http://www.online-literature.com/milton/paradiselost/"&gt;Paradise Lost&lt;/a&gt; and then decided they really wanted a haiku by &lt;a href="http://www.toyomasu.com/haiku/#kojo"&gt;Kojo&lt;/a&gt;. Now our proxy is stuck with downloading the first one and that slow server is going to take a while. Better to close the connection and let the slow server go back to sleep.&lt;/p&gt;
&lt;p&gt;Recall &lt;a href="https://krondo.com/a-second-interlude-deferred/#figure15"&gt;Figure 15&lt;/a&gt;, a diagram that shows the conceptual flow of control in a synchronous program. In that figure we see function calls going down, and exceptions going back up. If we wanted to cancel a synchronous function call (and this is just hypothetical) the flow control would go in the same direction as the function call, from high-level code to low-level code as in Figure 38:&lt;/p&gt;
&lt;figure id="figure38" class="wp-caption alignnone"&gt;
&lt;a href="https://krondo.com/uploads/2010/08/sync-cancel.png"&gt;&lt;img src="https://krondo.com/i-thought-i-wanted-it-but-i-changed-my-mind/images/sync-cancel.png" alt="Figure 38: synchronous program flow, with hypothetical cancellation" width="376" height="257" loading="lazy" decoding="async"&gt;&lt;/a&gt;
&lt;figcaption&gt;Figure 38: synchronous program flow, with hypothetical cancellation&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Of course, in a synchronous program that isn&amp;rsquo;t possible because the high-level code doesn&amp;rsquo;t even resume running until the low-level operation is finished, at which point there is nothing to cancel. But in an asynchronous program the high-level code gets control of the program before the low-level code is done, which at least raises the possibility of canceling the low-level request before it finishes.&lt;/p&gt;
&lt;p&gt;In a Twisted program, the lower-level request is embodied by a &lt;code&gt;Deferred&lt;/code&gt; object, which you can think of as a &amp;ldquo;handle&amp;rdquo; on the outstanding asynchronous operation. The normal flow of information in a deferred is downward, from low-level code to high-level code, which matches the flow of return information in a synchronous program. Starting in Twisted 10.1.0, high-level code can send information back the other direction — it can tell the low-level code it doesn&amp;rsquo;t want the result anymore. See Figure 39:&lt;/p&gt;
&lt;figure id="figure39" class="wp-caption alignnone"&gt;
&lt;a href="https://krondo.com/uploads/2010/08/deferred-cancel.png"&gt;&lt;img src="https://krondo.com/i-thought-i-wanted-it-but-i-changed-my-mind/images/deferred-cancel.png" alt="Figure 39: Information flow in a deferred, including cancellation" width="396" height="242" loading="lazy" decoding="async"&gt;&lt;/a&gt;
&lt;figcaption&gt;Figure 39: Information flow in a deferred, including cancellation&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id="canceling-deferreds"&gt;Canceling Deferreds&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s take a look at a few sample programs to see how canceling deferreds actually works. Note, to run the examples and other code in this Part you will need a &lt;a href="http://twistedmatrix.com/trac/wiki/Downloads"&gt;version&lt;/a&gt; of Twisted 10.1.0 or later. Consider &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/deferred-cancel/defer-cancel-1.py#L1"&gt;deferred-cancel/defer-cancel-1.py&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twisted.internet&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;callback got:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Deferred&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;done&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With the new cancellation feature, the &lt;code&gt;Deferred&lt;/code&gt; class got a new method called &lt;code&gt;cancel&lt;/code&gt;. The example code makes a new deferred, adds a callback, and then cancels the deferred without firing it. Here&amp;rsquo;s the output:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;done
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Unhandled error in Deferred:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Traceback (most recent call last):
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Failure: twisted.internet.defer.CancelledError:
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ok, so canceling a deferred appears to cause the errback chain to run, and our regular callback is never called at all. Also notice the error is a &lt;code&gt;twisted.internet.defer.CancelledError&lt;/code&gt;, a custom Exception that means the deferred was canceled (but keep reading!). Let&amp;rsquo;s try adding an errback in &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/deferred-cancel/defer-cancel-2.py#L1"&gt;deferred-cancel/defer-cancel-2.py&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twisted.internet&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;callback got:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;errback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;errback got:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Deferred&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addCallbacks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;done&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we get this output:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;errback got: [Failure instance: Traceback (failure with no frames): &amp;lt;class &amp;#39;twisted.internet.defer.CancelledError&amp;#39;&amp;gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;done
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So we can &amp;lsquo;catch&amp;rsquo; the errback from a cancel just like any other deferred failure.&lt;/p&gt;
&lt;p&gt;Ok, let&amp;rsquo;s try firing the deferred and then canceling it, as in &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/deferred-cancel/defer-cancel-3.py#L1"&gt;deferred-cancel/defer-cancel-3.py&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twisted.internet&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;callback got:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;errback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;errback got:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Deferred&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addCallbacks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;result&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;done&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here we fire the deferred normally with the &lt;code&gt;callback&lt;/code&gt; method and then cancel it. Here&amp;rsquo;s the output:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;callback got: result
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;done
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Our callback was invoked (just as we would expect) and then the program finished normally, as if &lt;code&gt;cancel&lt;/code&gt; was never called at all. So it seems canceling a deferred has no effect if it has already fired (but keep reading!).&lt;/p&gt;
&lt;p&gt;What if we fire the deferred &lt;em&gt;after&lt;/em&gt; we cancel it, as in &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/deferred-cancel/defer-cancel-4.py#L1"&gt;deferred-cancel/defer-cancel-4.py&lt;/a&gt;?&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twisted.internet&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;callback got:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;errback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;errback got:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Deferred&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addCallbacks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;result&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;done&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In that case we get this output:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;errback got: [Failure instance: Traceback (failure with no frames): &amp;lt;class &amp;#39;twisted.internet.defer.CancelledError&amp;#39;&amp;gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;done
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Interesting! That&amp;rsquo;s the same output as the second example, where we never fired the deferred at all. So if the deferred has been canceled, firing the deferred normally has no effect. But why doesn&amp;rsquo;t &lt;code&gt;d.callback('result')&lt;/code&gt; raise an error, since you&amp;rsquo;re not supposed to be able to fire a deferred more than once, and the errback chain has clearly run?&lt;/p&gt;
&lt;p&gt;Consider Figure 39 again. Firing a deferred with a result or failure is the job of lower-level code, while canceling a deferred is an action taken by higher-level code. Firing the deferred means &amp;ldquo;Here&amp;rsquo;s your result&amp;rdquo;, while canceling a deferred means &amp;ldquo;I don&amp;rsquo;t want it any more&amp;rdquo;. And remember that canceling is a new feature, so most existing Twisted code is not written to handle cancel operations. But the Twisted developers have made it possible for us to cancel any deferred we want to, even if the code we got the deferred from was written before Twisted 10.1.0.&lt;/p&gt;
&lt;p&gt;To make that possible, the &lt;code&gt;cancel&lt;/code&gt; method actually does two things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Tell the &lt;code&gt;Deferred&lt;/code&gt; object &lt;em&gt;itself&lt;/em&gt; that you don&amp;rsquo;t want the result if it hasn&amp;rsquo;t shown up yet (i.e, the deferred hasn&amp;rsquo;t been fired), and thus to ignore any subsequent invocation of &lt;code&gt;callback&lt;/code&gt; or &lt;code&gt;errback&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;And, &lt;em&gt;optionally&lt;/em&gt;, tell the lower-level code that is producing the result to take whatever steps are required to cancel the operation.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Since older Twisted code is going to go ahead and fire that canceled deferred anyway, step #1 ensures our program won&amp;rsquo;t blow up if we cancel a deferred we got from an older library.&lt;/p&gt;
&lt;p&gt;This means we are always free to cancel a deferred, and we&amp;rsquo;ll be sure not to get the result if it hasn&amp;rsquo;t arrived (even if it arrives later). But canceling the deferred might not actually cancel the asynchronous operation. Aborting an asynchronous operation requires a context-specific action. You might need to close a network connection, roll back a database transaction, kill a sub-process, et cetera. And since a deferred is just a general-purpose callback organizer, how is it supposed to know what specific action to take when you cancel it? Or, alternatively, how could it forward the cancel request to the lower-level code that created and returned the deferred in the first place? Say it with me now:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I know, with a callback!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="canceling-deferreds-really"&gt;Canceling Deferreds, Really&lt;/h3&gt;
&lt;p&gt;Alright, take a look at &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/deferred-cancel/defer-cancel-5.py#L1"&gt;deferred-cancel/defer-cancel-5.py&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twisted.internet&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;canceller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;I need to cancel this deferred:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;callback got:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;errback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;errback got:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Deferred&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;canceller&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# created by lower-level code&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addCallbacks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# added by higher-level code&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;done&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This code is basically like the second example, except there is a third callback (&lt;code&gt;canceller&lt;/code&gt;) that&amp;rsquo;s passed to the &lt;code&gt;Deferred&lt;/code&gt; when we create it, rather than added afterwards. This callback is in charge of performing the context-specific actions required to abort the asynchronous operation (only if the deferred is actually canceled, of course). The &lt;code&gt;canceller&lt;/code&gt; callback is necessarily part of the lower-level code that returns the deferred, not the higher-level code that receives the deferred and adds its own callbacks and errbacks.&lt;/p&gt;
&lt;p&gt;Running the example produces this output:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;I need to cancel this deferred: &amp;lt;Deferred at 0xb7669d2cL&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;errback got: [Failure instance: Traceback (failure with no frames): &amp;lt;class &amp;#39;twisted.internet.defer.CancelledError&amp;#39;&amp;gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;done
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see, the &lt;code&gt;canceller&lt;/code&gt; callback is given the deferred whose result we no longer want. That&amp;rsquo;s where we would take whatever action we need to in order to abort the asynchronous operation. Notice that &lt;code&gt;canceller&lt;/code&gt; is invoked before the errback chain fires. In fact, we may choose to fire the deferred ourselves at this point with any result or error of our choice (and thus preempting the &lt;code&gt;CancelledError&lt;/code&gt; failure). Both possibilities are illustrated in &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/deferred-cancel/defer-cancel-6.py#L1"&gt;deferred-cancel/defer-cancel-6.py&lt;/a&gt; and &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/deferred-cancel/defer-cancel-7.py#L1"&gt;deferred-cancel/defer-cancel-7.py&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s do one more simple test before we fire up the reactor. We&amp;rsquo;ll create a deferred with a &lt;code&gt;canceller&lt;/code&gt; callback, fire it normally, and then cancel it. You can see the code in &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/deferred-cancel/defer-cancel-8.py#L1"&gt;deferred-cancel/defer-cancel-8.py&lt;/a&gt;. By examining the output of that script, you can see that canceling a deferred after it has been fired does &lt;em&gt;not&lt;/em&gt; invoke the &lt;code&gt;canceller&lt;/code&gt; callback. And that&amp;rsquo;s as we would expect since there&amp;rsquo;s nothing to cancel.&lt;/p&gt;
&lt;p&gt;The examples we&amp;rsquo;ve looked at so far haven&amp;rsquo;t had any actual asynchronous operations. Let&amp;rsquo;s make a simple program that invokes one asynchronous operation, then we&amp;rsquo;ll figure out how to make that operation cancellable. Consider the code in &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/deferred-cancel/defer-cancel-9.py#L1"&gt;deferred-cancel/defer-cancel-9.py&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twisted.internet.defer&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Deferred&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_poem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Sending poem&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Once upon a midnight dreary&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_poem&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Return a poem 5 seconds later.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twisted.internet&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;reactor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Deferred&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;reactor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callLater&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;send_poem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;got_poem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;poem&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;I got a poem:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;poem&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;poem_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;get_poem failed:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twisted.internet&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;reactor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;reactor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callLater&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reactor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# stop the reactor in 10 seconds&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_poem&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addCallbacks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;got_poem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;poem_error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;reactor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This example includes a &lt;code&gt;get_poem&lt;/code&gt; function that uses the reactor&amp;rsquo;s &lt;code&gt;callLater&lt;/code&gt; method to asynchronously return a poem five seconds after &lt;code&gt;get_poem&lt;/code&gt; is called. The &lt;code&gt;main&lt;/code&gt; function calls &lt;code&gt;get_poem&lt;/code&gt;, adds a callback/errback pair, and then starts up the reactor. We also arrange (again using &lt;code&gt;callLater&lt;/code&gt;) to stop the reactor in ten seconds. Normally we would do this by attaching a callback to the deferred, but you&amp;rsquo;ll see why we do it this way shortly.&lt;/p&gt;
&lt;p&gt;Running the example produces this output (after the appropriate delay):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Sending poem
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;I got a poem: Once upon a midnight dreary
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And after ten seconds our little program comes to a stop. Now let&amp;rsquo;s try canceling that deferred before the poem is sent. We&amp;rsquo;ll just add this bit of code to cancel the deferred after two seconds (well before the five second delay on the poem itself):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;reactor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callLater&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# cancel after 2 seconds&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The complete program is in &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/deferred-cancel/defer-cancel-10.py#L1"&gt;deferred-cancel/defer-cancel-10.py&lt;/a&gt;, which produces the following output:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;get_poem failed: [Failure instance: Traceback (failure with no frames): &amp;lt;class &amp;#39;twisted.internet.defer.CancelledError&amp;#39;&amp;gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Sending poem
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This example clearly illustrates that canceling a deferred does not necessarily cancel the underlying asynchronous request. After two seconds we see the output from our errback, printing out the &lt;code&gt;CancelledError&lt;/code&gt; as we would expect. But then after five seconds will still see the output from &lt;code&gt;send_poem&lt;/code&gt; (but the callback on the deferred doesn&amp;rsquo;t fire).&lt;/p&gt;
&lt;p&gt;At this point we&amp;rsquo;re just in the same situation as &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/deferred-cancel/defer-cancel-4.py#L1"&gt;deferred-cancel/defer-cancel-4.py&lt;/a&gt;. &amp;ldquo;Canceling&amp;rdquo; the deferred causes the eventual result to be ignored, but doesn&amp;rsquo;t abort the operation in any real sense. As we learned above, to make a truly cancelable deferred we must add a &lt;code&gt;cancel&lt;/code&gt; callback when the deferred is created.&lt;/p&gt;
&lt;p&gt;What does this new callback need to do? Take a look at the &lt;a href="https://github.com/twisted/twisted/tree/twisted-10.1.0/twisted/internet/interfaces.py#L556"&gt;documentation&lt;/a&gt; for the &lt;code&gt;callLater&lt;/code&gt; method. The return value of &lt;code&gt;callLater&lt;/code&gt; is another object, implementing &lt;code&gt;IDelayedCall&lt;/code&gt;, with a &lt;code&gt;cancel&lt;/code&gt; method we can use to prevent the delayed call from being executed.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s pretty simple, and the updated code is in &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/deferred-cancel/defer-cancel-11.py#L1"&gt;deferred-cancel/defer-cancel-11.py&lt;/a&gt;. The relevant changes are all in the &lt;code&gt;get_poem&lt;/code&gt; function:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_poem&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Return a poem 5 seconds later.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;canceler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# They don&amp;#39;t want the poem anymore, so cancel the delayed call&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;delayed_call&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# At this point we have three choices:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 1. Do nothing, and the deferred will fire the errback&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# chain with CancelledError.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 2. Fire the errback chain with a different error.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 3. Fire the callback chain with an alternative result.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Deferred&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;canceler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twisted.internet&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;reactor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;delayed_call&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reactor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callLater&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;send_poem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this new version, we save the return value from &lt;code&gt;callLater&lt;/code&gt; so we can use it in our cancel callback. The only thing our callback needs to do is invoke &lt;code&gt;delayed_call.cancel()&lt;/code&gt;. But as we discussed above, we could also choose to fire the deferred ourselves. The latest version of our example produces this output:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;get_poem failed: [Failure instance: Traceback (failure with no frames): &amp;lt;class &amp;#39;twisted.internet.defer.CancelledError&amp;#39;&amp;gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see, the deferred is canceled and the asynchronous operation has truly been aborted (i.e., we don&amp;rsquo;t see the &lt;code&gt;print&lt;/code&gt; output from &lt;code&gt;send_poem&lt;/code&gt;).&lt;/p&gt;
&lt;h3 id="poetry-proxy-30"&gt;Poetry Proxy 3.0&lt;/h3&gt;
&lt;p&gt;As we discussed in the Introduction, the poetry proxy server is a good candidate for implementing cancellation, as it allows us to abort the poem download if it turns out that nobody wants it (i.e., the client closes the connection before we send the poem). Version 3.0 of the proxy, located in &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-server-4/poetry-proxy.py#L1"&gt;twisted-server-4/poetry-proxy.py&lt;/a&gt;, implements deferred cancellation. The first change is in the &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-server-4/poetry-proxy.py#L52"&gt;&lt;code&gt;PoetryProxyProtocol&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PoetryProxyProtocol&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Protocol&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;connectionMade&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deferred&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_poem&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deferred&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deferred&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addBoth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loseConnection&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;connectionLost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deferred&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;deferred&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deferred&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deferred&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;deferred&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# cancel the deferred if it hasn&amp;#39;t fired&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You might compare it to the &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-server-2/poetry-proxy.py#L52"&gt;older version&lt;/a&gt;. The two main changes are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Save the deferred we get from &lt;code&gt;get_poem&lt;/code&gt; so we can cancel later if we need to.&lt;/li&gt;
&lt;li&gt;Cancel the deferred when the connection is closed. Note this also cancels the deferred after we actually get the poem, but as we discovered in the examples, canceling a deferred that has already fired has no effect.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now we need to make sure that canceling the deferred actually aborts the poem download. For that we need to change the &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-server-4/poetry-proxy.py#L105"&gt;&lt;code&gt;ProxyService&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProxyService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;poem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="c1"&gt;# the cached poem&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_poem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;poem&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Using cached poem.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# return an already-fired deferred&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;succeed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;poem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;canceler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Canceling poem download.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deferred&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;connector&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Fetching poem from server.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;deferred&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Deferred&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;canceler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;deferred&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_poem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;factory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PoetryClientFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deferred&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twisted.internet&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;reactor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;connector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reactor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connectTCP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deferred&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_poem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;poem&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;poem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;poem&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;poem&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Again, you may wish to compare this with the &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-server-2/poetry-proxy.py#100"&gt;older version&lt;/a&gt;. This class has a few more changes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We save the return value from &lt;code&gt;reactor.connectTCP&lt;/code&gt;, an &lt;a href="https://github.com/twisted/twisted/tree/twisted-10.1.0/twisted/internet/interfaces.py#L24"&gt;IConnector&lt;/a&gt; object. We can use the &lt;code&gt;disconnect&lt;/code&gt; method on that object to close the connection.&lt;/li&gt;
&lt;li&gt;We create the deferred with a &lt;code&gt;canceler&lt;/code&gt; callback. That callback is a closure which uses the &lt;code&gt;connector&lt;/code&gt; to close the connection. But first it sets the &lt;code&gt;factory.deferred&lt;/code&gt; attribute to &lt;code&gt;None&lt;/code&gt;. Otherwise, the factory might fire the deferred with a &amp;ldquo;connection closed&amp;rdquo; errback before the deferred itself fires with a &lt;code&gt;CancelledError&lt;/code&gt;. Since this deferred was canceled, having the deferred fire with &lt;code&gt;CancelledError&lt;/code&gt; seems more explicit.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You might also notice we now create the deferred in the &lt;code&gt;ProxyService&lt;/code&gt; instead of the &lt;code&gt;PoetryClientFactory&lt;/code&gt;. Since the canceler callback needs to access the &lt;code&gt;IConnector&lt;/code&gt; object, the &lt;code&gt;ProxyService&lt;/code&gt; ends up being the most convenient place to create the deferred.&lt;/p&gt;
&lt;p&gt;And, as in one of our earlier examples, our &lt;code&gt;canceler&lt;/code&gt; callback is implemented as a closure. Closures seem to be very useful when implementing cancel callbacks!&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s try out our new proxy. First start up a &lt;em&gt;slow&lt;/em&gt; server. It needs to be slow so we actually have time to cancel:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python blocking-server/slowpoetry.py --port &lt;span class="m"&gt;10001&lt;/span&gt; poetry/fascination.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we can start up our proxy (remember you need Twisted 10.1.0):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python twisted-server-4/poetry-proxy.py --port &lt;span class="m"&gt;10000&lt;/span&gt; &lt;span class="m"&gt;10001&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we can start downloading a poem from the proxy using any client, or even just curl:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl localhost:10000
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After a few seconds, press Ctrl-C to stop the client, or the curl process. In the terminal running the proxy you should see this output:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Fetching poem from server.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Canceling poem download.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And you should see the slow server has stopped printing output for each bit of poem it sends, since our proxy hung up. You can start and stop the client multiple times to verify each download is canceled each time. But if you let the poem run to completion, then the proxy caches the poem and sends it immediately after that.&lt;/p&gt;
&lt;h3 id="one-more-wrinkle"&gt;One More Wrinkle&lt;/h3&gt;
&lt;p&gt;We said several times above that canceling an already-fired deferred has no effect. Well, that&amp;rsquo;s not quite true. In &lt;a href="https://krondo.com/deferred-all-the-way-down/"&gt;Part 13&lt;/a&gt; we learned that the callbacks and errbacks attached to a deferred may return deferreds themselves. And in that case, the original (outer) deferred pauses the execution of its callback chains and waits for the inner deferred to fire (see &lt;a href="https://krondo.com/deferred-all-the-way-down/#figure28"&gt;Figure 28&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Thus, even though a deferred has fired the higher-level code that made the asynchronous request may not have received the result yet, because the callback chain is paused waiting for an inner deferred to finish. So what happens if the higher-level code cancels that outer deferred? In that case the outer deferred does not cancel itself (it has already fired after all); instead, the outer deferred cancels the inner deferred.&lt;/p&gt;
&lt;p&gt;So when you cancel a deferred, you might not be canceling the main asynchronous operation, but rather some other asynchronous operation triggered as a result of the first. Whew!&lt;/p&gt;
&lt;p&gt;We can illustrate this with one more example. Consider the code in &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/deferred-cancel/defer-cancel-12.py#L1"&gt;deferred-cancel/defer-cancel-12.py&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twisted.internet&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cancel_outer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;outer cancel callback.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cancel_inner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;inner cancel callback.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;first_outer_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;first outer callback, returning inner deferred&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;inner_d&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;second_outer_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;second outer callback got:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;outer_errback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;outer errback got:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;outer_d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Deferred&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cancel_outer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;inner_d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Deferred&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cancel_inner&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;outer_d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first_outer_callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;outer_d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addCallbacks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;second_outer_callback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;outer_errback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;outer_d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;result&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# at this point the outer deferred has fired, but is paused&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# on the inner deferred.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;canceling outer deferred.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;outer_d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;done&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this example we create two deferreds, the outer and the inner, and have one of the outer callbacks return the inner deferred. First we fire the outer deferred, and then we cancel it. The example produces this output:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;first outer callback, returning inner deferred
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;canceling outer deferred.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;inner cancel callback.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;outer errback got: [Failure instance: Traceback (failure with no frames): &amp;lt;class &amp;#39;twisted.internet.defer.CancelledError&amp;#39;&amp;gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;done
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see, canceling the outer deferred does not cause the outer cancel callback to fire. Instead, it cancels the inner deferred so the inner cancel callback fires, and then outer errback receives the &lt;code&gt;CancelledError&lt;/code&gt; (from the inner deferred).&lt;/p&gt;
&lt;p&gt;You may wish to stare at that code a while, and try out variations to see how they affect the outcome.&lt;/p&gt;
&lt;h3 id="discussion"&gt;Discussion&lt;/h3&gt;
&lt;p&gt;Canceling a deferred can be a very useful operation, allowing our programs to avoid work they no longer need to do. And as we have seen, it can be a little bit tricky, too.&lt;/p&gt;
&lt;p&gt;One very important fact to keep in mind is that canceling a deferred doesn&amp;rsquo;t necessarily cancel the underlying asynchronous operation. In fact, as of this writing, most deferreds won&amp;rsquo;t really &amp;ldquo;cancel&amp;rdquo;, since most Twisted code was written prior to Twisted 10.1.0 and hasn&amp;rsquo;t been updated. This includes many of the APIs in Twisted itself! Check the documentation and/or the source code to find out whether canceling the deferred will truly cancel the request, or simply ignore it.&lt;/p&gt;
&lt;p&gt;And the second important fact is that simply returning a deferred from your asynchronous APIs will not necessarily make them cancelable in the complete sense of the word. If you want to implement canceling in your own programs, you should study the Twisted source code to find more examples. Cancellation is a brand new feature so the patterns and best practices are still being worked out.&lt;/p&gt;
&lt;h3 id="looking-ahead"&gt;Looking Ahead&lt;/h3&gt;
&lt;p&gt;At this point we&amp;rsquo;ve learned just about everything about Deferreds and the core concepts behind Twisted. Which means there&amp;rsquo;s not much more to introduce, as the rest of Twisted consists mainly of specific applications, like web programming or asynchronous database access. So in the &lt;a href="https://krondo.com/twisted-and-erlang/"&gt;next&lt;/a&gt; couple of Parts we&amp;rsquo;re going to take a little detour and look at two other systems that use asynchronous I/O to see how some of their ideas relate to the ideas in Twisted. Then, in the final Part, we will wrap up and suggest ways to continue your Twisted education.&lt;/p&gt;
&lt;h3 id="suggested-exercises"&gt;Suggested Exercises&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Did you know you can spell canceled with one or two els? &lt;a href="http://mw4.m-w.com/dictionary/canceled"&gt;It&amp;rsquo;s true&lt;/a&gt;. It all depends on what sort of mood you&amp;rsquo;re in.&lt;/li&gt;
&lt;li&gt;Peruse the source code of the &lt;a href="https://github.com/twisted/twisted/tree/twisted-10.1.0/twisted/internet/defer.py#L167"&gt;&lt;code&gt;Deferred&lt;/code&gt;&lt;/a&gt; class, paying special attention to the implementation of cancellation.&lt;/li&gt;
&lt;li&gt;Search the Twisted 10.10 &lt;a href="https://github.com/twisted/twisted/tree/twisted-10.1.0/"&gt;source code&lt;/a&gt; for examples of deferreds with cancel callbacks. Study their implementation.&lt;/li&gt;
&lt;li&gt;Make the deferred returned by the &lt;code&gt;get_poetry&lt;/code&gt; method of one of our poetry clients cancelable.&lt;/li&gt;
&lt;li&gt;Make a reactor-based example that illustrates canceling an outer deferred which is paused on an inner deferred. If you use &lt;code&gt;callLater&lt;/code&gt; you will need to choose the delays carefully to ensure the outer deferred is canceled at the right moment.&lt;/li&gt;
&lt;li&gt;Find an asynchronous API in Twisted that doesn&amp;rsquo;t support a true cancel and implement cancellation for it. &lt;a href="http://twistedmatrix.com/trac/wiki/BasicGuideToContributingCode"&gt;Submit a patch&lt;/a&gt; to the Twisted project. Don&amp;rsquo;t forget unit tests!&lt;/li&gt;
&lt;/ol&gt;</content:encoded></item><item><title>Deferreds En Masse</title><link>https://krondo.com/deferreds-en-masse/</link><pubDate>Sun, 22 Aug 2010 21:17:01 +0000</pubDate><guid isPermaLink="true">https://krondo.com/deferreds-en-masse/</guid><category>Blather</category><category>Programming</category><category>Python</category><category>Software</category><description>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 want to run a group of asynchronous operations in “parallel”. Since Twisted is single-threaded they won’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.</description><content:encoded>&lt;h2 id="part-18-deferreds-en-masse"&gt;Part 18: Deferreds En Masse&lt;/h2&gt;
&lt;p&gt;This continues the introduction started &lt;a href="https://krondo.com/in-which-we-begin-at-the-beginning/"&gt;here&lt;/a&gt;. You can find an index to the entire series &lt;a href="https://krondo.com/an-introduction-to-asynchronous-programming-and-twisted/"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;Sometimes, though, we want to run a group of asynchronous operations in &amp;ldquo;parallel&amp;rdquo;. Since Twisted is single-threaded they won&amp;rsquo;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.&lt;/p&gt;
&lt;p&gt;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 &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-client-7/get-poetry.py#L160"&gt;&lt;code&gt;results&lt;/code&gt;&lt;/a&gt; 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&amp;rsquo;s still work left to do.&lt;/p&gt;
&lt;p&gt;As you might expect, Twisted includes an abstraction you can use to solve this problem and we&amp;rsquo;re going to take a look at it today.&lt;/p&gt;
&lt;h3 id="the-deferredlist"&gt;The DeferredList&lt;/h3&gt;
&lt;p&gt;The &lt;a href="https://github.com/twisted/twisted/tree/twisted-10.1.0/twisted/internet/defer.py#593"&gt;&lt;code&gt;DeferredList&lt;/code&gt;&lt;/a&gt; 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&amp;rsquo;s look at some examples.&lt;/p&gt;
&lt;p&gt;In &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/deferred-list/deferred-list-1.py#L1"&gt;deferred-list/deferred-list-1.py&lt;/a&gt; you will find this code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twisted.internet&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;got_results&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;We got:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Empty List.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeferredList&lt;/span&gt;&lt;span class="p"&gt;([])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Adding Callback.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;got_results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And if you run it, you will get this output:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Empty List.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Adding Callback.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;We got: []
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Some things to notice:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;DeferredList&lt;/code&gt; is created from a Python &lt;code&gt;list&lt;/code&gt;. In this case the list is empty, but we&amp;rsquo;ll soon see that the list elements must all be &lt;code&gt;Deferred&lt;/code&gt; objects.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;DeferredList&lt;/code&gt; is itself a deferred (it inherits from &lt;code&gt;Deferred&lt;/code&gt;). That means you can add callbacks and errbacks to it just like you would a regular deferred.&lt;/li&gt;
&lt;li&gt;In the example above, our callback was fired as soon as we added it, so the &lt;code&gt;DeferredList&lt;/code&gt; must have fired right away. We&amp;rsquo;ll discuss that more in a second.&lt;/li&gt;
&lt;li&gt;The result of the deferred list was itself a list (empty).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now look at &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/deferred-list/deferred-list-2.py#L1"&gt;deferred-list/deferred-list-2.py&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twisted.internet&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;got_results&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;We got:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;One Deferred.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Deferred&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeferredList&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;d1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Adding Callback.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;got_results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Firing d1.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;d1 result&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we are creating our &lt;code&gt;DeferredList&lt;/code&gt; with a 1-element list containing a single deferred. Here&amp;rsquo;s the output we get:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;One Deferred.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Adding Callback.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Firing d1.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;We got: [(True, &amp;#39;d1 result&amp;#39;)]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;More things to notice:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This time the &lt;code&gt;DeferredList&lt;/code&gt; didn&amp;rsquo;t fire its callback until we fired the deferred in the list.&lt;/li&gt;
&lt;li&gt;The result is still a list, but now it has one element.&lt;/li&gt;
&lt;li&gt;The element is a tuple whose second value is the result of the deferred in the list.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;rsquo;s try putting two deferreds in the list (&lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/deferred-list/deferred-list-1.py#L3"&gt;deferred-list/deferred-list-3.py&lt;/a&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twisted.internet&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;got_results&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;We got:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Two Deferreds.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Deferred&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Deferred&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeferredList&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;d1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Adding Callback.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;got_results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Firing d1.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;d1 result&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Firing d2.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;d2 result&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And here&amp;rsquo;s the output:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Two Deferreds.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Adding Callback.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Firing d1.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Firing d2.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;We got: [(True, &amp;#39;d1 result&amp;#39;), (True, &amp;#39;d2 result&amp;#39;)]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At this point it&amp;rsquo;s pretty clear the result of a &lt;code&gt;DeferredList&lt;/code&gt;, at least for the way we&amp;rsquo;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 &lt;code&gt;DeferredList&lt;/code&gt; itself doesn&amp;rsquo;t fire until all the deferreds in the original list have fired. And a &lt;code&gt;DeferredList&lt;/code&gt; created with an empty list fires right away since there aren&amp;rsquo;t any deferreds to wait for.&lt;/p&gt;
&lt;p&gt;What about the order of the results in the final list? Consider &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/deferred-list/deferred-list-4.py#L1"&gt;deferred-list/deferred-list-4.py&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twisted.internet&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;got_results&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;We got:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Two Deferreds.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Deferred&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Deferred&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeferredList&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;d1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Adding Callback.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;got_results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Firing d2.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;d2 result&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Firing d1.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;d1 result&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we are firing &lt;code&gt;d2&lt;/code&gt; first and then &lt;code&gt;d1&lt;/code&gt;. Note the deferred list is still constructed with &lt;code&gt;d1&lt;/code&gt; and &lt;code&gt;d2&lt;/code&gt; in their original order. Here&amp;rsquo;s the output:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Two Deferreds.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Adding Callback.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Firing d2.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Firing d1.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;We got: [(True, &amp;#39;d1 result&amp;#39;), (True, &amp;#39;d2 result&amp;#39;)]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;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).&lt;/p&gt;
&lt;p&gt;Alright, what happens if one or more of the deferreds in the list fails? And what are those &lt;code&gt;True&lt;/code&gt; values doing there? Let&amp;rsquo;s try the example in &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/deferred-list/deferred-list-5.py#L1"&gt;deferred-list/deferred-list-5.py&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twisted.internet&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;got_results&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;We got:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Deferred&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Deferred&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeferredList&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;d1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;consumeErrors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;got_results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Firing d1.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;d1 result&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Firing d2 with errback.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;d2 failure&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we are firing &lt;code&gt;d1&lt;/code&gt; with a normal result and &lt;code&gt;d2&lt;/code&gt; with an error. Ignore the &lt;code&gt;consumeErrors&lt;/code&gt; option for now, we&amp;rsquo;ll get back to it. Here&amp;rsquo;s the output:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Firing d1.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Firing d2 with errback.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;We got: [(True, &amp;#39;d1 result&amp;#39;), (False, &amp;lt;twisted.python.failure.Failure &amp;lt;type &amp;#39;exceptions.Exception&amp;#39;&amp;gt;&amp;gt;)]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now the tuple corresponding to &lt;code&gt;d2&lt;/code&gt; has a &lt;code&gt;Failure&lt;/code&gt; in slot two, and &lt;code&gt;False&lt;/code&gt; in slot one. At this point it should be pretty clear how a &lt;code&gt;DeferredList&lt;/code&gt; works (but see the Discussion below):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;DeferredList&lt;/code&gt; is constructed with a list of deferred objects.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;DeferredList&lt;/code&gt; is itself a deferred whose result is a list of the same length as the list of deferreds.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;DeferredList&lt;/code&gt; fires after all the deferreds in the original list have fired.&lt;/li&gt;
&lt;li&gt;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 &lt;code&gt;(True, result)&lt;/code&gt; and if the deferred failed, the element is &lt;code&gt;(False, failure)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;DeferredList&lt;/code&gt; never fails, since the result of each individual deferred is collected into the list no matter what (but again, see the Discussion below).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now let&amp;rsquo;s talk about that &lt;code&gt;consumeErrors&lt;/code&gt; option we passed to the &lt;code&gt;DeferredList&lt;/code&gt;. If we run the same code but without passing the option (&lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/deferred-list/deferred-list-6.py#L1"&gt;deferred-list/deferred-list-6.py&lt;/a&gt;), we get this output:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Firing d1.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Firing d2 with errback.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;We got: [(True, &amp;#39;d1 result&amp;#39;), (False, &amp;gt;twisted.python.failure.Failure &amp;gt;type &amp;#39;exceptions.Exception&amp;#39;&amp;lt;&amp;lt;)]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Unhandled error in Deferred:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Traceback (most recent call last):
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Failure: exceptions.Exception: d2 failure
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you recall, the &amp;ldquo;Unhandled error in Deferred&amp;rdquo; message is generated when a deferred is garbage collected and the last callback in that deferred failed. The message is telling us we haven&amp;rsquo;t caught all the potential asynchronous failures in our program. So where is it coming from in our example? It&amp;rsquo;s clearly not coming from the &lt;code&gt;DeferredList&lt;/code&gt;, since that succeeds. So it must be coming from &lt;code&gt;d2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;DeferredList&lt;/code&gt; needs to know when each deferred it is monitoring fires. And the &lt;code&gt;DeferredList&lt;/code&gt; does that in the usual way — 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, &lt;code&gt;d2&lt;/code&gt; remains in the failed state after it fires.&lt;/p&gt;
&lt;p&gt;But if we pass &lt;code&gt;consumeErrors=True&lt;/code&gt; to the &lt;code&gt;DeferredList&lt;/code&gt;, the errback added by the &lt;code&gt;DeferredList&lt;/code&gt; to each deferred will instead return &lt;code&gt;None&lt;/code&gt;, thus &amp;ldquo;consuming&amp;rdquo; the error and eliminating the warning message. We could also handle the error by adding our own errback to &lt;code&gt;d2&lt;/code&gt;, as in &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/deferred-list/deferred-list-7.py#L1"&gt;deferred-list/deferred-list-7.py&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="client-80"&gt;Client 8.0&lt;/h3&gt;
&lt;p&gt;Version 8.0 of our Get Poetry Now! client uses a &lt;code&gt;DeferredList&lt;/code&gt; to find out when all the poetry has finished (or failed). You can find the new client in &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-client-8/get-poetry.py#L1"&gt;twisted-client-8/get-poetry.py&lt;/a&gt;. Once again the only change is in &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-client-8/get-poetry.py#L151"&gt;&lt;code&gt;poetry_main&lt;/code&gt;&lt;/a&gt;. Let&amp;rsquo;s look at the important changes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;addresses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_transformed_poem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addCallbacks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;got_poem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;dlist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeferredList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;consumeErrors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;dlist&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;reactor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You may wish to compare it to the same section of &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-client-7/get-poetry.py#L180"&gt;&lt;code&gt;client 7.0&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In client 8.0, we don&amp;rsquo;t need the &lt;code&gt;poem_done&lt;/code&gt; callback or the &lt;code&gt;results&lt;/code&gt; list. Instead, we put each deferred we get back from &lt;code&gt;get_transformed_poem&lt;/code&gt; into a list (&lt;code&gt;ds&lt;/code&gt;) and then create a &lt;code&gt;DeferredList&lt;/code&gt;. Since the &lt;code&gt;DeferredList&lt;/code&gt; won&amp;rsquo;t fire until all the poems have finished or failed, we just add a callback to the &lt;code&gt;DeferredList&lt;/code&gt; to shutdown the reactor. In this case, we aren&amp;rsquo;t using the result from the &lt;code&gt;DeferredList&lt;/code&gt;, we just need to know when everything is finished. And that&amp;rsquo;s it!&lt;/p&gt;
&lt;h3 id="discussion"&gt;Discussion&lt;/h3&gt;
&lt;p&gt;We can visualize how a &lt;code&gt;DeferredList&lt;/code&gt; works in Figure 37:&lt;/p&gt;
&lt;figure id="figure37" class="wp-caption alignnone"&gt;
&lt;a href="https://krondo.com/uploads/2010/08/deferred-list.png"&gt;&lt;img src="https://krondo.com/deferreds-en-masse/images/deferred-list.png" alt="Figure 37: the result of a DeferredList" width="605" height="340" loading="lazy" decoding="async"&gt;&lt;/a&gt;
&lt;figcaption&gt;Figure 37: the result of a DeferredList&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Pretty simple, really. There are a couple options to &lt;code&gt;DeferredList&lt;/code&gt; we haven&amp;rsquo;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.&lt;/p&gt;
&lt;p&gt;In the &lt;a href="https://krondo.com/i-thought-i-wanted-it-but-i-changed-my-mind/"&gt;next Part&lt;/a&gt; we will cover one more feature of the &lt;code&gt;Deferred&lt;/code&gt; class, a feature recently introduced in Twisted 10.1.0.&lt;/p&gt;
&lt;h3 id="suggested-exercises"&gt;Suggested Exercises&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Read the &lt;a href="https://github.com/twisted/twisted/tree/twisted-10.1.0/twisted/internet/defer.py#5933"&gt;source code&lt;/a&gt; for the &lt;code&gt;DeferredList&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Modify the examples in deferred-list to experiment with the optional constructor arguments &lt;code&gt;fireOnOneCallback&lt;/code&gt; and &lt;code&gt;fireOnOneErrback&lt;/code&gt;. Come up with scenarios where you would use one or the other (or both).&lt;/li&gt;
&lt;li&gt;Can you create a &lt;code&gt;DeferredList&lt;/code&gt; using a list of &lt;code&gt;DeferredList&lt;/code&gt;s? If so, what would the result look like?&lt;/li&gt;
&lt;li&gt;Modify client 8.0 so that it doesn&amp;rsquo;t print out anything until all the poems have finished downloading. This time you will use the result from the &lt;code&gt;DeferredList&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Define the semantics of a &lt;code&gt;DeferredDict&lt;/code&gt; and then implement it.&lt;/li&gt;
&lt;/ol&gt;</content:encoded></item><item><title>Just Another Way to Spell "Callback"</title><link>https://krondo.com/just-another-way-to-spell-callback/</link><pubDate>Sun, 15 Aug 2010 17:00:43 +0000</pubDate><guid isPermaLink="true">https://krondo.com/just-another-way-to-spell-callback/</guid><category>Blather</category><category>Programming</category><category>Python</category><category>Software</category><description>Part 17: Just Another Way to Spell “Callback” This continues the introduction started here. You can find an index to the entire series here.
Introduction In this Part we’re going to return to the subject of callbacks. We’ll introduce another technique for writing callbacks in Twisted that uses generators. We’ll show how the technique works and contrast it with using “pure” Deferreds. Finally we’ll rewrite one of our poetry clients using this technique. But first let’s review how generators work so we can see why they are a candidate for creating callbacks.</description><content:encoded>&lt;h2 id="part-17-just-another-way-to-spell-callback"&gt;Part 17: Just Another Way to Spell &amp;ldquo;Callback&amp;rdquo;&lt;/h2&gt;
&lt;p&gt;This continues the introduction started &lt;a href="https://krondo.com/in-which-we-begin-at-the-beginning/"&gt;here&lt;/a&gt;. You can find an index to the entire series &lt;a href="https://krondo.com/an-introduction-to-asynchronous-programming-and-twisted/"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;
&lt;p&gt;In this Part we&amp;rsquo;re going to return to the subject of callbacks. We&amp;rsquo;ll introduce another technique for writing callbacks in Twisted that uses &lt;a href="http://docs.python.org/tutorial/classes.html#generators"&gt;generators&lt;/a&gt;. We&amp;rsquo;ll show how the technique works and contrast it with using &amp;ldquo;pure&amp;rdquo; Deferreds. Finally we&amp;rsquo;ll rewrite one of our poetry clients using this technique. But first let&amp;rsquo;s review how generators work so we can see why they are a candidate for creating callbacks.&lt;/p&gt;
&lt;h4 id="a-brief-review-of-generators"&gt;A Brief Review of Generators&lt;/h4&gt;
&lt;p&gt;As you probably know, a Python generator is a &amp;ldquo;restartable function&amp;rdquo; that you create by using the &lt;code&gt;yield&lt;/code&gt; expression in the body of your function. By doing so, the function becomes a &amp;ldquo;generator function&amp;rdquo; that returns an &lt;a href="http://docs.python.org/tutorial/classes.html#iterators"&gt;iterator&lt;/a&gt; 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 &lt;code&gt;yield&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Generators (and iterators) are often used to represent lazily-created sequences of values. Take a look at the example code in &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/inline-callbacks/gen-1.py#L1"&gt;inline-callbacks/gen-1.py&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_generator&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;starting up&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;workin&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;still workin&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;done&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;my_generator&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here we have a generator that creates the sequence 1, 2, 3. If you run the code, you will see the &lt;code&gt;print&lt;/code&gt; statements in the generator interleaved with the &lt;code&gt;print&lt;/code&gt; statement in the &lt;code&gt;for&lt;/code&gt; loop as the loop cycles through the generator.&lt;/p&gt;
&lt;p&gt;We can make this code more explicit by creating the generator ourselves (&lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/inline-callbacks/gen-2.py#L1"&gt;inline-callbacks/gen-2.py&lt;/a&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_generator&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;starting up&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;workin&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;still workin&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;done&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;gen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;my_generator&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gen&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;StopIteration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;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:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The generator function doesn&amp;rsquo;t start running until &amp;ldquo;called&amp;rdquo; by the loop (using the &lt;code&gt;next&lt;/code&gt; method).&lt;/li&gt;
&lt;li&gt;Once the generator is running, it keeps running until it &amp;ldquo;returns&amp;rdquo; to the loop (using &lt;code&gt;yield&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;When the loop is running other code (like the &lt;code&gt;print&lt;/code&gt; statement), the generator is not running.&lt;/li&gt;
&lt;li&gt;When the generator is running, the loop is not running (it&amp;rsquo;s &amp;ldquo;blocked&amp;rdquo; waiting for the generator).&lt;/li&gt;
&lt;li&gt;Once a generator &lt;code&gt;yield&lt;/code&gt;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.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is very much like the way callbacks work in an asynchronous system. We can think of the &lt;code&gt;while&lt;/code&gt; loop as the reactor, and the generator as a series of callbacks separated by &lt;code&gt;yield&lt;/code&gt; 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.&lt;/p&gt;
&lt;p&gt;Furthermore, you can have multiple generators active at once (see the example in &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/inline-callbacks/gen-3.py#L1"&gt;inline-callbacks/gen-3.py&lt;/a&gt;), with their &amp;ldquo;callbacks&amp;rdquo; interleaved with each other, just as you can have independent asynchronous tasks running in a system like Twisted.&lt;/p&gt;
&lt;p&gt;Something is still missing, though. Callbacks aren&amp;rsquo;t just called by the reactor, they also receive information. When part of a deferred&amp;rsquo;s chain, a callback either receives a result, in the form of a single Python value, or an error, in the form of a &lt;code&gt;Failure&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;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 &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/inline-callbacks/gen-4.py#L1"&gt;inline-callbacks/gen-4.py&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Malfunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_generator&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;starting up&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;got:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;got:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;Malfunction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;malfunction!&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;done&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;gen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;my_generator&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;gen&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# start the generator&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;gen&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# send the value 10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;gen&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# send the value 20&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;gen&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;throw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Malfunction&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="c1"&gt;# raise an exception inside the generator&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;gen&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;StopIteration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In Python 2.5 and later versions, the &lt;code&gt;yield&lt;/code&gt; statement is an expression that evaluates to a value. And the code that restarts the generator can determine that value using the &lt;code&gt;send&lt;/code&gt; method instead of &lt;code&gt;next&lt;/code&gt; (if you use &lt;code&gt;next&lt;/code&gt; the value is &lt;code&gt;None&lt;/code&gt;). What&amp;rsquo;s more, you can actually raise an arbitrary exception &lt;em&gt;inside&lt;/em&gt; the generator using the &lt;code&gt;throw&lt;/code&gt; method. How cool is that?&lt;/p&gt;
&lt;h3 id="inline-callbacks"&gt;Inline Callbacks&lt;/h3&gt;
&lt;p&gt;Given what we just reviewed about &lt;code&gt;send&lt;/code&gt;ing and &lt;code&gt;throw&lt;/code&gt;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 &lt;code&gt;yield&lt;/code&gt;s and the value of each &lt;code&gt;yield&lt;/code&gt; expression is the result for the next callback (or the &lt;code&gt;yield&lt;/code&gt; raises an exception and that&amp;rsquo;s the failure). Figure 35 shows the correspondence:&lt;/p&gt;
&lt;figure id="figure35" class="wp-caption aligncenter"&gt;
&lt;a href="https://krondo.com/uploads/2010/07/generator-callbacks1.png"&gt;&lt;img src="https://krondo.com/just-another-way-to-spell-callback/images/generator-callbacks1.png" alt="Figure 35: generator as a callback sequence" width="428" height="235" loading="lazy" decoding="async"&gt;&lt;/a&gt;
&lt;figcaption&gt;Figure 35: generator as a callback sequence&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Now when a series of callbacks is chained together in a deferred, each callback receives the result from the one prior. That&amp;rsquo;s easy enough to do with a generator — just &lt;code&gt;send&lt;/code&gt; the value you got from the previous run of the generator (the value it &lt;code&gt;yield&lt;/code&gt;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&amp;rsquo;s needed. So what&amp;rsquo;s the point?&lt;/p&gt;
&lt;p&gt;Recall the fact we learned in &lt;a href="https://krondo.com/deferred-all-the-way-down/"&gt;Part 13&lt;/a&gt;, 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&amp;rsquo;s chain is called with the result (or failure) from the inner deferred.&lt;/p&gt;
&lt;p&gt;So imagine that our generator &lt;code&gt;yield&lt;/code&gt;s a deferred object instead of an ordinary Python value. The generator is now &amp;ldquo;paused&amp;rdquo;, and that&amp;rsquo;s automatic; generators always pause after every &lt;code&gt;yield&lt;/code&gt; statement until they are explicitly restarted. So we can delay restarting the generator until the deferred fires, at which point we either &lt;code&gt;send&lt;/code&gt; the value (if the deferred succeeds) or &lt;code&gt;throw&lt;/code&gt; the exception (if the deferred fails). That would make our generator a genuine sequence of asynchronous callbacks and that&amp;rsquo;s the idea behind the &lt;a href="https://github.com/twisted/twisted/tree/twisted-10.1.0/twisted/internet/defer.py#L973"&gt;&lt;code&gt;inlineCallbacks&lt;/code&gt;&lt;/a&gt; function in &lt;a href="https://github.com/twisted/twisted/tree/twisted-10.1.0/twisted/internet/defer.py"&gt;&lt;code&gt;twisted.internet.defer&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id="inlinecallbacks"&gt;inlineCallbacks&lt;/h4&gt;
&lt;p&gt;Consider the example program in &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/inline-callbacks/inline-callbacks-1.py#L1"&gt;inline-callbacks/inline-callbacks-1.py&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twisted.internet.defer&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;inlineCallbacks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Deferred&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@inlineCallbacks&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_callbacks&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twisted.internet&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;reactor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;first callback&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;# yielded values that aren&amp;#39;t deferred come right back&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;second callback got&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Deferred&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;reactor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callLater&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="c1"&gt;# yielded deferreds will pause the generator&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;third callback got&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="c1"&gt;# the result of the deferred&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Deferred&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;reactor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callLater&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fourth callback got&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# the exception from the deferred&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;reactor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twisted.internet&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;reactor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;reactor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callWhenRunning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_callbacks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;reactor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;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 &lt;code&gt;inlineCallbacks&lt;/code&gt; function. First, &lt;code&gt;inlineCallbacks&lt;/code&gt; is a decorator and it always decorates generator functions, i.e., functions that use &lt;code&gt;yield&lt;/code&gt;. The whole purpose of &lt;code&gt;inlineCallbacks&lt;/code&gt; is turn a generator into a series of asynchronous callbacks according to the scheme we outlined before.&lt;/p&gt;
&lt;p&gt;Second, when we invoke an &lt;code&gt;inlineCallbacks&lt;/code&gt;-decorated function, we don&amp;rsquo;t need to call &lt;code&gt;next&lt;/code&gt; or &lt;code&gt;send&lt;/code&gt; or &lt;code&gt;throw&lt;/code&gt; ourselves. The decorator takes care of those details for us and ensures the generator will run to the end (assuming it doesn&amp;rsquo;t raise an exception).&lt;/p&gt;
&lt;p&gt;Third, if we &lt;code&gt;yield&lt;/code&gt; a non-deferred value from the generator, it is immediately restarted with that same value as the result of the &lt;code&gt;yield&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And finally, if we &lt;code&gt;yield&lt;/code&gt; a deferred from the generator, it will not be restarted until that deferred fires. If the deferred succeeds, the result of the &lt;code&gt;yield&lt;/code&gt; is just the result from the deferred. And if the deferred fails, the &lt;code&gt;yield&lt;/code&gt; statement raises the exception. Note the exception is just an ordinary &lt;code&gt;Exception&lt;/code&gt; object, rather than a &lt;code&gt;Failure&lt;/code&gt;, and we can catch it with a &lt;code&gt;try&lt;/code&gt;/&lt;code&gt;except&lt;/code&gt; statement around the &lt;code&gt;yield&lt;/code&gt; expression.&lt;/p&gt;
&lt;p&gt;In the example we are just using &lt;code&gt;callLater&lt;/code&gt; to fire the deferreds after a short period of time. While that&amp;rsquo;s a handy way to put in a non-blocking delay into our callback chain, normally we would be &lt;code&gt;yield&lt;/code&gt;ing a deferred returned by some other asynchronous operation (i.e., &lt;code&gt;get_poetry&lt;/code&gt;) invoked from our generator.&lt;/p&gt;
&lt;p&gt;Ok, now we know how an &lt;code&gt;inlineCallbacks&lt;/code&gt;-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&amp;rsquo;t know exactly when that generator will stop running (it might &lt;code&gt;yield&lt;/code&gt; 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&amp;rsquo;t one of the deferreds the generator may &lt;code&gt;yield&lt;/code&gt;. Rather, it&amp;rsquo;s a deferred that fires only after the generator has completely finished (or throws an exception).&lt;/p&gt;
&lt;p&gt;If the generator throws an exception, the returned deferred will fire its errback chain with that exception wrapped in a &lt;code&gt;Failure&lt;/code&gt;. But if we want the generator to return a normal value, we must &amp;ldquo;return&amp;rdquo; it using the &lt;code&gt;defer.returnValue&lt;/code&gt; function. Like the ordinary &lt;code&gt;return&lt;/code&gt; statement, it will also stop the generator (it actually raises a special exception). The &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/inline-callbacks/inline-callbacks-2.py#L1"&gt;inline-callbacks/inline-callbacks-2.py&lt;/a&gt; example illustrates both possibilities.&lt;/p&gt;
&lt;h3 id="client-70"&gt;Client 7.0&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s put &lt;code&gt;inlineCallbacks&lt;/code&gt; to work with a new version of our poetry client. You can see the code in &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-client-7/get-poetry.py#L1"&gt;twisted-client-7/get-poetry.py&lt;/a&gt;. You may wish to compare it to client 6.0 in &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-client-6/get-poetry.py#L151"&gt;twisted-client-6/get-poetry.py&lt;/a&gt;. The relevant changes are in &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-client-7/get-poetry.py#L151"&gt;&lt;code&gt;poetry_main&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;poetry_main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;addresses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;xform_addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;addresses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TransformProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;xform_addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twisted.internet&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;reactor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nd"&gt;@defer.inlineCallbacks&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_transformed_poem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;poem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;get_poetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;The poem download failed:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;poem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cummingsify&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;poem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Cummingsify failed!&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;defer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;returnValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;poem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;got_poem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;poem&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;poem&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;poem_done&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addresses&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;reactor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;addresses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_transformed_poem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addCallbacks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;got_poem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addBoth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;poem_done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;reactor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In our new version the &lt;code&gt;inlineCallbacks&lt;/code&gt; generator function &lt;code&gt;get_transformed_poem&lt;/code&gt; 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 &lt;code&gt;try&lt;/code&gt;/&lt;code&gt;except&lt;/code&gt; statements to handle asynchronous errors inside the generator.&lt;/p&gt;
&lt;p&gt;We can test the new client out in the same way as before. First start up a transform server:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python twisted-server-1/transformedpoetry.py --port &lt;span class="m"&gt;10001&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then start a couple of poetry servers:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python twisted-server-1/fastpoetry.py --port &lt;span class="m"&gt;10002&lt;/span&gt; poetry/fascination.txt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python twisted-server-1/fastpoetry.py --port &lt;span class="m"&gt;10003&lt;/span&gt; poetry/science.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you can run the new client:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python twisted-client-7/get-poetry.py &lt;span class="m"&gt;10001&lt;/span&gt; &lt;span class="m"&gt;10002&lt;/span&gt; &lt;span class="m"&gt;10003&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Try turning off one or more of the servers to see how the client handles errors.&lt;/p&gt;
&lt;h3 id="discussion"&gt;Discussion&lt;/h3&gt;
&lt;p&gt;Like the &lt;code&gt;Deferred&lt;/code&gt; object, the &lt;code&gt;inlineCallbacks&lt;/code&gt; function gives us a new way of organizing our asynchronous callbacks. And, as with deferreds, &lt;code&gt;inlineCallbacks&lt;/code&gt; doesn&amp;rsquo;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 &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/inline-callbacks/inline-callbacks-tb.py#L1"&gt;inline-callbacks/inline-callbacks-tb.py&lt;/a&gt;. Run that code and you will get a traceback with &lt;code&gt;reactor.run()&lt;/code&gt; at the top, lots of helper functions in between, and our callback at the bottom.&lt;/p&gt;
&lt;p&gt;We can adapt Figure 29, which explains what happens when one callback in a deferred returns another deferred, to show what happens when an &lt;code&gt;inlineCallbacks&lt;/code&gt; generator &lt;code&gt;yield&lt;/code&gt;s a deferred. See Figure 36:&lt;/p&gt;
&lt;figure id="figure36" class="wp-caption alignnone"&gt;
&lt;a href="https://krondo.com/uploads/2010/07/inline-callbacks1.png"&gt;&lt;img src="https://krondo.com/just-another-way-to-spell-callback/images/inline-callbacks1.png" alt="Figure 36: flow control in an inlineCallbacks function" width="629" height="582" loading="lazy" decoding="async"&gt;&lt;/a&gt;
&lt;figcaption&gt;Figure 36: flow control in an inlineCallbacks function&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The same figure works in both cases because the idea being illustrated is the same — one asynchronous operation is waiting for another.&lt;/p&gt;
&lt;p&gt;Since &lt;code&gt;inlineCallbacks&lt;/code&gt; and deferreds solve many of the same problems, why choose one over the other? Here are some potential advantages of &lt;code&gt;inlineCallbacks&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Since the callbacks share a namespace, there is no need to pass extra state around.&lt;/li&gt;
&lt;li&gt;The callback order is easier to see, as they just execute from top to bottom.&lt;/li&gt;
&lt;li&gt;With no function declarations for individual callbacks and implicit flow-control, there is generally less typing.&lt;/li&gt;
&lt;li&gt;Errors are handled with the familiar &lt;code&gt;try&lt;/code&gt;/&lt;code&gt;except&lt;/code&gt; statement.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And here are some potential pitfalls:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;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.&lt;/li&gt;
&lt;li&gt;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 &lt;code&gt;inlineCallbacks&lt;/code&gt; function is not a way to avoid learning the asynchronous programming model.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As with any technique, practice will provide the experience necessary to make an informed choice.&lt;/p&gt;
&lt;h3 id="summary"&gt;Summary&lt;/h3&gt;
&lt;p&gt;In this Part we learned about the &lt;code&gt;inlineCallbacks&lt;/code&gt; decorator and how it allows us to express a sequence of asynchronous callbacks in the form of a Python generator.&lt;/p&gt;
&lt;p&gt;In &lt;a href="https://krondo.com/deferreds-en-masse/"&gt;Part 18&lt;/a&gt; we will learn a technique for managing a set of &amp;ldquo;parallel&amp;rdquo; asynchronous operations.&lt;/p&gt;
&lt;h3 id="suggested-exercises"&gt;Suggested Exercises&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Why is the &lt;code&gt;inlineCallbacks&lt;/code&gt; function plural?&lt;/li&gt;
&lt;li&gt;Study the implementation of &lt;a href="https://github.com/twisted/twisted/tree/twisted-10.1.0/twisted/internet/defer.py#973"&gt;&lt;code&gt;inlineCallbacks&lt;/code&gt;&lt;/a&gt; and its helper function &lt;a href="https://github.com/twisted/twisted/tree/twisted-10.1.0/twisted/internet/defer.py#L874"&gt;&lt;code&gt;_inlineCallbacks&lt;/code&gt;&lt;/a&gt;. Ponder the phrase &amp;ldquo;the devil is in the details&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;How many callbacks are contained in a generator with &lt;strong&gt;N&lt;/strong&gt; &lt;code&gt;yield&lt;/code&gt; statements, assuming it has no loops or &lt;code&gt;if&lt;/code&gt; statements?&lt;/li&gt;
&lt;li&gt;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 &lt;code&gt;inlineCallbacks&lt;/code&gt;, how many ways do you think are actually possible?&lt;/li&gt;
&lt;li&gt;Move the &lt;code&gt;got_poem&lt;/code&gt; callback in client 7.0 inside the generator.&lt;/li&gt;
&lt;li&gt;Then move the &lt;code&gt;poem_done&lt;/code&gt; 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?&lt;/li&gt;
&lt;li&gt;A generator with &lt;code&gt;yield&lt;/code&gt; statements inside a &lt;code&gt;while&lt;/code&gt; loop can represent a conceptually infinite sequence. What does such a generator decorated with &lt;code&gt;inlineCallbacks&lt;/code&gt; represent?&lt;/li&gt;
&lt;/ol&gt;</content:encoded></item></channel></rss>