<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[Measure Twice]]></title>
  <link href="http://staceysern.github.io/atom.xml" rel="self"/>
  <link href="http://staceysern.github.io/"/>
  <updated>2013-12-01T08:01:40-05:00</updated>
  <id>http://staceysern.github.io/</id>
  <author>
    <name><![CDATA[Stacey Sern]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[Relaying]]></title>
    <link href="http://staceysern.github.io/blog/2013/09/08/relaying/"/>
    <updated>2013-09-08T15:27:00-04:00</updated>
    <id>http://staceysern.github.io/blog/2013/09/08/relaying</id>
    <content type="html"><![CDATA[<p>My latest Twisted adventure began with a comment I came across in <code>relay.py</code>:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="k">class</span> <span class="nc">RelayerMixin</span><span class="p">:</span>
</span><span class='line'>
</span><span class='line'>    <span class="c"># XXX - This is -totally- bogus</span>
</span><span class='line'>    <span class="c"># It opens about a -hundred- -billion- files</span>
</span><span class='line'>    <span class="c"># and -leaves- them open!</span>
</span></code></pre></td></tr></table></div></figure>


<p>This seemed like a worthy problem to investigate so that, at the very least, I
could write a ticket to track the issue.</p>

<p>The first challenge was to set up a smart host configuration with Twisted.
A smart host is a mail server which accepts mail to any address and
then determines the mail exchange for the address and connects to it to relay
the mail.  Unlike an open relay, a smart host imposes restrictions on the
source of messages.  While some may accept mail only from authenticated
senders, Twisted&rsquo;s default is to relay any mail received over a Unix socket or
from localhost.</p>

<p>It was easy enough to run a smart host on my development machine.  I just had
to invoke <code>twistd mail</code> with the relay option and specify a directory to hold
messages to be relayed:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="n">twistd</span> <span class="o">-</span><span class="n">n</span> <span class="n">mail</span> <span class="o">--</span><span class="n">relay</span><span class="o">=/</span><span class="n">tmp</span><span class="o">/</span><span class="n">mail_queue</span>
</span></code></pre></td></tr></table></div></figure>


<p>The smart host uses DNS to look up mail exchanges and
contacts them via SMTP on port 25.  Because my ISP does not allow outgoing
traffic on port 25 and because I did not want to relay test messages to
real mail servers, I needed to make some changes to the Twisted source so that
the email messages would be relayed to a Twisted mail server that I ran on a
second computer.  I modified <code>relaymanager.py</code> to relay to port 8025 and to
use a hosts file for DNS resolution.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="k">class</span> <span class="nc">SmartHostSMTPRelayingManager</span><span class="p">:</span>
</span><span class='line'>    <span class="o">...</span>
</span><span class='line'>    <span class="c"># PORT = 25</span>
</span><span class='line'>    <span class="n">PORT</span> <span class="o">=</span> <span class="mi">8025</span>
</span><span class='line'>    <span class="o">...</span>
</span><span class='line'>    <span class="k">def</span> <span class="nf">_checkStateMX</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span><span class='line'>        <span class="o">...</span>
</span><span class='line'>        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">mxcalc</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
</span><span class='line'>            <span class="c"># self.mxcalc = MXCalculator()</span>
</span><span class='line'>            <span class="kn">from</span> <span class="nn">twisted.names.client</span> <span class="kn">import</span> <span class="n">createResolver</span>
</span><span class='line'>            <span class="n">resolver</span> <span class="o">=</span> <span class="n">createResolver</span><span class="p">(</span><span class="bp">None</span><span class="p">,</span> <span class="bp">None</span><span class="p">,</span> <span class="n">b</span><span class="s">&quot;/tmp/hosts&quot;</span><span class="p">)</span>
</span><span class='line'>            <span class="bp">self</span><span class="o">.</span><span class="n">mxcalc</span> <span class="o">=</span> <span class="n">MXCalculator</span><span class="p">(</span><span class="n">resolver</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>The hosts file maps <code>example.com</code> and <code>example.net</code> to the IP address of the
computer running the target mail server.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="mf">10.224</span><span class="o">.</span><span class="mf">77.149</span> <span class="n">example</span><span class="o">.</span><span class="n">com</span>
</span><span class='line'><span class="mf">10.224</span><span class="o">.</span><span class="mf">77.149</span> <span class="n">example</span><span class="o">.</span><span class="n">net</span>
</span></code></pre></td></tr></table></div></figure>


<p>I configured that server to run on the default port, 8025,
and accept mail for a few users on the domains <code>example.com</code> and <code>example.net</code>:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="n">twistd</span> <span class="o">-</span><span class="n">n</span> <span class="n">mail</span> <span class="o">-</span><span class="n">d</span> <span class="n">example</span><span class="o">.</span><span class="n">com</span><span class="o">=/</span><span class="n">tmp</span><span class="o">/</span><span class="n">example</span><span class="o">.</span><span class="n">com</span> <span class="o">-</span><span class="n">u</span> <span class="n">jim</span><span class="o">=</span><span class="n">pwd</span> <span class="o">-</span><span class="n">u</span> <span class="n">nat</span><span class="o">=</span><span class="n">pwd</span>
</span><span class='line'><span class="o">-</span><span class="n">d</span> <span class="n">example</span><span class="o">.</span><span class="n">net</span><span class="o">=/</span><span class="n">tmp</span><span class="o">/</span><span class="n">example</span><span class="o">.</span><span class="n">net</span> <span class="o">-</span><span class="n">u</span> <span class="n">joe</span><span class="o">=</span><span class="n">pwd</span> <span class="o">-</span><span class="n">u</span> <span class="n">bob</span><span class="o">=</span><span class="n">pwd</span>
</span></code></pre></td></tr></table></div></figure>


<p>When I used telnet on the development machine to send mail to the smart host
running on the same machine and addressed it to one of the configured users on
example.com or example.net, the smart host relayed it to the mail server on the
second machine.</p>

<p>Now that I had a usable configuration, I wanted to explore the implications of
the comment that <code>RelayerMixin</code> opened a large number of files and never closed
them.  <code>RelayerMixin</code> is used to introduce a set of functions for relaying mail
to another class, a relayer, through inheritance. On initialization, the
relayer calls one of the <code>RelayerMixin</code> functions, <code>loadMessages</code>, with a list
of the pathnames of messages which it is responsible for relaying.
<code>loadMessages</code>  opens each message file and stores the file object in a list.
I hypothesized that if I sent a lot of messages to the smart host at once, its
relayers would open files for all the messages and hit the operating system
limit for open files.</p>

<p>I wrote a short program to send the SMTP commands for a series of messages to
the smart host running on port 8025 of the same machine.  The messages are
randomly destined to one of two addresses on each of the two domains served by
the mail server on the other machine.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="kn">from</span> <span class="nn">twisted.internet</span> <span class="kn">import</span> <span class="n">protocol</span><span class="p">,</span> <span class="n">reactor</span>
</span><span class='line'><span class="kn">from</span> <span class="nn">twisted.test.proto_helpers</span> <span class="kn">import</span> <span class="n">LineSendingProtocol</span>
</span><span class='line'><span class="kn">from</span> <span class="nn">twisted.internet.defer</span> <span class="kn">import</span> <span class="n">Deferred</span>
</span><span class='line'><span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">randint</span>
</span><span class='line'>
</span><span class='line'><span class="n">NUM_MESSAGES</span> <span class="o">=</span> <span class="mi">250</span>
</span><span class='line'>
</span><span class='line'><span class="n">addresses</span> <span class="o">=</span> <span class="p">[</span><span class="s">&#39;joe@example.net&#39;</span><span class="p">,</span> <span class="s">&#39;bob@example.net&#39;</span><span class="p">,</span>
</span><span class='line'>             <span class="s">&#39;jim@example.com&#39;</span><span class="p">,</span> <span class="s">&#39;nat@example.com&#39;</span><span class="p">]</span>
</span><span class='line'><span class="n">num_addrs</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">addresses</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span>
</span><span class='line'>
</span><span class='line'><span class="n">msgs</span> <span class="o">=</span> <span class="p">[</span><span class="s">&#39;helo&#39;</span><span class="p">]</span>
</span><span class='line'>
</span><span class='line'><span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">NUM_MESSAGES</span><span class="p">):</span>
</span><span class='line'>    <span class="n">origin</span> <span class="o">=</span> <span class="s">&#39;foo@example.com&#39;</span>
</span><span class='line'>    <span class="n">destination</span> <span class="o">=</span> <span class="n">addresses</span><span class="p">[</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">num_addrs</span><span class="p">)]</span>
</span><span class='line'>    <span class="n">msgs</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s">&#39;mail from: &lt;{}&gt;&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">origin</span><span class="p">))</span>
</span><span class='line'>    <span class="n">msgs</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s">&#39;rcpt to: &lt;{}&gt;&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">destination</span><span class="p">))</span>
</span><span class='line'>    <span class="n">msgs</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s">&#39;data&#39;</span><span class="p">),</span>
</span><span class='line'>    <span class="n">msgs</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s">&#39;from {} to {}&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">origin</span><span class="p">,</span> <span class="n">destination</span><span class="p">)),</span>
</span><span class='line'>    <span class="n">msgs</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s">&#39;hi {}&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">destination</span><span class="p">)),</span>
</span><span class='line'>    <span class="n">msgs</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s">&#39;.&#39;</span><span class="p">),</span>
</span><span class='line'>
</span><span class='line'><span class="n">msgs</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s">&#39;quit&#39;</span><span class="p">)</span>
</span><span class='line'><span class="n">client</span> <span class="o">=</span> <span class="n">LineSendingProtocol</span><span class="p">(</span><span class="n">msgs</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="n">done</span> <span class="o">=</span> <span class="n">Deferred</span><span class="p">()</span>
</span><span class='line'><span class="n">f</span> <span class="o">=</span> <span class="n">protocol</span><span class="o">.</span><span class="n">ClientFactory</span><span class="p">()</span>
</span><span class='line'><span class="n">f</span><span class="o">.</span><span class="n">protocol</span> <span class="o">=</span> <span class="k">lambda</span><span class="p">:</span> <span class="n">client</span>
</span><span class='line'><span class="n">f</span><span class="o">.</span><span class="n">clientConnectionLost</span> <span class="o">=</span> <span class="k">lambda</span> <span class="o">*</span><span class="n">args</span><span class="p">:</span> <span class="n">done</span><span class="o">.</span><span class="n">callback</span><span class="p">(</span><span class="bp">None</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="k">def</span> <span class="nf">finished</span><span class="p">(</span><span class="n">reason</span><span class="p">):</span>
</span><span class='line'>    <span class="n">reactor</span><span class="o">.</span><span class="n">stop</span><span class="p">()</span>
</span><span class='line'>
</span><span class='line'><span class="n">done</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">finished</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="n">reactor</span><span class="o">.</span><span class="n">connectTCP</span><span class="p">(</span><span class="s">&#39;127.0.0.1&#39;</span><span class="p">,</span> <span class="mi">8025</span><span class="p">,</span> <span class="n">f</span><span class="p">)</span>
</span><span class='line'><span class="n">reactor</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</span></code></pre></td></tr></table></div></figure>


<p>As I increased the number of messages sent, I expected to eventually see an
exception occur when too many files were opened but that did not occur no
matter how many messages were sent.  From the server log, I observed
that instead of opening one connection to the mail server for each domain and
sending all the queued messages for that domain, the smart host was repeatedly
connecting to the mail server and sending no more than a few messages at a
time. That explained why the limit on open files was not being reached.  The
relayers were being handed only a few messages at a time so there was no
need to open a lot of files at once.</p>

<p>This strategy for allocating work to relayers did not seem very efficient so I
started exploring further.  <code>SmartHostSMTPRelayingManager</code>, which implements
the smart host functionality, has a function, <code>checkState</code>, which is called
periodically to see if there are messages waiting to be relayed and if there is
capacity to create new relayers.  If so, it calls <code>_checkStateMX</code> to create
relayers and allocate messages to them.  It turns out that <code>_checkStateMX</code>
contains a subtle bug which is the cause of the allocation behavior.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="k">def</span> <span class="nf">_checkStateMX</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span><span class='line'>    <span class="n">nextMessages</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">queue</span><span class="o">.</span><span class="n">getWaiting</span><span class="p">()</span>
</span><span class='line'>    <span class="n">nextMessages</span><span class="o">.</span><span class="n">reverse</span><span class="p">()</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">exchanges</span> <span class="o">=</span> <span class="p">{}</span>
</span><span class='line'>    <span class="k">for</span> <span class="n">msg</span> <span class="ow">in</span> <span class="n">nextMessages</span><span class="p">:</span>
</span><span class='line'>        <span class="n">from_</span><span class="p">,</span> <span class="n">to</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">queue</span><span class="o">.</span><span class="n">getEnvelope</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span>
</span><span class='line'>        <span class="n">name</span><span class="p">,</span> <span class="n">addr</span> <span class="o">=</span> <span class="n">rfc822</span><span class="o">.</span><span class="n">parseaddr</span><span class="p">(</span><span class="n">to</span><span class="p">)</span>
</span><span class='line'>        <span class="n">parts</span> <span class="o">=</span> <span class="n">addr</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s">&#39;@&#39;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
</span><span class='line'>        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">parts</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">2</span><span class="p">:</span>
</span><span class='line'>            <span class="n">log</span><span class="o">.</span><span class="n">err</span><span class="p">(</span><span class="s">&quot;Illegal message destination: &quot;</span> <span class="o">+</span> <span class="n">to</span><span class="p">)</span>
</span><span class='line'>            <span class="k">continue</span>
</span><span class='line'>        <span class="n">domain</span> <span class="o">=</span> <span class="n">parts</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
</span><span class='line'>
</span><span class='line'>        <span class="bp">self</span><span class="o">.</span><span class="n">queue</span><span class="o">.</span><span class="n">setRelaying</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span>
</span><span class='line'>        <span class="n">exchanges</span><span class="o">.</span><span class="n">setdefault</span><span class="p">(</span><span class="n">domain</span><span class="p">,</span> <span class="p">[])</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">queue</span><span class="o">.</span><span class="n">getPath</span><span class="p">(</span><span class="n">msg</span><span class="p">))</span>
</span><span class='line'>        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">exchanges</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">maxConnections</span> <span class="o">-</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">managed</span><span class="p">)):</span>
</span><span class='line'>            <span class="k">break</span>
</span></code></pre></td></tr></table></div></figure>


<p><code>_checkStateMX</code> asks the relay queue for a list of waiting messages.  Then it
loops through the messages, grouping them by target domain. Eventually, each
group will be handed off to a relayer. The problem is that <code>_checkStateMX</code>
breaks out of the loop as soon as it has at least one message for the maximum
number of domains it can concurrently contact.  That value, <code>maxConnections</code>,
is an optional parameter to <code>SmartHostSMTPRelayingManager.__init__</code>.  Its
default value is 2.</p>

<p>As <code>_checkStateMX</code> loops through the waiting messages, it creates a list of
messages for the first domain it sees and keeps adding messages for that domain
to the list.  When it sees a second domain, it creates another list for that
domain but since it has hit the limit on connections, it breaks out of the
loop.  So, any other messages in the queue for either domain must wait to be
sent even though they could be handled by the same relayers.  Instead of
breaking out of the loop when it reaches the connection limit, <code>_checkStateMX</code>
should continue to add messages to the lists for the domains it has already
seen and ignore messages for other domains.</p>

<p>With the understanding of how messages are allocated to relayers, I was now
easily able to trigger an exception for too many open files by sending a
large number of messages to one domain instead of splitting them between two.</p>

<p>As a result of this exploration, I filed and submitted fixes for two issue
tickets, a defect <a href="https://tm.tl/#6719">ticket</a> for the handling of open files
by <code>RelayerMixin</code>, and an enhancement <a href="https://tm.tl/#6717">ticket</a> to improve
how messages are allocated to relayers.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Unit Tests]]></title>
    <link href="http://staceysern.github.io/blog/2013/08/15/unit-tests/"/>
    <updated>2013-08-15T07:04:00-04:00</updated>
    <id>http://staceysern.github.io/blog/2013/08/15/unit-tests</id>
    <content type="html"><![CDATA[<p>One of the first Twisted mail issue tickets I tackled involved  what seemed to
be the simple matter of adding missing unit tests but led to some refactoring
of Twisted code.  The <a href="http://twistedmatrix.com/trac/ticket/6383">ticket</a>
highlights the missing unit tests for the <code>startMessage</code> and <code>exists</code> functions
of both the <code>AbstractMaildirDomain</code> and <code>DomainQueuer</code> classes.</p>

<p><code>AbstractMaildirDomain</code> is a base class which is meant to be subclassed to
create mail domains where the emails are stored in the Maildir format.  Most of
the functions it provides are placeholders that need to be overridden in
subclasses.  However, it does provide implementations for two functions,
<code>exists</code> and <code>startMessage</code>.  <code>exists</code> checks whether a user exists in the
domain or an alias of it and, if so, returns a callable which returns a
<code>MaildirMessage</code> to store a message for the user. Otherwise, it raises an
<code>SMTPBadRcpt</code> exception.  <code>startMessage</code> returns a <code>MaildirMessage</code>
which stores a message for the user.</p>

<p>The existing unit tests for <code>AbstractMaildirDomain</code> were minimal, checking just
that it fully implemented the <code>IAliasableDomain</code> interface.
Additional test cases were needed to verify that:</p>

<ul>
<li><code>startMessage</code> returns a <code>MaildirMessage</code></li>
<li>for a valid user, <code>exists</code> returns a callable which
returns a <code>MaildirMessage</code> and that the callable returns distinct messages
when called multiple times</li>
<li>for an invalid user, <code>exists</code> raises <code>SMTPBadRcpt</code></li>
</ul>


<p><code>AbstractMaildirDomain</code> could not be used directly in testing <code>exists</code> and
<code>startMessage</code> because both call a function which has only a placeholder in the
base class.  So for testing purposes, I created a subclass of
<code>AbstractMaildirDomain</code>, <code>TestMaildirDomain</code>, which overrides the placeholder
functions.</p>

<p>Since each test would need a <code>TestMaildirDomain</code> to exercise, I wrote a <code>setUp</code>
function which runs prior to the test and creates a <code>TestMaildirDomain</code> as well
as a temporary Maildir directory for it to use.  I also added a <code>tearDown</code>
function which runs after each test to remove the temporary directory.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="k">class</span> <span class="nc">AbstractMaildirDomainTestCase</span><span class="p">(</span><span class="n">unittest</span><span class="o">.</span><span class="n">TestCase</span><span class="p">):</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">def</span> <span class="nf">setUp</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span><span class='line'>        <span class="bp">self</span><span class="o">.</span><span class="n">root</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">mktemp</span><span class="p">()</span>
</span><span class='line'>        <span class="bp">self</span><span class="o">.</span><span class="n">service</span> <span class="o">=</span> <span class="n">mail</span><span class="o">.</span><span class="n">mail</span><span class="o">.</span><span class="n">MailService</span><span class="p">()</span>
</span><span class='line'>        <span class="bp">self</span><span class="o">.</span><span class="n">domain</span> <span class="o">=</span> <span class="n">TestMaildirDomain</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">service</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">root</span><span class="p">)</span>
</span><span class='line'>        <span class="bp">self</span><span class="o">.</span><span class="n">domain</span><span class="o">.</span><span class="n">addUser</span><span class="p">(</span><span class="s">&#39;user&#39;</span><span class="p">,</span><span class="s">&quot;&quot;</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">def</span> <span class="nf">tearDown</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span><span class='line'>        <span class="n">shutil</span><span class="o">.</span><span class="n">rmtree</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">root</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>The tests for <code>startMessage</code> and <code>exists</code> turned out to be pretty
straightforward:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="k">def</span> <span class="nf">test_startMessage</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span><span class='line'>    <span class="n">msg</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">domain</span><span class="o">.</span><span class="n">startMessage</span><span class="p">(</span><span class="s">&#39;user@&#39;</span><span class="p">)</span>
</span><span class='line'>    <span class="bp">self</span><span class="o">.</span><span class="n">assertTrue</span><span class="p">(</span><span class="nb">isinstance</span><span class="p">(</span><span class="n">msg</span><span class="p">,</span> <span class="n">maildir</span><span class="o">.</span><span class="n">MaildirMessage</span><span class="p">))</span>
</span><span class='line'>
</span><span class='line'><span class="k">def</span> <span class="nf">test_exists</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span><span class='line'>    <span class="n">f</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">domain</span><span class="o">.</span><span class="n">exists</span><span class="p">(</span><span class="n">mail</span><span class="o">.</span><span class="n">smtp</span><span class="o">.</span><span class="n">User</span><span class="p">(</span><span class="s">&quot;user@&quot;</span><span class="p">,</span> <span class="bp">None</span><span class="p">,</span> <span class="bp">None</span><span class="p">,</span> <span class="bp">None</span><span class="p">))</span>
</span><span class='line'>    <span class="bp">self</span><span class="o">.</span><span class="n">assertTrue</span><span class="p">(</span><span class="nb">callable</span><span class="p">(</span><span class="n">f</span><span class="p">))</span>
</span><span class='line'>    <span class="n">msg1</span> <span class="o">=</span> <span class="n">f</span><span class="p">()</span>
</span><span class='line'>    <span class="bp">self</span><span class="o">.</span><span class="n">assertTrue</span><span class="p">(</span><span class="nb">isinstance</span><span class="p">(</span><span class="n">msg1</span><span class="p">,</span> <span class="n">mail</span><span class="o">.</span><span class="n">maildir</span><span class="o">.</span><span class="n">MaildirMessage</span><span class="p">))</span>
</span><span class='line'>    <span class="n">msg2</span> <span class="o">=</span> <span class="n">f</span><span class="p">()</span>
</span><span class='line'>    <span class="bp">self</span><span class="o">.</span><span class="n">assertTrue</span><span class="p">(</span><span class="n">msg1</span> <span class="o">!=</span> <span class="n">msg2</span><span class="p">)</span>
</span><span class='line'>    <span class="bp">self</span><span class="o">.</span><span class="n">assertTrue</span><span class="p">(</span><span class="n">msg1</span><span class="o">.</span><span class="n">finalName</span> <span class="o">!=</span> <span class="n">msg2</span><span class="o">.</span><span class="n">finalName</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="k">def</span> <span class="nf">test_doesntexist</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span><span class='line'>    <span class="bp">self</span><span class="o">.</span><span class="n">assertRaises</span><span class="p">(</span><span class="n">mail</span><span class="o">.</span><span class="n">smtp</span><span class="o">.</span><span class="n">SMTPBadRcpt</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">domain</span><span class="o">.</span><span class="n">exists</span><span class="p">,</span>
</span><span class='line'>            <span class="n">mail</span><span class="o">.</span><span class="n">smtp</span><span class="o">.</span><span class="n">User</span><span class="p">(</span><span class="s">&quot;nonexistentuser@&quot;</span><span class="p">,</span> <span class="bp">None</span><span class="p">,</span> <span class="bp">None</span><span class="p">,</span> <span class="bp">None</span><span class="p">))</span>
</span></code></pre></td></tr></table></div></figure>


<p>Like <code>AbstractMaildirDomain</code>, <code>DomainQueuer</code> acts as a domain, but instead of
saving emails for users, it puts messages in a queue for relaying. Its
<code>exists</code> and <code>startMessage</code> functions are meant to be used in
the same way as those of <code>AbstractMaildirDomain</code>.   It seemed like it would be
an easy matter to adapt the unit tests for <code>AbstractMaildirDomain</code> to work for
<code>DomainQueuer</code>.</p>

<p>It turned out, however, that the test for <code>exists</code> on <code>DomainQueuer</code> failed
because a function was being called on a variable set to <code>None</code>.  Something
clearly hadn&rsquo;t been properly configured for the test.</p>

<p>Here&rsquo;s the <code>DomainQueuer</code> code being exercised in the test:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="k">class</span> <span class="nc">DomainQueuer</span><span class="p">:</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">def</span> <span class="nf">exists</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user</span><span class="p">):</span>
</span><span class='line'>        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">willRelay</span><span class="p">(</span><span class="n">user</span><span class="o">.</span><span class="n">dest</span><span class="p">,</span> <span class="n">user</span><span class="o">.</span><span class="n">protocol</span><span class="p">):</span>
</span><span class='line'>            <span class="o">...</span>
</span><span class='line'>        <span class="k">raise</span> <span class="n">smtp</span><span class="o">.</span><span class="n">SMTPBadRcpt</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">def</span> <span class="nf">willRelay</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">address</span><span class="p">,</span> <span class="n">protocol</span><span class="p">):</span>
</span><span class='line'>        <span class="n">peer</span> <span class="o">=</span> <span class="n">protocol</span><span class="o">.</span><span class="n">transport</span><span class="o">.</span><span class="n">getPeer</span><span class="p">()</span>
</span><span class='line'>        <span class="k">return</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">authed</span> <span class="ow">or</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">peer</span><span class="p">,</span> <span class="n">UNIXAddress</span><span class="p">)</span> <span class="ow">or</span>
</span><span class='line'>            <span class="n">peer</span><span class="o">.</span><span class="n">host</span> <span class="o">==</span> <span class="s">&#39;127.0.0.1&#39;</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p><code>exists</code> calls <code>willRelay</code> with the <code>protocol</code> which was passed into it as part
of the <code>user</code> argument.  <code>willRelay</code>  tries to call <code>getPeer</code> on the
<code>transport</code> instance variable of the <code>protocol</code>.  But, the minimal <code>User</code>
object passed to <code>exists</code>, which worked for the <code>AbstractMaildirDomain</code> test,
is not sufficient for the <code>DomainQueuer</code> test.  <code>willRelay</code> needs the <code>protocol</code>
to determine whether it should relay the message.</p>

<p>I had two thoughts about how to get around this problem, but I wasn&rsquo;t sure
either of them was satisfactory.  One option would be to provide the <code>User</code>
object with a fake protocol and fake transport so that <code>willRelay</code> could be
configured to return the desired value.  That seemed to be a very roundabout
way to solve the problem of getting <code>willRelay</code> to return a specific boolean
value.</p>

<p>A more direct way to get <code>willRelay</code> to return the desired value would be to
monkeypatch it.  That is, as part of the unit test, to replace the
<code>DomainQueuer.willRelay</code> function with one that returns the desired value.  The
problem with monkeypatching is that, even though <code>willRelay</code> is part of the
public interface of <code>DomainQueuer</code>, it could change in the future.  For
example, it could received additional optional arguments.  Then, the unit
test which used the monkeypatched version might not reflect the behavior of
the real version of <code>willRelay</code>.</p>

<p>I got some great advice about how to approach this problem and unit testing in
general on the Twisted developers IRC channel.  The idea behind unit testing is
to test each unit of code independently.  Here we can consider <code>exists</code> and
<code>willRelay</code> to be two different units.  But the way <code>willRelay</code> is implemented
means that it is difficult to test <code>exists</code> independent of it.  I was advised
that sometimes the best thing to do when adding unit tests is to refactor the
code itself.  So, I attempted to do that without changing the public interface
of <code>DomainQueuer</code>.</p>

<p>I introduced a base class, <code>AbstractRelayRules</code>, whose purpose is to
encapsulate the rules for determining whether a message should be relayed.
Then, I defined a subclass, <code>DomainQueuerRelayRules</code>, which contains the
default rules for the <code>DomainQueuer</code> class.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="k">class</span> <span class="nc">AbstractRelayRules</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
</span><span class='line'>    <span class="sd">&quot;&quot;&quot;</span>
</span><span class='line'><span class="sd">    A base class for relay rules which determine whether a message</span>
</span><span class='line'><span class="sd">    should be relayed.</span>
</span><span class='line'><span class="sd">    &quot;&quot;&quot;</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">def</span> <span class="nf">willRelay</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">address</span><span class="p">,</span> <span class="n">protocol</span><span class="p">,</span> <span class="n">authorized</span><span class="p">):</span>
</span><span class='line'>        <span class="sd">&quot;&quot;&quot;</span>
</span><span class='line'><span class="sd">        Determine whether a message should be relayed.</span>
</span><span class='line'><span class="sd">        &quot;&quot;&quot;</span>
</span><span class='line'>        <span class="k">return</span> <span class="bp">False</span>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'><span class="k">class</span> <span class="nc">DomainQueuerRelayRules</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
</span><span class='line'>    <span class="sd">&quot;&quot;&quot;</span>
</span><span class='line'><span class="sd">    The default relay rules for a DomainQueuer.</span>
</span><span class='line'><span class="sd">    &quot;&quot;&quot;</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">def</span> <span class="nf">willRelay</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">address</span><span class="p">,</span> <span class="n">protocol</span><span class="p">,</span> <span class="n">authorized</span><span class="p">):</span>
</span><span class='line'>        <span class="sd">&quot;&quot;&quot;</span>
</span><span class='line'><span class="sd">        Determine whether a message should be relayed.</span>
</span><span class='line'>
</span><span class='line'><span class="sd">        Relay for all messages received over UNIX sockets or from </span>
</span><span class='line'><span class="sd">        localhost or when the message originator has been authenticated.</span>
</span><span class='line'><span class="sd">        &quot;&quot;&quot;</span>
</span><span class='line'>        <span class="n">peer</span> <span class="o">=</span> <span class="n">protocol</span><span class="o">.</span><span class="n">transport</span><span class="o">.</span><span class="n">getPeer</span><span class="p">()</span>
</span><span class='line'>        <span class="k">return</span> <span class="p">(</span><span class="n">authorized</span> <span class="ow">or</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">peer</span><span class="p">,</span> <span class="n">UNIXAddress</span><span class="p">)</span> <span class="ow">or</span>
</span><span class='line'>            <span class="n">peer</span><span class="o">.</span><span class="n">host</span> <span class="o">==</span> <span class="s">&#39;127.0.0.1&#39;</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>In <code>DomainQueuer</code>, I changed the <code>__init__</code> function to take a new optional
parameter specifying the rules to be used to determine whether to relay.  When
<code>relayRules</code> is not provided, the default <code>DomainQueuerRelayRules</code> is used.  I
also changed the <code>willRelay</code> function to ask the <code>relayRules</code> whether to relay
instead of determining that itself.  Existing code which creates a
<code>DomainQueuer</code> without providing the extra argument works in the exact same way
as the old code.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="k">class</span> <span class="nc">DomainQueuer</span><span class="p">:</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">service</span><span class="p">,</span> <span class="n">authenticated</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span> <span class="n">relayRules</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
</span><span class='line'>        <span class="o">...</span>
</span><span class='line'>        <span class="bp">self</span><span class="o">.</span><span class="n">relayRules</span> <span class="o">=</span> <span class="n">relayRules</span>
</span><span class='line'>        <span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">relayRules</span><span class="p">:</span>
</span><span class='line'>            <span class="bp">self</span><span class="o">.</span><span class="n">relayRules</span> <span class="o">=</span> <span class="n">DomainQueuerRelayRules</span><span class="p">()</span>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>    <span class="k">def</span> <span class="nf">willRelay</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">address</span><span class="p">,</span> <span class="n">protocol</span><span class="p">):</span>
</span><span class='line'>        <span class="sd">&quot;&quot;&quot;</span>
</span><span class='line'><span class="sd">        Determine whether a message should be relayed according</span>
</span><span class='line'><span class="sd">        to the relay rules.</span>
</span><span class='line'><span class="sd">        &quot;&quot;&quot;</span>
</span><span class='line'>        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">relayRules</span><span class="o">.</span><span class="n">willRelay</span><span class="p">(</span><span class="n">address</span><span class="p">,</span> <span class="n">protocol</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">authed</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>Finally, I created another subclass of <code>AbstractRelayRules</code> to be used for
test purposes. <code>TestRelayRules</code> can be configured with a boolean value which
determines whether relaying is allowed or not.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="k">class</span> <span class="nc">TestRelayRules</span><span class="p">(</span><span class="n">mail</span><span class="o">.</span><span class="n">relay</span><span class="o">.</span><span class="n">AbstractRelayRules</span><span class="p">):</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">relay</span><span class="p">):</span>
</span><span class='line'>        <span class="bp">self</span><span class="o">.</span><span class="n">relay</span> <span class="o">=</span> <span class="n">relay</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">def</span> <span class="nf">willRelay</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">address</span><span class="p">,</span> <span class="n">protocol</span><span class="p">,</span> <span class="n">authorized</span><span class="p">):</span>
</span><span class='line'>        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">relay</span>
</span></code></pre></td></tr></table></div></figure>


