Newer
Older
postorius / src / postorius / doc / _build / html / development.html


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">


<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    
    <title>Development &mdash; Postorius 1.0a2 documentation</title>
    
    <link rel="stylesheet" href="_static/default.css" type="text/css" />
    <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
    
    <script type="text/javascript">
      var DOCUMENTATION_OPTIONS = {
        URL_ROOT:    '',
        VERSION:     '1.0a2',
        COLLAPSE_INDEX: false,
        FILE_SUFFIX: '.html',
        HAS_SOURCE:  true
      };
    </script>
    <script type="text/javascript" src="_static/jquery.js"></script>
    <script type="text/javascript" src="_static/underscore.js"></script>
    <script type="text/javascript" src="_static/doctools.js"></script>
    <link rel="top" title="Postorius 1.0a2 documentation" href="index.html" />
    <link rel="prev" title="Installation" href="setup.html" /> 
  </head>
  <body>
    <div class="related">
      <h3>Navigation</h3>
      <ul>
        <li class="right" style="margin-right: 10px">
          <a href="genindex.html" title="General Index"
             accesskey="I">index</a></li>
        <li class="right" >
          <a href="py-modindex.html" title="Python Module Index"
             >modules</a> |</li>
        <li class="right" >
          <a href="setup.html" title="Installation"
             accesskey="P">previous</a> |</li>
        <li><a href="index.html">Postorius 1.0a2 documentation</a> &raquo;</li> 
      </ul>
    </div>  

    <div class="document">
      <div class="documentwrapper">
        <div class="bodywrapper">
          <div class="body">
            
  <div class="section" id="development">
<h1>Development<a class="headerlink" href="#development" title="Permalink to this headline">¶</a></h1>
<p>This is a short guide to help you get started with Postorius development.</p>
<div class="section" id="directory-layout">
<h2>Directory layout<a class="headerlink" href="#directory-layout" title="Permalink to this headline">¶</a></h2>
<p>Postorius is a Django application, so if you have developed with Django before,
the file structure probably looks familiar. These are the basics:</p>
<div class="highlight-python"><pre>__init__.py
auth/                   # Custom authorization code (List owners and
                        # moderators)
context_processors.py   # Some variables available in all templates
doc/                    # Sphinx documentation
fieldset_forms.py       # A custom form class to build html forms with
                        # fieldsets
forms.py                # All kinds of classes to build and validate forms
management/             # Commands to use with Django's manage.py script
models.py               # Code to connect to mailman.client and provide
                        # a Django-style model API for lists, mm-users and
                        # domains
static/                 # Static files (CSS, JS, images)
templates/              # HTML Templates
tests/                  # Postorius test files
urls.py                 # URL routes
utils.py                # Some handy utilities
views/
    views.py            # View classes and functions for all views connected
                        # in urls.py
    generic.py          # Generic class-based views; Currently holds a
                        # class for views based on a single mailing list</pre>
