Ticket #5685: 5685_3.patch

File 5685_3.patch, 30.0 KB (added by sdsern, 4 years ago)
  • doc/mail/tutorial/smtpclient/smtpclient-11.tac

     
    66
    77from twisted.application import internet
    88from twisted.internet import protocol
    9 from twisted.internet import defer
    109from twisted.mail import smtp, relaymanager
    1110from twisted.names.client import createResolver
    1211
  • doc/mail/tutorial/smtpclient/smtpclient-11a.tac

     
    66
    77from twisted.application import internet
    88from twisted.internet import protocol
    9 from twisted.internet import defer
    109from twisted.mail import smtp, relaymanager
    11 from twisted.names.client import createResolver
    1210
     11# Two changes are needed for this program to successfully send mail to
     12# an external server.  Change the mailTo attribute of SMTPTutorialClient
     13# to an actual email address and change the parameter to getMailExchange
     14# to the domain of that email address
     15
    1316class SMTPTutorialClient(smtp.ESMTPClient):
    1417    mailFrom = "tutorial_sender@example.com"
    1518    mailTo = "tutorial_recipient@example.net"
     
    5659    smtpClientService = internet.TCPClient(exchange, 25, smtpClientFactory)
    5760    smtpClientService.setServiceParent(application)
    5861
     62# Replace 'example.net' with the domain of the address you are sending mail to
    5963getMailExchange('example.net').addCallback(cbMailExchange)
  • doc/mail/tutorial/smtpclient/smtpclient.xhtml

     
    1111
    1212<h1>Twisted Mail Tutorial: Building an SMTP Client from Scratch</h1>
    1313
    14 <h2>Introduction</h2>
    15 
    1614<p>This tutorial will walk you through the creation of an extremely
    1715simple SMTP client application.  By the time the tutorial is complete,
    1816you will understand how to create and start a TCP client speaking the
    1917SMTP protocol, have it connect to an appropriate mail exchange server,
    2018and transmit a message for delivery.</p>
    2119
    22 <p>This tutorial uses <code>twistd</code>
    23 to launch the client and server applications.  Make
    24 sure that you have <code>twistd</code> installed and conveniently
    25 accessible for use in running each of the example <code>.tac</code>
    26 files.</p>
     20<p>
     21This tutorial uses <code>twistd</code> to launch the client and server applications. 
     22Make sure that you have <code>twistd</code> installed and conveniently accessible for use in running each of the example <code>.tac</code> files.
     23If you have checked out the Twisted code, you can find <code>twistd</code> in the <code>bin</code> directory.
     24</p>
    2725
    2826<h2>SMTP Client 1</h2>
    2927
     
    4745application = service.Application("SMTP Client Tutorial")
    4846</pre>
    4947
    50 <p>The second line of the <code>.tac</code> file creates a
    51 new <em>application service</em> and binds it to the local
    52 name <code>application</code>.  <code>twistd</code> requires this
    53 local name in each <code>.tac</code> file it runs.  It uses various
    54 pieces of configuration on the object to determine its behavior.  For
    55 example, <code>"SMTP Client Tutorial"</code> will be used as the name
     48<p>
     49The second line of the <code>.tac</code> file creates a new <em>application service</em> and binds it to the local name <code>application</code>. 
     50This local name is required by <code>twistd</code> in each <code>.tac</code> file it runs. 
     51It uses various pieces of configuration on the object to determine its behavior.
     52For example, <code>"SMTP Client Tutorial"</code> will be used as the name
    5653of the <code>.tap</code> file into which to serialize application
    57 state, should it be necessary to do so.</p>
     54state, should it be necessary to do so.
     55</p>
    5856
    59 <p>That does it for the first example.  We now have enough of
    60 a <code>.tac</code> file to pass to <code>twistd</code>.  If we
    61 run <a href="smtpclient-1.tac">smtpclient-1.tac</a> using
    62 the <code>twistd</code> command line:</p>
     57<p>
     58That does it for the first example. 
     59We now have enough of a <code>.tac</code> file to pass to <code>twistd</code>. 
     60If we run <a href="smtpclient-1.tac"><code>smtpclient-1.tac</code></a> using
     61the <code>twistd</code> command line:
     62</p>
    6363
    6464<pre class="shell">
    6565twistd -ny smtpclient-1.tac
     
    6969release and platform):</p>
    7070
    7171<pre class="shell">
    72 exarkun@boson:~/mail/tutorial/smtpclient$ twistd -ny smtpclient-1.tac
    737218:31 EST [-] Log opened.
    747318:31 EST [-] twistd 2.0.0 (/usr/bin/python2.4 2.4.1) starting up
    757418:31 EST [-] reactor class: twisted.internet.selectreactor.SelectReactor
     
    848318:34 EST [-] Received SIGINT, shutting down.
    858418:34 EST [-] Main loop terminated.
    868518:34 EST [-] Server Shut Down.
    87 exarkun@boson:~/mail/tutorial/smtpclient$
    8886</pre>
    8987
    9088<h2>SMTP Client 2</h2>
     
    108106implementations of many of the core Twisted concepts, such
    109107as <em>factories</em> and <em>protocols</em>.</p>
    110108
    111 <p>The next line of <a href="smtpclient-2.tac">smtpclient-2.tac</a>
    112 instantiates a new <em>client factory</em>.</p>
     109<p>
     110The next line of <a href="smtpclient-2.tac"><code>smtpclient-2.tac</code></a> instantiates a new <em>client factory</em>, <code class="API">twisted.internet.protocol.ClientFactory</code>:
     111</p>
    113112
    114113<pre class="python">
    115114smtpClientFactory = protocol.ClientFactory()
     
    122121may never be required to create one at all, if no connection ever
    123122manages to be established.</p>
    124123
    125 <p>Now that we have a client factory, we'll need to hook it up to the
    126 network somehow.  The next line of <code>smtpclient-2.tac</code> does
    127 just that:</p>
     124<p>
     125Now that we have a <em>client factory</em>, we'll need to hook it up to the network somehow. 
     126The next line of <code>smtpclient-2.tac</code> does just that:
     127</p>
    128128
    129129<pre class="python">
    130130smtpClientService = internet.TCPClient(None, None, smtpClientFactory)
    131131</pre>
    132132
    133 <p>We'll ignore the first two arguments
    134 to <code>internet.TCPClient</code> for the moment and instead focus on
    135 the third.  <code>TCPClient</code> is one of those <em>application
    136 service</em> classes.  It creates TCP connections to a specified
    137 address and then uses its third argument, a <em>client factory</em>,
    138 to get a <em>protocol instance</em>.  It then associates the TCP
    139 connection with the protocol instance and gets out of the way.</p>
     133<p>
     134We'll ignore the first two arguments to <code>TCPClient</code> for the moment and instead focus on the third. 
     135<code>TCPClient</code> is another of those <em>application service</em> classes. 
     136It creates TCP connections to a specified address and then uses its third argument, a <em>client factory</em>, to get a <em>protocol instance</em>. 
     137It then associates the TCP connection with the protocol instance and gets out of the way.
     138</p>
    140139
    141140<p>We can try to run <code>smtpclient-2.tac</code> the same way we
    142141ran <code>smtpclient-1.tac</code>, but the results might be a little
    143142disappointing:</p>
    144143
    145144<pre class="shell">
    146 exarkun@boson:~/mail/tutorial/smtpclient$ twistd -ny smtpclient-2.tac
    14714518:55 EST [-] Log opened.
    14814618:55 EST [-] twistd SVN-Trunk (/usr/bin/python2.4 2.4.1) starting up
    14914718:55 EST [-] reactor class: twisted.internet.selectreactor.SelectReactor
     
    170168          File "/usr/lib/python2.4/string.py", line 292, in split
    171169            return s.split(sep, maxsplit)
    172170        exceptions.AttributeError: 'NoneType' object has no attribute 'split'
    173 
    174 18:55 EST [-] Received SIGINT, shutting down.
    175 18:55 EST [-] Main loop terminated.
    176 18:55 EST [-] Server Shut Down.
    177 exarkun@boson:~/mail/tutorial/smtpclient$
    178171</pre>
    179172
    180173<p>What happened?  Those first two arguments to <code>TCPClient</code>
     
    207200<code>tutorial_recipient@localhost</code>, stores the
    208201email in <code>/tmp/emails</code>, and stores the process id in the
    209202<code>server.pid</code> file:
     203</p>
    210204<pre class="shell">
    211205twistd --pidfile=server.pid mail -H localhost -s 8025 \
    212206       -d localhost=/tmp/emails -u tutorial_recipient=pwd
    213207</pre>
    214 Now, we can
    215 run <a href="smtpclient-3.tac">smtpclient-3.tac</a> and see what this
    216 change gets us:</p>
     208<p>
     209Now, we can run <a href="smtpclient-3.tac"><code>smtpclient-3.tac</code></a> and see what this change gets us:
     210</p>
    217211
    218212<pre class="shell">
    219 exarkun@boson:~/mail/tutorial/smtpclient$ twistd -ny smtpclient-3.tac
    22021319:10 EST [-] Log opened.
    22121419:10 EST [-] twistd SVN-Trunk (/usr/bin/python2.4 2.4.1) starting up
    22221519:10 EST [-] reactor class: twisted.internet.selectreactor.SelectReactor
     
    25024319:10 EST [Uninitialized] Stopping factory
    251244          &lt;twisted.internet.protocol.ClientFactory instance at
    252245          0xb791e48c&gt;
    253 19:10 EST [-] Received SIGINT, shutting down.
    254 19:10 EST [-] Main loop terminated.
    255 19:10 EST [-] Server Shut Down.
    256 exarkun@boson:~/mail/tutorial/smtpclient$
    257246</pre>
    258247
    259 <p>A meagre amount of progress, but the service still raises an
    260 exception.  This time, it's because we haven't specified
    261 a <em>protocol class</em> for the factory to use.  We'll do that in
    262 the next example.</p>
     248<p>
     249A meager amount of progress, but the service still raises an exception. 
     250This time, it's because we haven't specified a <em>protocol class</em> for the factory to use. 
     251We'll do that in the next example.
     252</p>
    263253
    264254<h2>SMTP Client 4</h2>
    265255
    266 <p>In the previous example, we ran into a problem because we hadn't
    267 set up our <em>client factory's</em> <em>protocol</em> attribute
    268 correctly (or at all).  <code>ClientFactory.buildProtocol</code> is
    269 the method responsible for creating a <em>protocol instance</em>.  The
    270 default implementation calls the factory's <code>protocol</code> attribute,
    271 adds itself as an attribute named <code>factory</code> to the
    272 resulting instance, and returns it.  In <a
    273 href="smtpclient-4.tac">smtpclient-4.tac</a>, we'll correct the
    274 oversight that caused the traceback in smtpclient-3.tac:</p>
     256<p>
     257In the previous example, we ran into a problem because we hadn't set up our <em>client factory's</em> <em>protocol</em> attribute correctly (or at all). 
     258<code class="API">twisted.internet.protocol.ClientFactory</code>'s <code>buildProtocol</code> method is responsible for creating a <em>protocol instance</em>.
     259The default implementation calls the factory's <code>protocol</code> attribute, adds itself as an attribute named <code>factory</code> to the resulting instance, and returns it. 
     260In <a href="smtpclient-4.tac"><code>smtpclient-4.tac</code></a>, we'll correct the oversight that caused the traceback in <code>smtpclient-3.tac</code>:
     261</p>
    275262
    276263<pre class="python">
    277264smtpClientFactory.protocol = protocol.Protocol
     
    281268again traceback-free:</p>
    282269
    283270<pre class="shell">
    284 exarkun@boson:~/doc/mail/tutorial/smtpclient$ twistd -ny smtpclient-4.tac
    28527119:29 EST [-] Log opened.
    28627219:29 EST [-] twistd SVN-Trunk (/usr/bin/python2.4 2.4.1) starting up
    28727319:29 EST [-] reactor class: twisted.internet.selectreactor.SelectReactor
     
    29027619:29 EST [-] Starting factory &lt;twisted.internet.protocol.ClientFactory
    291277              instance at 0xb791e4ac&gt;
    29227819:29 EST [-] Enabling Multithreading.
    293 19:29 EST [-] Received SIGINT, shutting down.
    294 19:29 EST [Protocol,client] Stopping factory
    295           &lt;twisted.internet.protocol.ClientFactory instance at
    296           0xb791e4ac&gt;
    297 19:29 EST [-] Main loop terminated.
    298 19:29 EST [-] Server Shut Down.
    299 exarkun@boson:~/doc/mail/tutorial/smtpclient$
    300279</pre>
    301280
    302281<p>But what does this
     
    310289
    311290<h2>SMTP Client 5</h2>
    312291
    313 <p>In <a href="smtpclient-5.tac">smtpclient-5.tac</a>, we will
    314 use Twisted's SMTP protocol implementation for the first time.
    315 We'll make the obvious change, simply swapping
    316 out <code>twisted.internet.protocol.Protocol</code> in favor
    317 of <code class="API">twisted.mail.smtp.ESMTPClient</code>.  Don't worry about
    318 the <em>E</em> in <em>ESMTP</em>.  It indicates we're actually using a
    319 newer version of the SMTP protocol.  There is
    320 an <code>SMTPClient</code> in Twisted, but there's essentially no
     292<p>
     293In <a href="smtpclient-5.tac"><code>smtpclient-5.tac</code></a>, we will use Twisted's SMTP protocol implementation for the first time.
     294We'll make the obvious change, simply swapping out <code>twisted.internet.protocol.Protocol</code> in favor of <code class="API">twisted.mail.smtp.ESMTPClient</code>. 
     295Don't worry about the <em>E</em> in <em>ESMTP</em>. 
     296It indicates we're actually using a newer version of the SMTP protocol. 
     297There is an <code>SMTPClient</code> in Twisted, but there's essentially no
    321298reason to ever use it.</p>
    322299
    323 <p>smtpclient-5.tac adds a new import:</p>
     300<p>
     301<code>smtpclient-5.tac</code> adds a new import:
     302</p>
    324303
    325304<pre class="python">
    326305from twisted.mail import smtp
    327306</pre>
    328307
    329 <p>All of the mail related code in Twisted exists beneath the
    330 <code class="API">twisted.mail</code> package.  More specifically, everything
    331 having to do with the SMTP protocol implementation is defined in
    332 the <code class="API">twisted.mail.smtp</code> module.</p>
     308<p>
     309All of the mail-related code in Twisted exists beneath the <code class="API">twisted.mail</code> package. 
     310More specifically, everything having to do with the SMTP protocol implementation is defined in the <code class="API">twisted.mail.smtp</code> module.
     311</p>
    333312
    334 <p>Next we remove a line we added in smtpclient-4.tac:</p>
     313<p>
     314Next we remove a line we added in <code>smtpclient-4.tac</code>:
     315</p>
    335316
    336317<pre class="python">
    337318smtpClientFactory.protocol = protocol.Protocol
     
    348329version?</p>
    349330
    350331<pre class="shell">
    351 exarkun@boson:~/doc/mail/tutorial/smtpclient$ twistd -ny smtpclient-5.tac
    35233219:42 EST [-] Log opened.
    35333319:42 EST [-] twistd SVN-Trunk (/usr/bin/python2.4 2.4.1) starting up
    35433419:42 EST [-] reactor class: twisted.internet.selectreactor.SelectReactor
     
    38236219:42 EST [Uninitialized] Stopping factory
    383363          &lt;twisted.internet.protocol.ClientFactory instance at
    384364          0xb791e54c&gt;
    385 19:43 EST [-] Received SIGINT, shutting down.
    386 19:43 EST [-] Main loop terminated.
    387 19:43 EST [-] Server Shut Down.
    388 exarkun@boson:~/doc/mail/tutorial/smtpclient$
    389365</pre>
    390366
    391367
     
    398374
    399375<h2>SMTP Client 6</h2>
    400376
    401 <p><a href="smtpclient-6.tac">smtpclient-6.tac</a> introduces
    402 a <code class="API">twisted.internet.protocol.ClientFactory</code> subclass with
    403 an overridden <code>buildProtocol</code> method to overcome the
    404 problem encountered in the previous example.</p>
     377<p>
     378<a href="smtpclient-6.tac"><code>smtpclient-6.tac</code></a> introduces a <code>ClientFactory</code> subclass with an overridden <code>buildProtocol</code> method to overcome the problem encountered in the previous example.
     379</p>
    405380
    406381<pre class="python">
    407382class SMTPClientFactory(protocol.ClientFactory):
     
    411386        return self.protocol(secret=None, identity='example.com')
    412387</pre>
    413388
    414 <p>The overridden method does almost the same thing as the base
    415 implementation: the only change is that it passes values for two
    416 arguments to <code>twisted.mail.smtp.ESMTPClient</code>'s initializer.
    417 The <code>secret</code> argument is used for SMTP authentication
    418 (which we will not attempt yet).  The <code>identity</code> argument
    419 is used to identify ourselves. Another minor change to note is
    420 that the <code>protocol</code> attribute is now defined in the class
    421 definition, rather than tacked onto an instance after one is created.
    422 This means it is a class attribute, rather than an instance attribute,
    423 now, which makes no difference as far as this example is concerned.
    424 There are circumstances in which the difference is important: be sure
    425 you understand the implications of each approach when creating your
    426 own factories.</p>
     389<p>
     390The overridden method does almost the same thing as the base implementation: the only change is that it passes values for two arguments to <code class="API">twisted.mail.smtp.ESMTPClient</code>'s initializer.
     391The <code>secret</code> argument is used for SMTP authentication (which we will not attempt here). 
     392The <code>identity</code> argument is used to identify ourselves.
     393Another minor change to note is that the <code>protocol</code> attribute is now defined in the class definition, rather than tacked onto an instance after one is created.
     394This means it is a class attribute, rather than an instance attribute, now, which makes no difference as far as this example is concerned.
     395There are circumstances in which the difference is important: be sure you understand the implications of each approach when creating your own factories.
     396</p>
    427397
    428 <p>One other change is required.  Instead of
    429 instantiating <code>twisted.internet.protocol.ClientFactory</code>, we
    430 will now instantiate <code>SMTPClientFactory</code>:</p>
     398<p>
     399One other change is required. 
     400Instead of instantiating <code>ClientFactory</code>, we will now instantiate <code>SMTPClientFactory</code>:
     401</p>
    431402
    432403<pre class="python">
    433404smtpClientFactory = SMTPClientFactory()
     
    437408code <strong>still</strong> isn't quite traceback-free.</p>
    438409
    439410<pre class="shell">
    440 exarkun@boson:~/doc/mail/tutorial/smtpclient$ twistd -ny smtpclient-6.tac
    44141121:17 EST [-] Log opened.
    44241221:17 EST [-] twistd SVN-Trunk (/usr/bin/python2.4 2.4.1) starting up
    44341321:17 EST [-] reactor class: twisted.internet.selectreactor.SelectReactor
     
    478448
    47944921:17 EST [ESMTPClient,client] Stopping factory
    480450          &lt;__builtin__.SMTPClientFactory instance at 0xb77fd68c&gt;
    481 21:17 EST [-] Received SIGINT, shutting down.
    482 21:17 EST [-] Main loop terminated.
    483 21:17 EST [-] Server Shut Down.
    484 exarkun@boson:~/doc/mail/tutorial/smtpclient$
    485451</pre>
    486452
    487453<p>What we have accomplished with this iteration of the example is to
     
    492458
    493459<h2>SMTP Client 7</h2>
    494460
    495 <p>SMTP Client 7 is the first version of our SMTP client which
    496 actually includes message data to transmit.  For simplicity's sake,
    497 the message is defined as part of a new class.  In a useful program
    498 which sent email, message data might be pulled in from the filesystem,
    499 a database, or be generated based on
    500 user-input.  <a href="smtpclient-7.tac">smtpclient-7.tac</a>, however,
    501 defines a new class, <code>SMTPTutorialClient</code>, with three class
    502 attributes (<code>mailFrom</code>, <code>mailTo</code>,
    503 and <code>mailData</code>):</p>
     461<p>
     462SMTP Client 7 is the first version of our SMTP client which actually includes message data to transmit. 
     463For simplicity's sake, the message is defined as part of a new class. 
     464In a useful program which sent email, message data might be pulled in from the filesystem, a database, or be generated based on user input. 
     465<a href="smtpclient-7.tac"><code>smtpclient-7.tac</code></a>, however, defines a new class, <code>SMTPTutorialClient</code>, with three class attributes (<code>mailFrom</code>, <code>mailTo</code>, and <code>mailData</code>):
     466</p>
    504467
    505468<pre class="python">
    506469class SMTPTutorialClient(smtp.ESMTPClient):
     
    516479'''
    517480</pre>
    518481
    519 <p>This statically defined data is accessed later in the class
    520 definition by three of the methods which are part of the
    521  <em>SMTPClient callback API</em>.  Twisted expects each of the three
    522 methods below to be defined and to return an object with a particular
    523 meaning.  First, <code>getMailFrom</code>:</p>
     482<p>
     483This statically defined data is accessed later in the class definition by three methods which are defined by the base class <code class="API">twisted.mail.smtp.SMTP</code> and are expected to be overridden in subclasses.
     484The first method is <code>getMailFrom</code>:
     485 </p>
    524486
    525487<pre class="python">
    526488    def getMailFrom(self):
     
    529491        return result
    530492</pre>
    531493
    532 <p>This method is called to determine the <em>reverse-path</em>,
    533 otherwise known as the <em>envelope from</em>, of the message.  This
    534 value will be used when sending the <code>MAIL FROM</code> SMTP
    535 command.  The method must return a string which conforms to the <a
     494<p>
     495This method is called to determine the <em>reverse-path</em>, otherwise known as the <em>envelope from</em>, of the message. 
     496This value will be used when sending the <code>MAIL FROM</code> SMTP
     497command. 
     498The method must return a string which conforms to the <a
    536499href="http://www.faqs.org/rfcs/rfc2821.html">RFC 2821</a> definition
    537 of a <em>reverse-path</em>.  In simpler terms, it should be a string
    538 like <code>"alice@example.com"</code>.  Only one <em>envelope
    539 from</em> is allowed by the SMTP protocol, so it cannot be a list of
    540 strings or a comma separated list of addresses.  Our implementation
    541 of <code>getMailFrom</code> does a little bit more than just return a
    542 string; we'll get back to this in a little bit.</p>
     500of a <em>reverse-path</em>. 
     501In simpler terms, it should be a string like <code>"alice@example.com"</code>.  Only one <em>envelope from</em> is allowed by the SMTP protocol, so it cannot be a list of strings or a comma separated list of addresses. 
     502</p>
    543503
    544504<p>The next method is <code>getMailTo</code>:</p>
    545505
     
    563523        return StringIO.StringIO(self.mailData)
    564524</pre>
    565525
    566 <p>This one is quite simple as well: it returns a file or a file-like
    567 object which contains the message contents.  In our case, we return
    568 a <code>StringIO</code> since we already have a string containing our
    569 message.  If the contents of the file returned
    570 by <code>getMailData</code> span multiple lines (as email messages
    571 often do), the lines should be <code>\n</code> delimited (as they
    572 would be when opening a text file in the <code>"rt"</code> mode).
    573 Necessary newline translation will be performed
    574 by <code>SMTPClient</code> automatically.</p>
     526<p>
     527This one is quite simple as well: it returns a file or a file-like object which contains the message contents. 
     528In our case, we return a <code>StringIO</code> object since we already have a string containing our message. 
     529If the contents of the file returned by <code>getMailData</code> span multiple lines (as email messages often do), the lines should be <code>\n</code> delimited (as they would be when opening a text file in the <code>"rt"</code> mode).
     530Necessary newline translation will be performed by <code>SMTPClient</code> automatically.
     531</p>
    575532
    576 <p>There is one more new callback method defined in smtpclient-7.tac.
     533<p>
     534There is one more new callback method defined in <code>smtpclient-7.tac</code>.
    577535This one isn't for providing information about the messages to
    578536Twisted, but for Twisted to provide information about the success or
    579537failure of the message transmission to the application:</p>
     
    599557
    600558<h2>SMTP Client 8</h2>
    601559
    602 <p>Thus far we have succeeded in creating a Twisted client application
    603 which starts up, connects to a (possibly) remote host, transmits some
    604 data, and disconnects.  Notably missing, however, is application
    605 shutdown.  Hitting <code>^C</code> is fine during development,
    606 but it's not exactly
    607 a long-term solution.  Fortunately, programmatic shutdown is extremely
    608 simple.  <a href="smtpclient-8.tac">smtpclient-8.tac</a>
    609 extends <code>sentMail</code> with these two lines:</p>
     560<p>
     561Thus far we have succeeded in creating a Twisted client application which starts up, connects to a server, transmits some data, and disconnects. 
     562Notably missing, however, is application shutdown. 
     563Hitting <code>^C</code> is fine during development, but it's not exactly a long-term solution. 
     564Fortunately, programmatic shutdown is extremely simple. 
     565<a href="smtpclient-8.tac"><code>smtpclient-8.tac</code></a> extends <code>sentMail</code> with these two lines:
     566</p>
    610567
    611568<pre class="python">
    612569        from twisted.internet import reactor
    613570        reactor.stop()
    614571</pre>
    615572
    616 <p>The <code>stop</code> method of the reactor causes the main event
    617 loop to exit, allowing a Twisted server to shut down.  With this
    618 version of the example, we see that the program actually terminates
    619 after sending the message, without user-intervention:</p>
     573<p>
     574The <code>stop</code> method of the <code>reactor</code> causes the main event loop to exit, allowing a Twisted server to shut down.
     575With this version of the example, we see that the program actually terminates
     576after sending the message, without user intervention:
     577</p>
    620578
    621579<pre class="shell">
    622 exarkun@boson:~/doc/mail/tutorial/smtpclient$ twistd -ny smtpclient-8.tac
    62358019:52 EST [-] Log opened.
    62458119:52 EST [-] twistd SVN-Trunk (/usr/bin/python2.4 2.4.1) starting up
    62558219:52 EST [-] reactor class: twisted.internet.selectreactor.SelectReactor
     
    633590          &lt;__builtin__.SMTPClientFactory instance at 0xb791beec&gt;
    63459119:52 EST [-] Main loop terminated.
    63559219:52 EST [-] Server Shut Down.
    636 exarkun@boson:~/doc/mail/tutorial/smtpclient$
    637593</pre>
    638594
    639595<h2>SMTP Client 9</h2>
     
    660616twistd --pidfile=server.pid mail -H example.net -s 8025 \
    661617       -d example.net=/tmp/emails -u tutorial_recipient=pwd
    662618</pre>
    663 <p>In <a href="smtpclient-9.tac">smtpclient-9.tac</a>, we'll take the
    664 first step towards this feature by defining a function which returns
    665 the mail exchange host for a particular domain:</p>
     619<p>
     620In <a href="smtpclient-9.tac"><code>smtpclient-9.tac</code></a>, we'll take the first step towards this feature by defining a function which returns the mail exchange host for a particular domain:
     621</p>
    666622
    667623<pre class="python">
    668624def getMailExchange(host):
     
    686642
    687643<h2>SMTP Client 10</h2>
    688644
    689 <p>In the previous example we defined <code>getMailExchange</code> to
    690 return a string representing the mail exchange host for a particular
    691 domain.  While this was a step in the right direction, it turns out
    692 not to be a very big one.  Determining the mail exchange host for a
    693 particular domain is going to involve network traffic (specifically,
    694 some DNS requests).  These might take an arbitrarily large amount of
    695 time, so we need to introduce a <code>Deferred</code> to represent the
    696 result of <code>getMailExchange</code>.  <a
    697 href="smtpclient-10.tac">smtpclient-10.tac</a> redefines it
    698 thusly:</p>
     645<p>
     646In the previous example we defined <code>getMailExchange</code> to return a string representing the mail exchange host for a particular domain. 
     647While this was a step in the right direction, it turns out not to be a very big one. 
     648Determining the mail exchange host for a particular domain is going to involve network traffic (specifically, some DNS requests). 
     649This might take an arbitrarily large amount of time.
     650To address this issue, Twisted provides <code>Deferred</code>s, a mechanism for delaying action until an asynchronous request is complete. 
     651An in-depth exploration of <code>Deferred</code>s is beyond the scope of this document. 
     652For more information, see the <a href="../../../core/howto/defer.html">Deferred Reference</a>.
     653</p>
    699654
     655<p>
     656We can use a <code class="API">twisted.internet.defer.Deferred</code> to represent the result of <code>getMailExchange</code>. 
     657<a href="smtpclient-10.tac"><code>smtpclient-10.tac</code></a> redefines it thusly:
     658</p>
     659
    700660<pre class="python">
    701661def getMailExchange(host):
    702662    return defer.succeed('localhost')
    703663</pre>
    704664
    705 <p><code>defer.succeed</code> is a function which creates a
    706 new <code>Deferred</code> which already has a result, in this
    707 case <code>'localhost'</code>.  Now we need to adjust
    708 our <code>TCPClient</code>-constructing code to expect and properly
    709 handle this <code>Deferred</code>:</p>
     665<p>
     666Since we're not using DNS to get the mail exchange, we use <code>defer.succeed</code> to return a <code>Deferred</code> which already has a result, in this case <code>'localhost'</code>. 
     667Now we need to adjust our <code>TCPClient</code>-constructing code to expect and properly handle this <code>Deferred</code>:
     668</p>
    710669
    711670<pre class="python">
    712671def cbMailExchange(exchange):
     
    718677getMailExchange('example.net').addCallback(cbMailExchange)
    719678</pre>
    720679
    721 <p>An in-depth exploration of <code>Deferred</code>s is beyond the
    722 scope of this document.  For such a look, see
    723 the <a href="../../../core/howto/defer.html">Deferred Reference</a>.
    724 However, in brief, what this version of the code does is to delay the
    725 creation of the <code>TCPClient</code> until the <code>Deferred</code>
    726 returned by <code>getMailExchange</code> fires.  Once it does, we
    727 proceed normally through the creation of
    728 our <code>SMTPClientFactory</code> and <code>TCPClient</code>, as well
    729 as set the <code>TCPClient</code>'s service parent, just as we did in
    730 the previous examples.</p>
     680<p>
     681The new function, <code>cbMailExchange</code>, is set as a callback on the
     682<code>Deferred</code> returned by <code>getMailExchange</code>. 
     683When the asynchronous request initiated by <code>getMailExchange</code> is successfully completed, <code>cbMailExchange</code> will be called. 
     684Then, we proceed normally through the creation of our <code>SMTPClientFactory</code> and <code>TCPClient</code>, as well as set the <code>TCPClient</code>'s service parent, just as we did in the previous examples.
     685</p>
    731686
    732687<h2>SMTP Client 11</h2>
    733688
     
    746701</p>
    747702
    748703<p>
    749 The following modification in <a href="smtpclient-11.tac">smtpclient-11.tac</a>
    750 creates such an <code>MXCalculator</code>:
     704The following modification in <a href="smtpclient-11.tac"><code>smtpclient-11.tac</code></a> creates such an <code>MXCalculator</code>:
    751705</p>
    752706<pre class="python">
    753707def getMailExchange(host):
     
    765719required.  </p>
    766720
    767721<p>
    768 In case your ISP does not block port 25 and you'd like to try sending mail
    769 to port 25 on <code>example.net</code>, try running
    770 <a href="smtpclient-11a.tac">smtpclient-11a.tac</a>.  It configures the
    771 <code>MXCalculator</code> to contact a DNS server to get the address of
    772 <code>example.net</code> and sends the message to its port 25.
     722In case your ISP does not block port 25 and you'd like to try to send mail to an external server, <a href="smtpclient-11a.tac"><code>smtpclient-11a.tac</code></a> configures the <code>MXCalculator</code> to contact a DNS server to get the address of the mail server and sends the message to its port 25.
     723Since <code>example.net</code> does not accept email, you'll need to make two changes to the file to supply an actual email address for the recipient and to look up the recipient's domain.
    773724</p>
    774725
    775726
    776 <h2>Summary</h2>
     727<h2>Conclusion</h2>
    777728<p>
    778729This tutorial has worked its way from the skeleton of a Twisted application,
    779730through an SMTP client which sends a message to a recipient on an SMTP server