<p>Now, the unit tests for <code>DomainQueuer.exists</code> using <code>TestRelayRules</code> are quite
simple.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="k">class</span> <span class="nc">DomainQueuerTestCase</span><span class="p">(</span><span class="n">unittest</span><span class="o">.</span><span class="n">TestCase</span><span class="p">):</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">def</span> <span class="nf">test_exists</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span><span class='line'>        <span class="n">dq</span> <span class="o">=</span> <span class="n">mail</span><span class="o">.</span><span class="n">relay</span><span class="o">.</span><span class="n">DomainQueuer</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">service</span><span class="p">,</span> <span class="bp">False</span><span class="p">,</span> <span class="n">TestRelayRules</span><span class="p">(</span><span class="bp">True</span><span class="p">))</span>
</span><span class='line'>        <span class="n">f</span> <span class="o">=</span> <span class="n">dq</span><span class="o">.</span><span class="n">exists</span><span class="p">(</span><span class="n">mail</span><span class="o">.</span><span class="n">smtp</span><span class="o">.</span><span class="n">User</span><span class="p">(</span><span class="s">&quot;user@&quot;</span><span class="p">,</span> <span class="bp">None</span><span class="p">,</span> <span class="bp">None</span><span class="p">,</span> <span class="bp">None</span><span class="p">))</span>
</span><span class='line'>        <span class="bp">self</span><span class="o">.</span><span class="n">assertTrue</span><span class="p">(</span><span class="nb">callable</span><span class="p">(</span><span class="n">f</span><span class="p">))</span>
</span><span class='line'>        <span class="n">msg1</span> <span class="o">=</span> <span class="n">f</span><span class="p">()</span>
</span><span class='line'>        <span class="bp">self</span><span class="o">.</span><span class="n">assertTrue</span><span class="p">(</span><span class="nb">isinstance</span><span class="p">(</span><span class="n">msg1</span><span class="p">,</span> <span class="n">mail</span><span class="o">.</span><span class="n">mail</span><span class="o">.</span><span class="n">FileMessage</span><span class="p">))</span>
</span><span class='line'>        <span class="n">msg2</span> <span class="o">=</span> <span class="n">f</span><span class="p">()</span>
</span><span class='line'>        <span class="bp">self</span><span class="o">.</span><span class="n">assertTrue</span><span class="p">(</span><span class="n">msg1</span> <span class="o">!=</span> <span class="n">msg2</span><span class="p">)</span>
</span><span class='line'>        <span class="bp">self</span><span class="o">.</span><span class="n">assertTrue</span><span class="p">(</span><span class="n">msg1</span><span class="o">.</span><span class="n">finalName</span> <span class="o">!=</span> <span class="n">msg2</span><span class="o">.</span><span class="n">finalName</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">def</span> <span class="nf">test_doesntexist</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span><span class='line'>        <span class="n">dq</span> <span class="o">=</span> <span class="n">mail</span><span class="o">.</span><span class="n">relay</span><span class="o">.</span><span class="n">DomainQueuer</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">service</span><span class="p">,</span> <span class="bp">False</span><span class="p">,</span> <span class="n">TestRelayRules</span><span class="p">(</span><span class="bp">False</span><span class="p">))</span>
</span><span class='line'>        <span class="bp">self</span><span class="o">.</span><span class="n">assertRaises</span><span class="p">(</span><span class="n">mail</span><span class="o">.</span><span class="n">smtp</span><span class="o">.</span><span class="n">SMTPBadRcpt</span><span class="p">,</span> <span class="n">dq</span><span class="o">.</span><span class="n">exists</span><span class="p">,</span>
</span><span class='line'>                <span class="n">mail</span><span class="o">.</span><span class="n">smtp</span><span class="o">.</span><span class="n">User</span><span class="p">(</span><span class="s">&quot;user@&quot;</span><span class="p">,</span> <span class="bp">None</span><span class="p">,</span> <span class="bp">None</span><span class="p">,</span> <span class="bp">None</span><span class="p">))</span>
</span></code></pre></td></tr></table></div></figure>