</div>
</div>
<div class="section" id="development-workflow">
<h2>Development Workflow<a class="headerlink" href="#development-workflow" title="Permalink to this headline">¶</a></h2>
<p>The source code is hosted on <a class="reference external" href="https://launchpad.net">Launchpad</a>, which also means that we are using
Bazaar for version control.</p>
<p>Changes are usually not made directly in the project&#8217;s trunk branch, but in
feature-related personal branches, which get reviewed and then merged into
the trunk.</p>
<p>The ideal workflow would be like this:</p>
<ol class="arabic simple">
<li>File a bug to suggest a new feature or report a bug (or just pick one of
the existing bugs).</li>
<li>Create a new branch with your code changes.</li>
<li>Make a &#8220;merge proposal&#8221; to get your code reviewed and merged.</li>
</ol>
<p>Launchpad has a nice <a class="reference external" href="https://launchpad.net/+tour/index">tour</a> which describes all this in detail.</p>
</div>
<div class="section" id="writing-view-code">
<h2>Writing View Code<a class="headerlink" href="#writing-view-code" title="Permalink to this headline">¶</a></h2>
<p>When the work on Postorius was started, the standard way to write view code in
Django was to write a single function for each different view. Since then
Django has introduced so-called &#8216;class-based views&#8217; which make it much easier
to reuse view functionality. While using simple functions is not discuraged, it
makes sense to check if using a class-based approach could make sense.</p>
<p>Check Postorius&#8217; <tt class="docutils literal"><span class="pre">views/views.py</span></tt> and <tt class="docutils literal"><span class="pre">views/generic.py</span></tt> for examples!</p>
</div>
<div class="section" id="authentication-authorization">
<h2>Authentication/Authorization<a class="headerlink" href="#authentication-authorization" title="Permalink to this headline">¶</a></h2>
<p>Three of Django&#8217;s default User roles are relvant for Postorius:</p>
<ul class="simple">
<li>Superuser: Can do everything.</li>
<li>AnonymousUser: Can view list index and info pages.</li>
<li>Authenticated users: Can view list index and info pages. Can (un)subscribe
from lists.</li>
</ul>
<p>Apart from these default roles, there are two others relevant in Postorius:</p>
<ul class="simple">
<li>List owners: Can change list settings, moderate messages and delete their
lists.</li>
<li>List moderators: Can moderate messages.</li>
</ul>
<p>There are a number of decorators to protect views from unauthorized users.</p>
<ul class="simple">
<li><tt class="docutils literal"><span class="pre">&#64;user_passes_test(lambda</span> <span class="pre">u:</span> <span class="pre">u.is_superuser)</span></tt></li>
<li><tt class="docutils literal"><span class="pre">&#64;login_required</span></tt></li>
<li><tt class="docutils literal"><span class="pre">&#64;list_owner_required</span></tt></li>
<li><tt class="docutils literal"><span class="pre">&#64;list_moderator_required</span></tt></li>
</ul>
<p>Check out views/views.py for examples!</p>
</div>
<div class="section" id="accessing-the-mailman-api">
<h2>Accessing the Mailman API<a class="headerlink" href="#accessing-the-mailman-api" title="Permalink to this headline">¶</a></h2>
<p>Postorius uses mailman.client to connect to Mailman&#8217;s REST API. In order to
directly use the client, <tt class="docutils literal"><span class="pre">cd</span></tt> to your project folder and execute
<tt class="docutils literal"><span class="pre">python</span> <span class="pre">manage.py</span> <span class="pre">mmclient</span></tt>. This will open a python shell (IPython, if
that&#8217;s available) and provide you with a client object connected to to your
local Mailman API server (it uses the credentials from your settings.py).</p>
<p>A quick example:</p>
<div class="highlight-python"><pre>$ python manage.py mmclient

&gt;&gt;&gt; client
&lt;Client (user:pwd) http://localhost:8001/3.0/&gt;

&gt;&gt;&gt; print client.system['mailman_version']
GNU Mailman 3.0.0b2+ (Here Again)

&gt;&gt;&gt; mailman_dev = client.get_list('mailman-developers@python.org')
&gt;&gt;&gt; print mailman_dev settings
{u'owner_address': u'mailman-developers@python.org',
 u'default_nonmember_action': u'hold', ...}</pre>
</div>
<p>For detailed information how to use mailman.client, check out its <a class="reference external" href="http://bazaar.launchpad.net/~mailman-coders/mailman.client/trunk/view/head:/src/mailmanclient/docs/using.txt">documentation</a>.</p>
</div>
<div class="section" id="testing">
<h2>Testing<a class="headerlink" href="#testing" title="Permalink to this headline">¶</a></h2>
<p>Currently only some of the Postorius code is covered by a test. We should change that!</p>
<p>All test modules reside in the <tt class="docutils literal"><span class="pre">postorius/src/postorius/tests</span></tt> directory
and this is where you should put your own tests, too. To make the django test
runner find your tests, make sure to add them to the folder&#8217;s <tt class="docutils literal"><span class="pre">__init__.py</span></tt>:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">postorius.tests</span> <span class="kn">import</span> <span class="n">test_utils</span>
<span class="kn">from</span> <span class="nn">postorius.tests.test_list_members</span> <span class="kn">import</span> <span class="n">ListMembersViewTest</span>
<span class="kn">from</span> <span class="nn">postorius.tests.test_list_settings</span> <span class="kn">import</span> <span class="n">ListSettingsViewTest</span>
<span class="kn">from</span> <span class="nn">postorius.tests.my_own_tests</span> <span class="kn">import</span> <span class="n">MyOwnUnitTest</span>

<span class="n">__test__</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">&quot;Test Utils&quot;</span><span class="p">:</span> <span class="n">test_utils</span><span class="p">,</span>
    <span class="s">&quot;List Members&quot;</span><span class="p">:</span> <span class="n">ListMembersViewTest</span><span class="p">,</span>
    <span class="s">&quot;List Settings&quot;</span><span class="p">:</span> <span class="n">ListSettingsViewTest</span><span class="p">,</span>
    <span class="s">&quot;My Own Test&quot;</span><span class="p">:</span> <span class="n">MyOwnUnitTest</span><span class="p">,</span>
<span class="p">}</span>
</pre></div>
</div>
<div class="section" id="running-the-tests">
<h3>Running the tests<a class="headerlink" href="#running-the-tests" title="Permalink to this headline">¶</a></h3>
<p>To run the tests go to your project folder and run <tt class="docutils literal"><span class="pre">python</span> <span class="pre">manage.py</span> <span class="pre">test</span>
<span class="pre">postorius</span></tt> from there.</p>
</div>
<div class="section" id="testing-mailman-client-results">
<h3>Testing mailman.client results<a class="headerlink" href="#testing-mailman-client-results" title="Permalink to this headline">¶</a></h3>
<p>Most of Postorius&#8217; code involves some results from calls to the mailman.client
library. mailman.client is itself covered by tests, so Postorius&#8217; own tests
don&#8217;t need to check if mailman.client returns correct results. Instead we can
just mock them! This has the big advantage that you can run the test suite
without having to worry about the state of the local Mailman database. It also
makes the tests run faster, because we spare ourselves the HTTP calls to the
local Mailman REST API.</p>
<p>This approach has the obvious downside that the Postorius tests will not
recognize any changes to the Mailman API. So at some point there should be some
separate integration tests to test the whole chain. But let&#8217;s not worry about
that for now.</p>
</div>
<div class="section" id="mocking-mailman-client-objects">
<h3>Mocking mailman.client objects<a class="headerlink" href="#mocking-mailman-client-objects" title="Permalink to this headline">¶</a></h3>
<p>Postorius uses Michael Foord&#8217;s <tt class="docutils literal"><span class="pre">mock</span></tt> library for mocking. There are some
shortcuts you can use to quickly create mock objects that behave a little bit
like objects retreived from mailman.client, like:</p>
<ul class="simple">
<li>create_mock_domain</li>
<li>create_mock_list</li>
<li>create_mock_member</li>
</ul>
<p>These <tt class="docutils literal"><span class="pre">create_mock_*</span></tt> functions are very simple tools that return MagigMock objects with the properties passed to them in a dictionary. They also set some defaults for properties that you didn&#8217;t pass to its <tt class="docutils literal"><span class="pre">create_mock_*</span></tt> function. For instance, a mock list created with <tt class="docutils literal"><span class="pre">create_mock_list()</span></tt> will always have <tt class="docutils literal"><span class="pre">members</span></tt>, <tt class="docutils literal"><span class="pre">moderators</span></tt> and <tt class="docutils literal"><span class="pre">owners</span></tt> properties.</p>
<span class="target" id="module-postorius.tests.test_utils"></span><div class="section" id="domains">
<h4>Domains<a class="headerlink" href="#domains" title="Permalink to this headline">¶</a></h4>
<p><tt class="docutils literal"><span class="pre">postorius.tests.utils.create_mock_domain</span></tt> creates a mock domain object:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">properties</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span><span class="n">contact_address</span><span class="o">=</span><span class="s">&#39;postmaster@example.org&#39;</span><span class="p">,</span>
<span class="gp">... </span>                  <span class="n">description</span><span class="o">=</span><span class="s">&#39;Example dot Org&#39;</span><span class="p">,</span>
<span class="gp">... </span>                  <span class="n">mail_host</span><span class="o">=</span><span class="s">&#39;example.org&#39;</span><span class="p">,</span>
<span class="gp">... </span>                  <span class="n">url_host</span><span class="o">=</span><span class="s">&#39;www.example.org&#39;</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">mock_domain</span> <span class="o">=</span> <span class="n">create_mock_domain</span><span class="p">(</span><span class="n">properties</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">mock_domain</span>
<span class="go">&lt;MagicMock name=&#39;Domain&#39; id=&#39;...&#39;&gt;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">mock_domain</span><span class="o">.</span><span class="n">contact_address</span>
<span class="go">postmaster@example.org</span>
<span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">mock_domain</span><span class="o">.</span><span class="n">description</span>
<span class="go">Example dot Org</span>
<span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">mock_domain</span><span class="o">.</span><span class="n">mail_host</span>
<span class="go">example.org</span>
<span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">mock_domain</span><span class="o">.</span><span class="n">url_host</span>
<span class="go">www.example.org</span>
</pre></div>
</div>
</div>
<div class="section" id="mailing-lists">
<h4>Mailing Lists<a class="headerlink" href="#mailing-lists" title="Permalink to this headline">¶</a></h4>
<p><tt class="docutils literal"><span class="pre">postorius.tests.utils.create_mock_list</span></tt> creates a mock list object:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">properties</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span><span class="n">fqdn_listname</span><span class="o">=</span><span class="s">&#39;testlist@example.org&#39;</span><span class="p">,</span>
<span class="gp">... </span>                  <span class="n">mail_host</span><span class="o">=</span><span class="s">&#39;example.org&#39;</span><span class="p">,</span>
<span class="gp">... </span>                  <span class="n">list_name</span><span class="o">=</span><span class="s">&#39;testlist&#39;</span><span class="p">,</span>
<span class="gp">... </span>                  <span class="n">display_name</span><span class="o">=</span><span class="s">&#39;Test List&#39;</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">mock_list</span> <span class="o">=</span> <span class="n">create_mock_list</span><span class="p">(</span><span class="n">properties</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">mock_list</span>
<span class="go">&lt;MagicMock name=&#39;List&#39; id=&#39;...&#39;&gt;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">mock_list</span><span class="o">.</span><span class="n">fqdn_listname</span>
<span class="go">testlist@example.org</span>
<span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">mock_list</span><span class="o">.</span><span class="n">mail_host</span>
<span class="go">example.org</span>
<span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">mock_list</span><span class="o">.</span><span class="n">list_name</span>
<span class="go">testlist</span>
<span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">mock_list</span><span class="o">.</span><span class="n">display_name</span>
<span class="go">Test List</span>
</pre></div>
</div>
</div>
<div class="section" id="memberships">
<h4>Memberships<a class="headerlink" href="#memberships" title="Permalink to this headline">¶</a></h4>
<p><tt class="docutils literal"><span class="pre">postorius.tests.utils.create_mock_list</span></tt> creates a mock list object:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">properties</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span><span class="n">fqdn_listname</span><span class="o">=</span><span class="s">&#39;testlist@example.org&#39;</span><span class="p">,</span>
<span class="gp">... </span>                  <span class="n">address</span><span class="o">=</span><span class="s">&#39;les@example.org&#39;</span><span class="p">,)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">mock_member</span> <span class="o">=</span> <span class="n">create_mock_member</span><span class="p">(</span><span class="n">properties</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">mock_member</span>
<span class="go">&lt;MagicMock name=&#39;Member&#39; id=&#39;...&#39;&gt;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">mock_member</span><span class="o">.</span><span class="n">fqdn_listname</span>
<span class="go">testlist@example.org</span>
<span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">mock_member</span><span class="o">.</span><span class="n">address</span>
<span class="go">les@example.org</span>
</pre></div>
</div>
</div>
</div>
</div>
</div>


          </div>
        </div>
      </div>
      <div class="sphinxsidebar">
        <div class="sphinxsidebarwrapper">
  <h3><a href="index.html">Table Of Contents</a></h3>
  <ul>
