Ticket #4572: smtpclient.xhtml.patch

File smtpclient.xhtml.patch, 17.0 KB (added by jdb, 4 years ago)
  • doc/mail/tutorial/smtpclient/smtpclient.xhtml

    diff --git a/doc/mail/tutorial/smtpclient/smtpclient.xhtml b/doc/mail/tutorial/smtpclient/smtpclient.xhtml
    index aea44ba..9e89a84 100644
    a b files.</p> 
    2929<h3>SMTP Client 1</h3> 
    3030 
    3131<p>The first step is to create <a href="smtpclient-1.tac">the most 
    32 minimal <code>.tac</code> file</a> possible for use by 
    33 <code>twistd</code>.</p> 
     32minimal <code>.tac</code> file</a> possible for use by <code>twistd</code> .</p> 
    3433 
    3534<pre class="python"> 
    3635from twisted.application import service 
    3736</pre> 
    3837 
    39 <p>The first line of the <code>.tac</code> file imports 
    40 <code>twisted.application.service</code>, a module which contains many 
    41 of the basic <em>service</em> classes and helper functions available 
    42 in Twisted.  In particular, we will be using the 
    43 <code>Application</code> function to create a new <em>application 
     38<p>The first line of the <code>.tac</code> file 
     39imports <code>twisted.application.service</code>, a module which 
     40contains many of the basic <em>service</em> classes and helper 
     41functions available in Twisted.  In particular, we will be using 
     42the <code>Application</code> function to create a new <em>application 
    4443service</em>.  An <em>application service</em> simply acts as a 
    4544central object on which to store certain kinds of deployment 
    4645configuration.</p> 
    configuration.</p> 
    4948application = service.Application("SMTP Client Tutorial") 
    5049</pre> 
    5150 
    52 <p>The second line of the <code>.tac</code> file creates a new 
    53 <em>application service</em> and binds it to the local name 
    54 <code>application</code>.  <code>twistd</code> requires this local 
    55 name in each <code>.tac</code> file it runs.  It uses various pieces 
    56 of configuration on the object to determine its behavior.  For 
     51<p>The second line of the <code>.tac</code> file creates a 
     52new <em>application service</em> and binds it to the local 
     53name <code>application</code>.  <code>twistd</code> requires this 
     54local name in each <code>.tac</code> file it runs.  It uses various 
     55pieces of configuration on the object to determine its behavior.  For 
    5756example, <code>"SMTP Client Tutorial"</code> will be used as the name 
    5857of the <code>.tap</code> file into which to serialize application 
    5958state, should it be necessary to do so.</p> 
    6059 
    61 <p>That does it for the first example.  We now have enough of a 
    62 <code>.tac</code> file to pass to <code>twistd</code>.  If we run <a 
    63 href="smtpclient-1.tac">smtpclient-1.tac</a> using the 
    64 <code>twistd</code> command line:</p> 
     60<p>That does it for the first example.  We now have enough of 
     61a <code>.tac</code> file to pass to <code>twistd</code>.  If we 
     62run <a href="smtpclient-1.tac">smtpclient-1.tac</a> using 
     63the <code>twistd</code> command line:</p> 
    6564 
    6665<pre class="python"> 
    6766twistd -ny smtpclient-1.tac 
    from twisted.application import internet 
    101100from twisted.internet import protocol 
    102101</pre> 
    103102 
    104 <p><code>twisted.application.internet</code> is another 
    105 <em>application service</em> module.  It provides services for 
     103<p><code>twisted.application.internet</code> is 
     104another <em>application service</em> module.  It provides services for 
    106105establishing outgoing connections (as well as creating network 
    107 servers, though we are not interested in those parts for the moment). 
    108 <code>twisted.internet.protocol</code> provides base implementations 
    109 of many of the core Twisted concepts, such as <em>factories</em> and 
    110 <em>protocols</em>.</p> 
     106servers, though we are not interested in those parts for the 
     107moment). <code>twisted.internet.protocol</code> provides base 
     108implementations of many of the core Twisted concepts, such 
     109as <em>factories</em> and <em>protocols</em>.</p> 
    111110 
    112111<p>The next line of <a href="smtpclient-2.tac">smtpclient-2.tac</a> 
    113112instantiates a new <em>client factory</em>.</p> 
    instantiates a new <em>client factory</em>.</p> 
    116115smtpClientFactory = protocol.ClientFactory() 
    117116</pre> 
    118117 
    119 <p><em>Client factories</em> are responsible for constructing 
    120 <em>protocol instances</em> whenever connections are established. 
    121 They may be required to create just one instance, or many instances if 
    122 many different connections are established, or they may never be 
    123 required to create one at all, if no connection ever manages to be 
    124 established.</p> 
     118<p><em>Client factories</em> are responsible for 
     119constructing <em>protocol instances</em> whenever connections are 
     120established.  They may be required to create just one instance, or 
     121many instances if many different connections are established, or they 
     122may never be required to create one at all, if no connection ever 
     123manages to be established.</p> 
    125124 
    126125<p>Now that we have a client factory, we'll need to hook it up to the 
    127126network somehow.  The next line of <code>smtpclient-2.tac</code> does 
    just that:</p> 
    131130smtpClientService = internet.TCPClient(None, None, smtpClientFactory) 
    132131</pre> 
    133132 
    134 <p>We'll ignore the first two arguments to 
    135 <code>internet.TCPClient</code> for the moment and instead focus on 
     133<p>We'll ignore the first two arguments 
     134to <code>internet.TCPClient</code> for the moment and instead focus on 
    136135the third.  <code>TCPClient</code> is one of those <em>application 
    137136service</em> classes.  It creates TCP connections to a specified 
    138137address and then uses its third argument, a <em>client factory</em>, 
    139138to get a <em>protocol instance</em>.  It then associates the TCP 
    140139connection with the protocol instance and gets out of the way.</p> 
    141140 
    142 <p>We can try to run <code>smtpclient-2.tac</code> the same way we ran 
    143 <code>smtpclient-1.tac</code>, but the results might be a little 
     141<p>We can try to run <code>smtpclient-2.tac</code> the same way we 
     142ran <code>smtpclient-1.tac</code>, but the results might be a little 
    144143disappointing:</p> 
    145144 
    146145<pre class="shell"> 
    something with a bit more meaning:</p> 
    198197smtpClientService = internet.TCPClient('localhost', 25, smtpClientFactory) 
    199198</pre> 
    200199 
    201 <p>This directs the client to connect to <em>localhost</em> on port 
    202 <em>25</em>.  This isn't the address we want ultimately, but it's a 
    203 good place-holder for the time being.  We can run <a 
    204 href="smtpclient-3.tac">smtpclient-3.tac</a> and see what this change 
    205 gets us:</p> 
     200<p>This directs the client to connect to <em>localhost</em> on 
     201port <em>25</em>.  This isn't the address we want ultimately, but it's 
     202a good place-holder for the time being.  We can 
     203run <a href="smtpclient-3.tac">smtpclient-3.tac</a> and see what this 
     204change gets us:</p> 
    206205 
    207206<pre class="shell"> 
    208207exarkun@boson:~/mail/tutorial/smtpclient$ twistd -ny smtpclient-3.tac 
    exarkun@boson:~/mail/tutorial/smtpclient$ 
    246245</pre> 
    247246 
    248247<p>A meagre amount of progress, but the service still raises an 
    249 exception.  This time, it's because we haven't specified a 
    250 <em>protocol class</em> for the factory to use.  We'll do that in the 
    251 next example.</p> 
     248exception.  This time, it's because we haven't specified 
     249a <em>protocol class</em> for the factory to use.  We'll do that in 
     250the next example.</p> 
    252251 
    253252<h3>SMTP Client 4</h3> 
    254253 
    exarkun@boson:~/doc/mail/tutorial/smtpclient$ twistd -ny smtpclient-4.tac 
    288287exarkun@boson:~/doc/mail/tutorial/smtpclient$ 
    289288</pre> 
    290289 
    291 <p>But what does this mean? 
    292 <code>twisted.internet.protocol.Protocol</code> is the base 
    293 <em>protocol</em> implementation.  For those familiar with the classic 
    294 UNIX network services, it is equivalent to the <em>discard</em> 
    295 service.  It never produces any output and it discards all its input. 
    296 Not terribly useful, and certainly nothing like an SMTP client.  Let's 
    297 see how we can improve this in the next example.</p> 
     290<p>But what does this 
     291mean? <code>twisted.internet.protocol.Protocol</code> is the 
     292base <em>protocol</em> implementation.  For those familiar with the 
     293classic UNIX network services, it is equivalent to 
     294the <em>discard</em> service.  It never produces any output and it 
     295discards all its input.  Not terribly useful, and certainly nothing 
     296like an SMTP client.  Let's see how we can improve this in the next 
     297example.</p> 
    298298 
    299299<h3>SMTP Client 5</h3> 
    300300 
    301301<p>In <a href="smtpclient-5.tac">smtpclient-5.tac</a>, we will begin 
    302302to use Twisted's SMTP protocol implementation for the first time. 
    303 We'll make the obvious change, simply swapping out 
    304 <code>twisted.internet.protocol.Protocol</code> in favor of 
    305 <code>twisted.mail.smtp.ESMTPClient</code>.  Don't worry about the 
    306 <em>E</em> in <em>ESMTP</em>.  It indicates we're actually using a 
    307 newer version of the SMTP protocol.  There is an 
    308 <code>SMTPClient</code> in Twisted, but there's essentially no reason 
    309 to ever use it.</p> 
     303We'll make the obvious change, simply swapping 
     304out <code>twisted.internet.protocol.Protocol</code> in favor 
     305of <code>twisted.mail.smtp.ESMTPClient</code>.  Don't worry about 
     306the <em>E</em> in <em>ESMTP</em>.  It indicates we're actually using a 
     307newer version of the SMTP protocol.  There is 
     308an <code>SMTPClient</code> in Twisted, but there's essentially no 
     309reason to ever use it.</p> 
    310310 
    311311<p>smtpclient-5.tac adds a new import:</p> 
    312312 
    to ever use it.</p> 
    314314from twisted.mail import smtp 
    315315</pre> 
    316316 
    317 <p>All of the mail related code in Twisted exists beneath the 
    318 <code>twisted.mail</code> package.  More specifically, everything 
    319 having to do with the SMTP protocol implementation is defined in the 
    320 <code>twisted.mail.smtp</code> module.</p> 
     317<p>All of the mail related code in Twisted exists beneath 
     318the <code>twisted.mail</code> package.  More specifically, everything 
     319having to do with the SMTP protocol implementation is defined in 
     320the <code>twisted.mail.smtp</code> module.</p> 
    321321 
    322322<p>Next we remove a line we added in smtpclient-4.tac:</p> 
    323323 
    exarkun@boson:~/doc/mail/tutorial/smtpclient$ 
    379379 
    380380<p>Oops, back to getting a traceback.  This time, the default 
    381381implementation of <code>buildProtocol</code> seems no longer to be 
    382 sufficient.  It instantiates the protocol with no arguments, but 
    383 <code>ESMTPClient</code> wants at least one argument.  In the next 
     382sufficient.  It instantiates the protocol with no arguments, 
     383but <code>ESMTPClient</code> wants at least one argument.  In the next 
    384384version of the client, we'll override <code>buildProtocol</code> to 
    385385fix this problem.</p> 
    386386 
    387387<h3>SMTP Client 6</h3> 
    388388 
    389 <p><a href="smtpclient-6.tac">smtpclient-6.tac</a> introduces a 
    390 <code>twisted.internet.protocol.ClientFactory</code> subclass with an 
    391 overridden <code>buildProtocol</code> method to overcome the problem 
    392 encountered in the previous example.</p> 
     389<p><a href="smtpclient-6.tac">smtpclient-6.tac</a> introduces 
     390a <code>twisted.internet.protocol.ClientFactory</code> subclass with 
     391an overridden <code>buildProtocol</code> method to overcome the 
     392problem encountered in the previous example.</p> 
    393393 
    394394<pre class="python"> 
    395395class SMTPClientFactory(protocol.ClientFactory): 
    will now instantiate <code>SMTPClientFactory</code>:</p> 
    421421smtpClientFactory = SMTPClientFactory() 
    422422</pre> 
    423423 
    424 <p>Running this version of the code, we observe that the code 
    425 <strong>still</strong> isn't quite traceback-free.</p> 
     424<p>Running this version of the code, we observe that the 
     425code <strong>still</strong> isn't quite traceback-free.</p> 
    426426 
    427427<pre class="shell"> 
    428428exarkun@boson:~/doc/mail/tutorial/smtpclient$ twistd -ny smtpclient-6.tac 
    provide that information to it.</p> 
    484484actually includes message data to transmit.  For simplicity's sake, 
    485485the message is defined as part of a new class.  In a useful program 
    486486which sent email, message data might be pulled in from the filesystem, 
    487 a database, or be generated based on user-input.  <a 
    488 href="smtpclient-7.tac">smtpclient-7.tac</a>, however, defines a new 
    489 class, <code>SMTPTutorialClient</code>, with three class attributes 
    490 (<code>mailFrom</code>, <code>mailTo</code>, and 
    491 <code>mailData</code>):</p> 
     487a database, or be generated based on 
     488user-input.  <a href="smtpclient-7.tac">smtpclient-7.tac</a>, however, 
     489defines a new class, <code>SMTPTutorialClient</code>, with three class 
     490attributes (<code>mailFrom</code>, <code>mailTo</code>, 
     491and <code>mailData</code>):</p> 
    492492 
    493493<pre class="python"> 
    494494class SMTPTutorialClient(smtp.ESMTPClient): 
    Twisted is <code>getMailData</code>:</p> 
    552552</pre> 
    553553 
    554554<p>This one is quite simple as well: it returns a file or a file-like 
    555 object which contains the message contents.  In our case, we return a 
    556 <code>StringIO</code> since we already have a string containing our 
    557 message.  If the contents of the file returned by 
    558 <code>getMailData</code> span multiple lines (as email messages often 
    559 do), the lines should be <code>\n</code> delimited (as they would be 
    560 when opening a text file in the <code>"rt"</code> mode): necessary 
    561 newline translation will be performed by <code>SMTPClient</code> 
    562 automatically.</p> 
     555object which contains the message contents.  In our case, we return 
     556a <code>StringIO</code> since we already have a string containing our 
     557message.  If the contents of the file returned 
     558by <code>getMailData</code> span multiple lines (as email messages 
     559often do), the lines should be <code>\n</code> delimited (as they 
     560would be when opening a text file in the <code>"rt"</code> mode): 
     561necessary newline translation will be performed 
     562by <code>SMTPClient</code> automatically.</p> 
    563563 
    564564<p>There is one more new callback method defined in smtpclient-7.tac. 
    565565This one isn't for providing information about the messages to 
    which starts up, connects to a (possibly) remote host, transmits some 
    587587data, and disconnects.  Notably missing, however, is application 
    588588shutdown.  Hitting ^C is fine during development, but it's not exactly 
    589589a long-term solution.  Fortunately, programmatic shutdown is extremely 
    590 simple.  <a href="smtpclient-8.tac">smtpclient-8.tac</a> extends 
    591 <code>sentMail</code> with these two lines:</p> 
     590simple.  <a href="smtpclient-8.tac">smtpclient-8.tac</a> 
     591extends <code>sentMail</code> with these two lines:</p> 
    592592 
    593593<pre class="python"> 
    594594        from twisted.internet import reactor 
    def getMailExchange(host): 
    667667    return defer.succeed('localhost') 
    668668</pre> 
    669669 
    670 <p><code>defer.succeed</code> is a function which creates a new 
    671 <code>Deferred</code> which already has a result, in this case 
    672 <code>'localhost'</code>.  Now we need to adjust our 
    673 <code>TCPClient</code>-constructing code to expect and properly handle 
    674 this <code>Deferred</code>:</p> 
     670<p><code>defer.succeed</code> is a function which creates a 
     671new <code>Deferred</code> which already has a result, in this 
     672case <code>'localhost'</code>.  Now we need to adjust 
     673our <code>TCPClient</code>-constructing code to expect and properly 
     674handle this <code>Deferred</code>:</p> 
    675675 
    676676<pre class="python"> 
    677677def cbMailExchange(exchange): 
    getMailExchange('example.net').addCallback(cbMailExchange) 
    687687scope of this document.  For such a look, see 
    688688the <a href="../../../core/howto/defer.html">Deferred Reference</a>. 
    689689However, in brief, what this version of the code does is to delay the 
    690 creation of the <code>TCPClient</code> until the 
    691 <code>Deferred</code> returned by <code>getMailExchange</code> fires. 
    692 Once it does, we proceed normally through the creation of our 
    693 <code>SMTPClientFactory</code> and <code>TCPClient</code>, as well as 
    694 set the <code>TCPClient</code>'s service parent, just as we did in the 
    695 previous examples.</p> 
     690creation of the <code>TCPClient</code> until the <code>Deferred</code> 
     691returned by <code>getMailExchange</code> fires.  Once it does, we 
     692proceed normally through the creation of 
     693our <code>SMTPClientFactory</code> and <code>TCPClient</code>, as well 
     694as set the <code>TCPClient</code>'s service parent, just as we did in 
     695the previous examples.</p> 
    696696 
    697697<h3>SMTP Client 11</h3> 
    698698 
    699699<p>At last we're ready to perform the mail exchange lookup.  We do 
    700 this by calling on an object provided specifically for this task, 
    701 <code>twisted.mail.relaymanager.MXCalculator</code>:</p> 
     700this by calling on an object provided specifically for this 
     701task, <code>twisted.mail.relaymanager.MXCalculator</code>:</p> 
    702702 
    703703<pre class="python"> 
    704704def getMailExchange(host): 
    def getMailExchange(host): 
    710710<p>Because <code>getMX</code> returns a <code>Record_MX</code> object 
    711711rather than a string, we do a little bit of post-processing to get the 
    712712results we want.  We have already converted the rest of the tutorial 
    713 application to expect a <code>Deferred</code> from 
    714 <code>getMailExchange</code>, so no further changes are required.  <a 
    715 href="smtpclient-11.tac">smtpclient-11.tac</a> completes this tutorial 
    716 by being able to both look up the mail exchange host for the recipient 
    717 domain, connect to it, complete an SMTP transaction, report its 
    718 results, and finally shut down the reactor.</p> 
    719  
    720 <!-- TODO: write a conclusion 
    721  
    722 <h3>Conclusion</h3> 
     713application to expect a <code>Deferred</code> 
     714from <code>getMailExchange</code>, so no further changes are 
     715required.  <a href="smtpclient-11.tac">smtpclient-11.tac</a> completes 
     716this tutorial by being able to both look up the mail exchange host for 
     717the recipient domain, connect to it, complete an SMTP transaction, 
     718report its results, and finally shut down the reactor.</p> 
    723719 
    724 <p>XXX wrap it up</p> 
     720<!-- TODO: write a conclusion to wrap it up --> 
    725721 
    726 --> 
    727722</body> 
    728723</html>