<p>Refactoring to decouple the rules for relaying messages from the
<code>DomainQueuer</code> certainly made the unit testing code much cleaner.  As a general
matter, difficulty in writing unit tests may highlight dependencies in the code
which should be refactored.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Types]]></title>
    <link href="http://staceysern.github.io/blog/2013/08/01/types/"/>
    <updated>2013-08-01T14:42:00-04:00</updated>
    <id>http://staceysern.github.io/blog/2013/08/01/types</id>
    <content type="html"><![CDATA[<p>Much of the work of documenting the Twisted Mail API has involved searching
through the Python code to determine the types for parameters and return values.
It often involves comparing functions in different classes which inherit from
the same base class or implement the same interface.
In some cases, I&rsquo;ve resorted to looking at unit tests or example code to see
how objects are used.
After a recent experience while tracking down types, I&rsquo;m more convinced than
ever of the value of the API documentation.</p>

<p>I was documenting the <code>alias</code> module, which contains classes for redirecting
mail from one user to another user, to a file, to a process, and to a group of
aliases. Four different classes inherit from the base class <code>AliasBase</code>
and implement the interface <code>IAlias</code>, which contains the function
<code>createMessageReceiver</code>.
The class hierarchy looks like this:</p>

<pre>
twisted.mail.alias.AliasBase
    twisted.mail.alias.AddressAlias
    twisted.mail.alias.AliasGroup
    twisted.mail.alias.FileAlias
    twisted.mail.alias.ProcessAlias
</pre>


<p>I was trying to determine the return value of <code>IAlias.createMessageReceiver</code>.
The return value was clear for three of the four classes that implement
<code>IAlias</code> because the object to be returned was created in the return statement.</p>

<pre>
FileAlias -> FileWrapper 
ProcessAlias -> MessageWrapper   
AliasGroup -> MultiWrapper 
</pre>


<p>The objects returned are all message receivers which implement the
<code>smtp.IMessage</code> interface.  They deliver a message to the appropriate place:
a file, a process or a group of message receivers.
It seemed pretty clear that the return value of the <code>createMessageReceiver</code>
function in the <code>IAlias</code> interface should be <code>smtp.IMessage</code>.
However, there was one more class that implemented the interface,
<code>AddressAlias</code>, and the return value from that wasn&rsquo;t so clear.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="k">class</span> <span class="nc">AddressAlias</span><span class="p">(</span><span class="n">AliasBase</span><span class="p">):</span>
</span><span class='line'>    <span class="n">implements</span><span class="p">(</span><span class="n">IAlias</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">alias</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">):</span>
</span><span class='line'>        <span class="n">AliasBase</span><span class="o">.</span><span class="n">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">)</span>
</span><span class='line'>        <span class="bp">self</span><span class="o">.</span><span class="n">alias</span> <span class="o">=</span> <span class="n">smtp</span><span class="o">.</span><span class="n">Address</span><span class="p">(</span><span class="n">alias</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">def</span> <span class="nf">createMessageReceiver</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span><span class='line'>        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">domain</span><span class="p">()</span><span class="o">.</span><span class="n">exists</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">alias</span><span class="p">))</span>
</span></code></pre></td></tr></table></div></figure>


<p><code>AddressAlias.createMessageReceiver</code> returns the result of a call to <code>exists</code>
on the result of a call to <code>domain</code>.
<code>domain</code> is a base class function which returns an object which implements the
<code>IDomain</code> interface.
Fortunately, the <code>IDomain</code> interface was documented.
It returns a callable which takes no arguments and returns an object
implementing <code>IMessage</code>.
Unfortunately, this return value didn&rsquo;t match the pattern of the other three
classes implementing <code>IAlias.createMessageReceiver</code>, all of which return an
object implementing <code>IMessage</code>.</p>

<p>Although messy, it was possible that the return value of
<code>IAlias.createMessageReceiver</code>
was either an <code>smtp.IMessage</code> provider or a callable which takes no arguments
and returns an <code>smtp.IMessage</code> provider.
Or, it might have been a mistake.</p>

<p>At this point, I fortuitously happened to be looking at this code in an old
branch and noticed a difference. There, the
<code>AddressAlias.createMessageReceiver</code> function appeared as follows:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="k">def</span> <span class="nf">createMessageReceiver</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span><span class='line'>    <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">domain</span><span class="p">()</span><span class="o">.</span><span class="n">startMessage</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">alias</span><span class="p">))</span>
</span></code></pre></td></tr></table></div></figure>


<p>After some investigation, I found a
<a href="https://twistedmatrix.com/trac/ticket/4151#4151">ticket</a> that had been fixed
earlier this year to remove calls to the deprecated <code>IDomain.startMessage</code>
function.
In the old code, <code>startMessage</code> also returns an <code>IMessage</code> provider.
So, it seemed that a bug had been introduced in the switch from <code>startMessage</code>
to <code>exists</code>.</p>

<p>The result of the call to <code>exists</code> must be invoked to get the proper message
receiver to return. The code should read:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="k">def</span> <span class="nf">createMessageReceiver</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span><span class='line'>    <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">domain</span><span class="p">()</span><span class="o">.</span><span class="n">exists</span><span class="p">(</span><span class="n">User</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">alias</span><span class="p">),</span> <span class="bp">None</span><span class="p">,</span> <span class="bp">None</span><span class="p">,</span> <span class="bp">None</span><span class="p">)()</span>
</span></code></pre></td></tr></table></div></figure>