<li><a class="reference internal" href="#">Development</a><ul>
<li><a class="reference internal" href="#directory-layout">Directory layout</a></li>
<li><a class="reference internal" href="#development-workflow">Development Workflow</a></li>
<li><a class="reference internal" href="#writing-view-code">Writing View Code</a></li>
<li><a class="reference internal" href="#authentication-authorization">Authentication/Authorization</a></li>
<li><a class="reference internal" href="#accessing-the-mailman-api">Accessing the Mailman API</a></li>
<li><a class="reference internal" href="#testing">Testing</a><ul>
<li><a class="reference internal" href="#running-the-tests">Running the tests</a></li>
<li><a class="reference internal" href="#testing-mailman-client-results">Testing mailman.client results</a></li>
<li><a class="reference internal" href="#mocking-mailman-client-objects">Mocking mailman.client objects</a><ul>
<li><a class="reference internal" href="#domains">Domains</a></li>
<li><a class="reference internal" href="#mailing-lists">Mailing Lists</a></li>
<li><a class="reference internal" href="#memberships">Memberships</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>

  <h4>Previous topic</h4>
  <p class="topless"><a href="setup.html"
                        title="previous chapter">Installation</a></p>
  <h3>This Page</h3>
  <ul class="this-page-menu">
    <li><a href="_sources/development.txt"
           rel="nofollow">Show Source</a></li>
  </ul>
<div id="searchbox" style="display: none">
  <h3>Quick search</h3>
    <form class="search" action="search.html" method="get">
      <input type="text" name="q" />
      <input type="submit" value="Go" />
      <input type="hidden" name="check_keywords" value="yes" />
      <input type="hidden" name="area" value="default" />
    </form>
    <p class="searchtip" style="font-size: 90%">
    Enter search terms or a module, class or function name.
    </p>
</div>
<script type="text/javascript">$('#searchbox').show(0);</script>
        </div>
      </div>
      <div class="clearer"></div>
    </div>
    <div class="related">
      <h3>Navigation</h3>
      <ul>
        <li class="right" style="margin-right: 10px">
          <a href="genindex.html" title="General Index"
             >index</a></li>
        <li class="right" >
          <a href="py-modindex.html" title="Python Module Index"
             >modules</a> |</li>
        <li class="right" >
          <a href="setup.html" title="Installation"
             >previous</a> |</li>
        <li><a href="index.html">Postorius 1.0a2 documentation</a> &raquo;</li> 
      </ul>
    </div>
    <div class="footer">
        &copy; Copyright 2012, The Free Software Foundation.
      Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.2.
    </div>
  </body>
</html>