diff --git a/patchwork/templates/patchwork/filters.html b/patchwork/templates/patchwork/filters.html
index 65fd6bf..acb9375 100644
--- a/patchwork/templates/patchwork/filters.html
+++ b/patchwork/templates/patchwork/filters.html
@@ -70,7 +70,7 @@ $(document).ready(function() {
                 return callback();
 
             req = $.ajax({
-                url: '{% url 'patchwork.views.submitter_complete' %}?q=' +
+                url: '{% url 'patchwork.views.api.submitters' %}?q=' +
                       encodeURIComponent(query) + '&l=10',
                 error: function() {
                     callback();
diff --git a/patchwork/templates/patchwork/help/pwclient.html b/patchwork/templates/patchwork/help/pwclient.html
index 7101ec1..793cf73 100644
--- a/patchwork/templates/patchwork/help/pwclient.html
+++ b/patchwork/templates/patchwork/help/pwclient.html
@@ -11,13 +11,13 @@ and applying patches.</p>
 
 <p>To use pwclient, you will need:</p>
 <ul>
- <li>The <a href="{% url 'patchwork.views.pwclient' %}">pwclient</a>
+ <li>The <a href="{% url 'patchwork.views.pwclient.pwclient' %}">pwclient</a>
   program (11kB, python script)</li>
  <li>(optional) a <code>.pwclientrc</code> file in your home directory.</li>
 </ul>
 
 <p>You can create your own <code>.pwclientrc</code> file. Each
-<a href="{% url 'patchwork.views.projects' %}">patchwork project</a>
+<a href="{% url 'patchwork.views.project.list' %}">patchwork project</a>
 provides a sample linked from the 'project info' page.</p>
 
 {% endblock %}
diff --git a/patchwork/templates/patchwork/project.html b/patchwork/templates/patchwork/project.html
index be8cadc..e1cf3f5 100644
--- a/patchwork/templates/patchwork/project.html
+++ b/patchwork/templates/patchwork/project.html
@@ -49,9 +49,9 @@
 </table>
 
 {% if settings.ENABLE_XMLRPC %}
-<p>Sample <a href="{% url 'patchwork.views.help' "pwclient/" %}">patchwork
+<p>Sample <a href="{% url 'patchwork.views.help.help' "pwclient/" %}">patchwork
 client</a> configuration for this project: <a
-href="{% url 'patchwork.views.pwclientrc' project.linkname %}"
+href="{% url 'patchwork.views.pwclient.pwclientrc' project.linkname %}"
 >.pwclientrc</a>.</p>
 {% endif %}
   
diff --git a/patchwork/templatetags/person.py b/patchwork/templatetags/person.py
index 6829019..d35203b 100644
--- a/patchwork/templatetags/person.py
+++ b/patchwork/templatetags/person.py
@@ -38,9 +38,9 @@ def personify(person, project):
     else:
         linktext = escape(person.email)
 
-    url = reverse(
-        'patchwork.views.patch.list', kwargs={'project_id': project.linkname})
-    str = '<a href="%s?%s=%s">%s</a>' % \
-        (url, SubmitterFilter.param, escape(person.id), linktext)
+    url = reverse('patchwork.views.patch.list',
+                  kwargs={'project_id': project.linkname})
+    str = '<a href="%s?%s=%s">%s</a>' % (
+        url, SubmitterFilter.param, escape(person.id), linktext)
 
     return mark_safe(str)
diff --git a/patchwork/tests/test_confirm.py b/patchwork/tests/test_confirm.py
index 4c14e56..7ef922e 100644
--- a/patchwork/tests/test_confirm.py
+++ b/patchwork/tests/test_confirm.py
@@ -25,7 +25,8 @@ from patchwork.models import EmailConfirmation, Person
 
 
 def _confirmation_url(conf):
-    return reverse('patchwork.views.confirm', kwargs={'key': conf.key})
+    return reverse('patchwork.views.confirm',
+                   kwargs={'key': conf.key})
 
 
 class TestUser(object):
diff --git a/patchwork/tests/test_mail_settings.py b/patchwork/tests/test_mail_settings.py
index b9f1dd9..02205aa 100644
--- a/patchwork/tests/test_mail_settings.py
+++ b/patchwork/tests/test_mail_settings.py
@@ -188,7 +188,8 @@ class OptinRequestTest(TestCase):
         self.assertContains(response, self.email)
 
         # check email
-        url = reverse('patchwork.views.confirm', kwargs={'key': conf.key})
+        url = reverse('patchwork.views.confirm',
+                      kwargs={'key': conf.key})
         self.assertEqual(len(mail.outbox), 1)
         msg = mail.outbox[0]
         self.assertEqual(msg.to, [self.email])
diff --git a/patchwork/tests/test_registration.py b/patchwork/tests/test_registration.py
index b5eacef..b5f5bb4 100644
--- a/patchwork/tests/test_registration.py
+++ b/patchwork/tests/test_registration.py
@@ -28,7 +28,8 @@ from patchwork.tests.utils import create_user
 
 
 def _confirmation_url(conf):
-    return reverse('patchwork.views.confirm', kwargs={'key': conf.key})
+    return reverse('patchwork.views.confirm',
+                   kwargs={'key': conf.key})
 
 
 class TestUser(object):
diff --git a/patchwork/tests/test_user.py b/patchwork/tests/test_user.py
index e9d1822..3df5ffa 100644
--- a/patchwork/tests/test_user.py
+++ b/patchwork/tests/test_user.py
@@ -28,7 +28,8 @@ from patchwork.tests.utils import defaults, error_strings
 
 
 def _confirmation_url(conf):
-    return reverse('patchwork.views.confirm', kwargs={'key': conf.key})
+    return reverse('patchwork.views.confirm',
+                   kwargs={'key': conf.key})
 
 
 class TestUser(object):
diff --git a/patchwork/tests/test_xmlrpc.py b/patchwork/tests/test_xmlrpc.py
index d8c77f0..d8e1d98 100644
--- a/patchwork/tests/test_xmlrpc.py
+++ b/patchwork/tests/test_xmlrpc.py
@@ -42,7 +42,7 @@ class XMLRPCTest(LiveServerTestCase):
     def testGetRedirect(self):
         response = self.client.patch(self.url)
         self.assertRedirects(response,
-                             reverse('patchwork.views.help',
+                             reverse('patchwork.views.help.help',
                                      kwargs={'path': 'pwclient/'}))
 
     def testList(self):
diff --git a/patchwork/urls.py b/patchwork/urls.py
index 81a571a..5be286a 100644
--- a/patchwork/urls.py
+++ b/patchwork/urls.py
@@ -29,7 +29,7 @@ urlpatterns = patterns(
     '',
     url(r'^admin/', include(admin.site.urls)),
 
-    (r'^$', 'patchwork.views.projects'),
+    (r'^$', 'patchwork.views.project.list'),
     (r'^project/(?P<project_id>[^/]+)/list/$', 'patchwork.views.patch.list'),
     (r'^project/(?P<project_id>[^/]+)/$', 'patchwork.views.project.project'),
 
@@ -85,7 +85,7 @@ urlpatterns = patterns(
     (r'^confirm/(?P<key>[0-9a-f]+)/$', 'patchwork.views.confirm'),
 
     # submitter autocomplete
-    (r'^submitter/$', 'patchwork.views.submitter_complete'),
+    (r'^submitter/$', 'patchwork.views.api.submitters'),
 
     # email setup
     (r'^mail/$', 'patchwork.views.mail.settings'),
@@ -93,16 +93,16 @@ urlpatterns = patterns(
     (r'^mail/optin/$', 'patchwork.views.mail.optin'),
 
     # help!
-    (r'^help/(?P<path>.*)$', 'patchwork.views.help'),
+    (r'^help/(?P<path>.*)$', 'patchwork.views.help.help'),
 )
 
 if settings.ENABLE_XMLRPC:
     urlpatterns += patterns(
         '',
         (r'xmlrpc/$', 'patchwork.views.xmlrpc.xmlrpc'),
-        (r'^pwclient/$', 'patchwork.views.pwclient'),
+        (r'^pwclient/$', 'patchwork.views.pwclient.pwclient'),
         (r'^project/(?P<project_id>[^/]+)/pwclientrc/$',
-         'patchwork.views.pwclientrc'),
+         'patchwork.views.pwclient.pwclientrc'),
     )
 
 # redirect from old urls
diff --git a/patchwork/views/__init__.py b/patchwork/views/__init__.py
index bf6cad1..3a0c8e9 100644
--- a/patchwork/views/__init__.py
+++ b/patchwork/views/__init__.py
@@ -27,11 +27,14 @@ from email.parser import HeaderParser
 import email.utils
 import re
 
-from .base import *  # noqa
-from patchwork.utils import Order, get_patch_ids, bundle_actions, set_bundle
-from patchwork.paginator import Paginator
+from django.http import Http404
+from django.shortcuts import render_to_response, get_object_or_404
+
 from patchwork.forms import MultiplePatchForm
-from patchwork.models import Comment, Patch
+from patchwork.models import Comment, Patch, EmailConfirmation
+from patchwork.paginator import Paginator
+from patchwork.requestcontext import PatchworkRequestContext
+from patchwork.utils import Order, get_patch_ids, bundle_actions, set_bundle
 
 
 def generic_list(request, project, view,
@@ -220,3 +223,31 @@ def patch_to_mbox(patch):
         mail['Date'] = email.utils.formatdate(utc_timestamp)
 
     return mail
+
+
+def confirm(request, key):
+    import patchwork.views.user
+    import patchwork.views.mail
+
+    views = {
+        'userperson': patchwork.views.user.link_confirm,
+        'registration': patchwork.views.user.register_confirm,
+        'optout': patchwork.views.mail.optout_confirm,
+        'optin': patchwork.views.mail.optin_confirm,
+    }
+
+    conf = get_object_or_404(EmailConfirmation, key=key)
+    if conf.type not in views:
+        raise Http404
+
+    if conf.active and conf.is_valid():
+        return views[conf.type](request, conf)
+
+    context = PatchworkRequestContext(request)
+    context['conf'] = conf
+    if not conf.active:
+        context['error'] = 'inactive'
+    elif not conf.is_valid():
+        context['error'] = 'expired'
+
+    return render_to_response('patchwork/confirm-error.html', context)
diff --git a/patchwork/views/api.py b/patchwork/views/api.py
new file mode 100644
index 0000000..91f4f3d
--- /dev/null
+++ b/patchwork/views/api.py
@@ -0,0 +1,54 @@
+# Patchwork - automated patch tracking system
+# Copyright (C) 2008 Jeremy Kerr <jk@ozlabs.org>
+#
+# This file is part of the Patchwork package.
+#
+# Patchwork is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# Patchwork is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Patchwork; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+import json
+
+from django.db.models import Q
+from django.http import HttpResponse
+
+from patchwork.models import Person
+
+
+def submitters(request):
+    search = request.GET.get('q', '')
+    limit = request.GET.get('l', None)
+
+    if len(search) <= 3:
+        return HttpResponse(content_type="application/json")
+
+    queryset = Person.objects.filter(Q(name__icontains=search) |
+                                     Q(email__icontains=search))
+    if limit is not None:
+        try:
+            limit = int(limit)
+        except ValueError:
+            limit = None
+
+    if limit is not None and limit > 0:
+        queryset = queryset[:limit]
+
+    data = []
+    for submitter in queryset:
+        item = {}
+        item['pk'] = submitter.id
+        item['name'] = submitter.name
+        item['email'] = submitter.email
+        data.append(item)
+
+    return HttpResponse(json.dumps(data), content_type="application/json")
diff --git a/patchwork/views/base.py b/patchwork/views/base.py
deleted file mode 100644
index cd9c43f..0000000
--- a/patchwork/views/base.py
+++ /dev/null
@@ -1,138 +0,0 @@
-# Patchwork - automated patch tracking system
-# Copyright (C) 2008 Jeremy Kerr <jk@ozlabs.org>
-#
-# This file is part of the Patchwork package.
-#
-# Patchwork is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# Patchwork is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Patchwork; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-from __future__ import absolute_import
-
-import json
-
-from django.conf import settings
-from django.core import urlresolvers
-from django.db.models import Q
-from django.http import HttpResponse, HttpResponseRedirect, Http404
-from django.shortcuts import render_to_response, get_object_or_404
-from django.template.loader import render_to_string
-
-from patchwork.models import Project, Person, EmailConfirmation
-from patchwork.requestcontext import PatchworkRequestContext
-
-
-def projects(request):
-    context = PatchworkRequestContext(request)
-    projects = Project.objects.all()
-
-    if projects.count() == 1:
-        return HttpResponseRedirect(
-            urlresolvers.reverse('patchwork.views.patch.list',
-                                 kwargs={'project_id': projects[0].linkname}))
-
-    context['projects'] = projects
-    return render_to_response('patchwork/projects.html', context)
-
-
-def pwclientrc(request, project_id):
-    project = get_object_or_404(Project, linkname=project_id)
-    context = PatchworkRequestContext(request)
-    context.project = project
-    if settings.FORCE_HTTPS_LINKS or request.is_secure():
-        context['scheme'] = 'https'
-    else:
-        context['scheme'] = 'http'
-    response = HttpResponse(content_type="text/plain")
-    response['Content-Disposition'] = 'attachment; filename=.pwclientrc'
-    response.write(render_to_string('patchwork/pwclientrc', context))
-    return response
-
-
-def pwclient(request):
-    context = PatchworkRequestContext(request)
-    response = HttpResponse(content_type="text/x-python")
-    response['Content-Disposition'] = 'attachment; filename=pwclient'
-    response.write(render_to_string('patchwork/pwclient', context))
-    return response
-
-
-def confirm(request, key):
-    import patchwork.views.user
-    import patchwork.views.mail
-    views = {
-        'userperson': patchwork.views.user.link_confirm,
-        'registration': patchwork.views.user.register_confirm,
-        'optout': patchwork.views.mail.optout_confirm,
-        'optin': patchwork.views.mail.optin_confirm,
-    }
-
-    conf = get_object_or_404(EmailConfirmation, key=key)
-    if conf.type not in views:
-        raise Http404
-
-    if conf.active and conf.is_valid():
-        return views[conf.type](request, conf)
-
-    context = PatchworkRequestContext(request)
-    context['conf'] = conf
-    if not conf.active:
-        context['error'] = 'inactive'
-    elif not conf.is_valid():
-        context['error'] = 'expired'
-
-    return render_to_response('patchwork/confirm-error.html', context)
-
-
-def submitter_complete(request):
-    search = request.GET.get('q', '')
-    limit = request.GET.get('l', None)
-
-    if len(search) <= 3:
-        return HttpResponse(content_type="application/json")
-
-    queryset = Person.objects.filter(Q(name__icontains=search) |
-                                     Q(email__icontains=search))
-    if limit is not None:
-        try:
-            limit = int(limit)
-        except ValueError:
-            limit = None
-
-    if limit is not None and limit > 0:
-        queryset = queryset[:limit]
-
-    data = []
-    for submitter in queryset:
-        item = {}
-        item['pk'] = submitter.id
-        item['name'] = submitter.name
-        item['email'] = submitter.email
-        data.append(item)
-
-    return HttpResponse(json.dumps(data), content_type="application/json")
-
-help_pages = {'': 'index.html',
-              'about/': 'about.html',
-              }
-
-if settings.ENABLE_XMLRPC:
-    help_pages['pwclient/'] = 'pwclient.html'
-
-
-def help(request, path):
-    context = PatchworkRequestContext(request)
-    if path in help_pages:
-        return render_to_response(
-            'patchwork/help/' + help_pages[path], context)
-    raise Http404
diff --git a/patchwork/views/help.py b/patchwork/views/help.py
new file mode 100644
index 0000000..cf14fe2
--- /dev/null
+++ b/patchwork/views/help.py
@@ -0,0 +1,43 @@
+# Patchwork - automated patch tracking system
+# Copyright (C) 2008 Jeremy Kerr <jk@ozlabs.org>
+#
+# This file is part of the Patchwork package.
+#
+# Patchwork is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# Patchwork is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Patchwork; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+from __future__ import absolute_import
+
+from django.conf import settings
+from django.http import Http404
+from django.shortcuts import render_to_response
+
+from patchwork.requestcontext import PatchworkRequestContext
+
+
+help_pages = {
+    '': 'index.html',
+    'about/': 'about.html',
+}
+
+if settings.ENABLE_XMLRPC:
+    help_pages['pwclient/'] = 'pwclient.html'
+
+
+def help(request, path):
+    context = PatchworkRequestContext(request)
+    if path in help_pages:
+        return render_to_response(
+            'patchwork/help/' + help_pages[path], context)
+    raise Http404
diff --git a/patchwork/views/project.py b/patchwork/views/project.py
index 82103d4..e3f3747 100644
--- a/patchwork/views/project.py
+++ b/patchwork/views/project.py
@@ -20,12 +20,28 @@
 from __future__ import absolute_import
 
 from django.contrib.auth.models import User
+from django.core import urlresolvers
+from django.http import HttpResponseRedirect
 from django.shortcuts import render_to_response, get_object_or_404
 
 from patchwork.models import Patch, Project
 from patchwork.requestcontext import PatchworkRequestContext
 
 
+def list(request):
+    context = PatchworkRequestContext(request)
+    projects = Project.objects.all()
+
+    if projects.count() == 1:
+        return HttpResponseRedirect(
+            urlresolvers.reverse('patchwork.views.patch.list',
+                                 kwargs={'project_id': projects[0].linkname}))
+
+    context['projects'] = projects
+
+    return render_to_response('patchwork/projects.html', context)
+
+
 def project(request, project_id):
     context = PatchworkRequestContext(request)
     project = get_object_or_404(Project, linkname=project_id)
diff --git a/patchwork/views/pwclient.py b/patchwork/views/pwclient.py
new file mode 100644
index 0000000..b3e0052
--- /dev/null
+++ b/patchwork/views/pwclient.py
@@ -0,0 +1,50 @@
+# Patchwork - automated patch tracking system
+# Copyright (C) 2008 Jeremy Kerr <jk@ozlabs.org>
+#
+# This file is part of the Patchwork package.
+#
+# Patchwork is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# Patchwork is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Patchwork; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+from __future__ import absolute_import
+
+from django.conf import settings
+from django.http import HttpResponse
+from django.shortcuts import get_object_or_404
+from django.template.loader import render_to_string
+
+from patchwork.models import Project
+from patchwork.requestcontext import PatchworkRequestContext
+
+
+def pwclientrc(request, project_id):
+    project = get_object_or_404(Project, linkname=project_id)
+    context = PatchworkRequestContext(request)
+    context.project = project
+    if settings.FORCE_HTTPS_LINKS or request.is_secure():
+        context['scheme'] = 'https'
+    else:
+        context['scheme'] = 'http'
+    response = HttpResponse(content_type="text/plain")
+    response['Content-Disposition'] = 'attachment; filename=.pwclientrc'
+    response.write(render_to_string('patchwork/pwclientrc', context))
+    return response
+
+
+def pwclient(request):
+    context = PatchworkRequestContext(request)
+    response = HttpResponse(content_type="text/x-python")
+    response['Content-Disposition'] = 'attachment; filename=pwclient'
+    response.write(render_to_string('patchwork/pwclient', context))
+    return response
diff --git a/patchwork/views/xmlrpc.py b/patchwork/views/xmlrpc.py
index b9514a4..d928400 100644
--- a/patchwork/views/xmlrpc.py
+++ b/patchwork/views/xmlrpc.py
@@ -30,7 +30,7 @@ except ImportError:
     from xmlrpc.server import XMLRPCDocGenerator
 import sys
 
-from django.core import urlresolvers
+from django.core.urlresolvers import reverse
 from django.contrib.auth import authenticate
 from django.http import (
     HttpResponse, HttpResponseRedirect, HttpResponseServerError)
@@ -136,8 +136,8 @@ dispatcher = PatchworkXMLRPCDispatcher()
 @csrf_exempt
 def xmlrpc(request):
     if request.method not in ['POST', 'GET']:
-        return HttpResponseRedirect(urlresolvers.reverse(
-            'patchwork.views.help', kwargs={'path': 'pwclient/'}))
+        return HttpResponseRedirect(reverse('patchwork.views.help.help',
+                                            kwargs={'path': 'pwclient/'}))
 
     response = HttpResponse()
 
diff --git a/templates/base.html b/templates/base.html
index 2e0b487..b6f7056 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -34,7 +34,7 @@
         <span class="icon-bar"></span>
       </button>
       <span class="navbar-brand"><a
-         href="{% url 'patchwork.views.projects' %}">Patchwork</a>
+         href="{% url 'patchwork.views.project.list' %}">Patchwork</a>
        {% block heading %}{% endblock %}</span>
     </div>
     <div class="collapse navbar-collapse" id="navbar-collapse">
@@ -72,7 +72,7 @@
 {% if project %}
   <div id="breadcrumb">
    <div id="breadcrumb-left">
-     <a href="{% url 'patchwork.views.projects' %}">All projects</a>
+     <a href="{% url 'patchwork.views.project.list' %}">All projects</a>
      &#8594;
      {% block breadcrumb %}
       <a href="{% url 'patchwork.views.patch.list' project_id=project.linkname %}"
@@ -96,7 +96,7 @@
   <div id="footer">
    <a href="http://jk.ozlabs.org/projects/patchwork/">patchwork</a>
    patch tracking system | <a
-   href="{% url 'patchwork.views.help' path="about/" %}">about patchwork</a>
+   href="{% url 'patchwork.views.help.help' path="about/" %}">about patchwork</a>
   </div>
  </body>
 </html>