<p>I filed a <a href="https://twistedmatrix.com/trac/ticket/6638#6638">ticket</a> in the
issue tracking system and subsequently submitted a fix.
While reworking the unit tests, I relied heavily on the API documentation I had
written for the <code>alias</code> module.
I think it&rsquo;s safe to say that had the API been fully documented when the
original change was made, this error would have been easy to spot during code
review or to avoid in the first place.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[API Documentation]]></title>
    <link href="http://staceysern.github.io/blog/2013/07/18/api-documentation/"/>
    <updated>2013-07-18T16:15:00-04:00</updated>
    <id>http://staceysern.github.io/blog/2013/07/18/api-documentation</id>
    <content type="html"><![CDATA[<p>My focus for the past few weeks has been on reviewing the API
documentation for Twisted Mail and filling in the missing pieces.
Why bother with documentation when there are bugs to fix and code to write?
The quality of API documentation directly affects the experience that a
developer has employing or further developing a library.
It can be the difference between a piece of software being successfully integrated
into a larger project or being discarded because the user just can&rsquo;t figure out
how
it works.</p>

<p>The <a href="http://twistedmatrix.com/documents/current/api/">Twisted API documentation</a>
is automatically generated from comments embedded in the source code.
The <a href="http://twistedmatrix.com/documents/current/core/development/policy/coding-standard.html">Twisted Coding Standards</a>
dictate that all methods, functions, classes, and modules have docstrings which
describe their purpose and detail all parameters and attributes.
The docstrings are written in
<a href="http://epydoc.sourceforge.net/manual-epytext.html">Epytext Markup Language</a> and are processed
by pydoctor, an API documentation generator, to produce the API documentation.
Epytext provides a way to specify formatting information, to annotate
parameters, return values, and their types, and to create cross-reference links.
Here&rsquo;s an example of a docstring written in Epytext:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="k">def</span> <span class="nf">exists</span><span class="p">(</span><span class="n">address</span><span class="p">,</span> <span class="n">domain</span><span class="p">):</span>
</span><span class='line'>    <span class="sd">&quot;&quot;&quot;</span>
</span><span class='line'><span class="sd">    Checks whether a user exists in a domain.</span>
</span><span class='line'>
</span><span class='line'><span class="sd">    @param address: The user</span>
</span><span class='line'><span class="sd">    @type address: L{Address}</span>
</span><span class='line'>
</span><span class='line'><span class="sd">    @param domain: The domain</span>
</span><span class='line'><span class="sd">    @type domain: L{IDomain}</span>
</span><span class='line'>
</span><span class='line'><span class="sd">    @rtype: C{bool}</span>
</span><span class='line'><span class="sd">    @return: C{True} if the user exists in the domain, </span>
</span><span class='line'><span class="sd">             C{False} otherwise</span>
</span><span class='line'><span class="sd">    &quot;&quot;&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<p>And here’s the API documentation that would be generated from that docstring:</p>

<p><img src="http://staceysern.github.io/images/exists.png"></p>

<p>Currently, a good deal of Twisted Mail’s methods, functions, and classes are
missing docstrings. Even those methods and functions that have docstrings
often lack a full description of parameters, return values and their types.
My task is to provide that missing information.  It&rsquo;s a great opportunity to
dive deeply into the code.  And, once the code is fully documented, I&rsquo;ll find
it much easier to fix bugs and introduce new features.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Tutorials]]></title>
    <link href="http://staceysern.github.io/blog/2013/06/30/tutorials/"/>
    <updated>2013-06-30T20:37:00-04:00</updated>
    <id>http://staceysern.github.io/blog/2013/06/30/tutorials</id>
    <content type="html"><![CDATA[<p>If there were a theme for my first two weeks as an Outreach Program for Women
intern, it would be tutorials. As my project, I&rsquo;m maintaining the mail portion
of Twisted, an event-driven, networking framework in Python. I&rsquo;ll be working to
improve the documentation, make progress on partially completed tickets, and
attack some new defect and enhancement tickets. Up until now, I had used several
aspects of Twisted in a project and submitted a couple of patches, but I hadn&rsquo;t
used the mail subproject and wasn&rsquo;t familiar with any of the mail protocols
that Twisted supports. Working through the mail example code and tutorials
seemed like a good introduction and would allow me to evaluate how well the
documentation meets the needs of new users. Further, there were a few
outstanding tickets related to documentation that I&rsquo;d be able to work on
straightaway.</p>

<p>I found immediate success in running the examples of clients that use the
IMAP4 protocol to access email from a remote server. I was also able to run a
simple email server that uses SMTP to receive messages, although I had to read
the source code to figure out from which address it would accept messages. When
it came to the examples of clients which use SMTP to send messages, nothing
worked.</p>

<p>SMTP is a text-based protocol in which a client issues a series of commands and
receives replies from a server in order to send mail. To investigate the
problem with the SMTP client examples, I thought to use telnet to connect
directly to the email server and issue the commands manually. Not only couldn&rsquo;t
I connect with telnet to the servers used in the examples, I couldn&rsquo;t even
connect to a Google server. In all of these cases, I was attempting to connect
on port 25, which is the well-known port for SMTP. My OPW mentor, Jessica
McKellar, theorized that the problem was caused by my ISP blocking outbound
traffic for port 25. Verizon&rsquo;s website confirms that they do this to prevent
virus-infected computers from sending spam.</p>

<p>With this problem in mind, I moved on to the tutorial on how to build SMTP
clients. I ran into the same problem that had earlier prompted a
<a href="http://twistedmatrix.com/trac/ticket/5685">trouble ticket</a>. The
tutorial requires a server running on port 25 of localhost but it does not
explicitly mention this requirement nor does it give directions for running
such a server. A patch had been submitted to explicitly mention the requirement
and suggest that the user could change the example to use an external SMTP
server. A review of that patch had found no problem with the change in wording.
The review, however, raised an ancillary issue, noting that the output listed
in the tutorial did not match what was actually generated by running the
examples. This same complaint was documented in another open
<a href="http://twistedmatrix.com/trac/ticket/6476">ticket</a>.</p>

<p>I wasn&rsquo;t satisfied with the proposed solution, since the user might well be
blocked from contacting an external server on port 25. I thought the best
solution might be to include directions on running a server locally, to be used
with the tutorial. With my limited knowledge of Twisted mail, I modified the
example SMTP server to run on port 25 and accept mail from any user. There were
some quirky requirements involved in running this server, but I was able to get
through the tutorial with it.</p>

<p>Although I had gotten the tutorial working with a local server, I suspected
that my solution might not be the most elegant. Rather than submit a patch and
upon review learn of a better way to solve the problem, I thought it prudent to
consult with the Twisted developer community on the twisted-dev IRC channel. I
learned that I could use the twistd utility to start up a mail server with its
configuration, including the port, specified through command-line flags.</p>

<p>When I mentioned that I planned to update the output listing in the tutorial in
response to both tickets, one developer suggested that was futile since the
output would vary based on release and platform and that it would be better
just to mention in the tutorial that the listing was representative. He then
closed the second ticket, explaining why it wouldn&rsquo;t be fixed.</p>

<p>In the discussion, one of the developers asked me to amend the first ticket to
summarize the problem and specify the completion condition. I specified that
the solution was to make the tutorial self-contained so that a user would need
no external knowledge to work through it. Then I revised the tutorial to make
it so. At the same time, I fixed some issues of formatting, punctuation,
grammar, clarity, and accuracy.</p>

<p>Before I could submit a patch, I needed to build the documentation. Similar to
the process of building code, tools must be run on the raw form of the tutorial
to produce nicely formatted output. Twisted includes a documentation generator
tool called lore which translates XHTML input with annotations to indicate,
among other things, links to API documentation and code listings into HTML
output.</p>

<p>My patch received a quick review. In addition to some requests for minor
changes and clarifications, the review included some comments which seemed to
suggest that the examples used in the the tutorial might not best represent how
to write an SMTP client. I was concerned because the ticket was currently
limited in scope to making the tutorial self-contained. Changing the examples,
while possibly an improvement, would require significantly reworking the
tutorial. In the IRC channel, I asked for confirmation that this was what was
being suggested and if so, should it be done as part of the same ticket. The
consensus was that the suggestion constituted scope creep and should be
addressed in a separate ticket. Now, I am left to submit a few minor revisions
to hopefully close out the ticket.</p>

<p>If that weren&rsquo;t enough in the way of tutorials, I also tackled a new tutorial
on building SMTP servers. Comments had been sitting in the
<a href="http://twistedmatrix.com/trac/ticket/3324">ticket</a> for nine
months. In addition to incorporating the comments, I edited the document and
modified the presentation of some examples to hopefully make them more clear. I
found the final example, which is meant to explain how to use the
<code>IMessageDeliveryFactory</code> interface, to be lacking and wrote a new example to
better showcase it. The changes are nearly ready to be submitted.</p>

<p>Work on these tutorials will continue but my focus is now turning to another
area of documentation &ndash; the API. More on that to come.</p>
]]></content>
  </entry>
  
</feed>
