diff --git a/klaus.py b/klaus.py
index dd0f23f8e0f09d4846068d4f6ef654d900d099b2..c80424e4cffd8b631f0b45d1ad67f48df91980f9 100644
--- a/klaus.py
+++ b/klaus.py
@@ -31,6 +31,7 @@ except IOError:
 
 
 def query_string_to_dict(query_string):
+    """ Transforms a POST/GET string into a Python dict """
     return {k: v[0] for k, v in urlparse.parse_qs(query_string).iteritems()}
 
 class KlausApplication(NanoApplication):
@@ -43,6 +44,15 @@ class KlausApplication(NanoApplication):
         self.jinja_env.globals['KLAUS_VERSION'] = KLAUS_VERSION
 
     def route(self, pattern):
+        """
+        Extends `NanoApplication.route` by multiple features:
+
+        - Overrides the WSGI `HTTP_HOST` by `self.custom_host` (if set)
+        - Tries to use the keyword arguments returned by the view function
+          to render the template called `<class>.html` (<class> being the
+          name of `self`'s class). Raising `Response` can be used to skip
+          this behaviour, directly returning information to Nano.
+        """
         super_decorator = super(KlausApplication, self).route(pattern)
         def decorator(callback):
             @wraps(callback)
@@ -63,6 +73,7 @@ class KlausApplication(NanoApplication):
         return self.jinja_env.get_template(template_name).render(**kwargs)
 
 app = application = KlausApplication(debug=True, default_content_type='text/html')
+# KLAUS_REPOS=/foo/bar/,/spam/ --> {'bar': '/foo/bar/', 'spam': '/spam/'}
 app.repos = {repo.rstrip(os.sep).split(os.sep)[-1]: repo for repo in
              sys.argv[1:] or os.environ.get('KLAUS_REPOS', '').split()}
 
@@ -78,6 +89,7 @@ def pygmentize(code, filename=None, language=None):
 pygments_formatter = HtmlFormatter(linenos=True)
 
 def timesince(when, now=time.time):
+    """ Returns the difference between `when` and `now` in human readable form. """
     delta = now() - when
     result = []
     break_next = False
@@ -130,6 +142,7 @@ def guess_is_image(filename):
     return mime.startswith('image/')
 
 def force_unicode(s):
+    """ Does all kind of magic to turn `s` into unicode """
     if isinstance(s, unicode):
         return s
     try:
@@ -149,9 +162,18 @@ def force_unicode(s):
         raise exc
 
 def extract_author_name(email):
+    """
+    Extracts the name from an email address...
+    >>> extract_author_name("John <john@example.com>")
+    "John"
+
+    ... or returns the address if none is given.
+    >>> extract_author_name("noname@example.com")
+    "noname@example.com"
+    """
     match = re.match('^(.*?)<.*?>$', email)
     if match:
-        return match.group(1)
+        return match.group(1).strip()
     return email
 
 app.jinja_env.filters['u'] = force_unicode
@@ -164,6 +186,12 @@ app.jinja_env.filters['is_image'] = guess_is_image
 app.jinja_env.filters['shorten_author'] = extract_author_name
 
 def subpaths(path):
+    """
+    Yields a `(last part, subpath)` tuple for all possible sub-paths of `path`.
+
+    >>> list(subpaths("foo/bar/spam"))
+    [('foo', 'foo'), ('bar', 'foo/bar'), ('spam', 'foo/bar/spam')]
+    """
     seen = []
     for part in path.split('/'):
         seen.append(part)
@@ -197,6 +225,7 @@ def route(pattern, name=None):
 
 @route('/', 'repo_list')
 class RepoList(BaseView):
+    """ Shows a list of all repos and can be sorted by last update. """
     def view(self):
         self['repos'] = repos = []
         for name in app.repos.iterkeys():
@@ -232,6 +261,7 @@ class BaseRepoView(BaseView):
         return commit, isbranch
 
     def build_url(self, view=None, **kwargs):
+        """ Builds url relative to the current repo + commit """
         if view is None:
             view = self.__class__.__name__
         default_kwargs = {
@@ -246,6 +276,10 @@ class TreeViewMixin(object):
         self['tree'] = self.listdir()
 
     def listdir(self):
+        """
+        Returns a list of directories and files in the current path of the
+        selected commit
+        """
         dirs, files = [], []
         tree, root = self.get_tree()
         for entry in tree.iteritems():
@@ -261,6 +295,7 @@ class TreeViewMixin(object):
         return {'dirs' : dirs, 'files' : files}
 
     def get_tree(self):
+        """ Gets the Git tree of the selected commit and path """
         root = self['path']
         tree = self['repo'].get_tree(self['commit'], root)
         if isinstance(tree, Blob):
@@ -270,6 +305,10 @@ class TreeViewMixin(object):
 
 @route('/:repo:/tree/:commit_id:/(?P<path>.*)', 'history')
 class TreeView(TreeViewMixin, BaseRepoView):
+    """
+    Shows a list of files/directories for the current path as well as all
+    commit history for that path in a paginated form.
+    """
     def view(self):
         super(TreeView, self).view()
         try:
@@ -297,6 +336,7 @@ class BaseBlobView(BaseRepoView):
 
 @route('/:repo:/blob/:commit_id:/(?P<path>.*)', 'view_blob')
 class BlobView(BaseBlobView, TreeViewMixin):
+    """ Shows a single file, syntax highlighted """
     def view(self):
         BaseBlobView.view(self)
         TreeViewMixin.view(self)
@@ -306,6 +346,10 @@ class BlobView(BaseBlobView, TreeViewMixin):
 
 @route('/:repo:/raw/:commit_id:/(?P<path>.*)', 'raw_blob')
 class RawBlob(BaseBlobView):
+    """
+    Shows a single file in raw form
+    (as if it were a normal filesystem file served through a static file server)
+    """
     def view(self):
         super(RawBlob, self).view()
         mime, encoding = self.get_mimetype_and_encoding()
@@ -330,12 +374,18 @@ class RawBlob(BaseBlobView):
 
 @route('/:repo:/commit/:commit_id:/', 'view_commit')
 class CommitView(BaseRepoView):
+    """ Shows a single commit diff """
     def view(self):
         pass
 
 
 @route('/static/(?P<path>.+)', 'static')
 class StaticFilesView(BaseView):
+    """
+    Serves assets (everything under /static/).
+
+    Don't use this in production! Use a static file server instead.
+    """
     def __init__(self, env, path):
         self['path'] = path
         super(StaticFilesView, self).__init__(env)
diff --git a/repo.py b/repo.py
index 752e5fe2fc546a428884842a937fa24a28889d7d..2732a4ae66c3fd33e5c30d4177985ca1ea190281 100644
--- a/repo.py
+++ b/repo.py
@@ -1,14 +1,17 @@
 import os
-from itertools import islice
-try:
-    from cStringIO import StringIO
-except ImportError:
-    from StringIO import StringIO
+import itertools
+import cStringIO
 
 import dulwich, dulwich.patch
 from diff import prepare_udiff
 
 def pairwise(iterable):
+    """
+    Yields the items in `iterable` pairwise:
+
+    >>> list(pairwise(['a', 'b', 'c', 'd']))
+    [('a', 'b'), ('b', 'c'), ('c', 'd')]
+    """
     prev = None
     for item in iterable:
         if prev is not None:
@@ -17,18 +20,24 @@ def pairwise(iterable):
 
 class RepoWrapper(dulwich.repo.Repo):
     def get_branch_or_commit(self, id):
+        """
+        Returns a `(commit_object, is_branch)` tuple for the commit or branch
+        identified by `id`.
+        """
         try:
             return self[id], False
         except KeyError:
             return self.get_branch(id), True
 
     def get_branch(self, name):
+        """ Returns the commit object pointed to by the branch `name`. """
         return self['refs/heads/'+name]
 
     def get_default_branch(self):
         return self.get_branch('master')
 
     def get_branch_names(self):
+        """ Returns a sorted list of branch names. """
         branches = []
         for ref in self.get_refs():
             if ref.startswith('refs/heads/'):
@@ -37,6 +46,7 @@ class RepoWrapper(dulwich.repo.Repo):
         return branches
 
     def get_tag_names(self):
+        """ Returns a sorted list of tag names. """
         tags = []
         for ref in self.get_refs():
             if ref.startswith('refs/tags/'):
@@ -45,6 +55,13 @@ class RepoWrapper(dulwich.repo.Repo):
         return tags
 
     def history(self, commit=None, path=None, max_commits=None, skip=0):
+        """
+        Returns a list of all commits that infected `path`, starting at branch
+        or commit `commit`. `skip` can be used for pagination, `max_commits`
+        to limit the number of commits returned.
+
+        Similar to `git log [branch/commit] [--skip skip] [-n max_commits]`.
+        """
         if not isinstance(commit, dulwich.objects.Commit):
             commit, _ = self.get_branch_or_commit(commit)
         commits = self._history(commit)
@@ -52,9 +69,10 @@ class RepoWrapper(dulwich.repo.Repo):
         if path:
             commits = (c1 for c1, c2 in pairwise(commits)
                        if self._path_changed_between(path, c1, c2))
-        return list(islice(commits, skip, skip+max_commits))
+        return list(itertools.islice(commits, skip, skip+max_commits))
 
     def _history(self, commit):
+        """ Yields all commits that lead to `commit`. """
         if commit is None:
             commit = self.get_default_branch()
         while commit.parents:
@@ -63,6 +81,10 @@ class RepoWrapper(dulwich.repo.Repo):
         yield commit
 
     def _path_changed_between(self, path, commit1, commit2):
+        """
+        Returns `True` if `path` changed between `commit1` and `commit2`,
+        including the case that the file was added or deleted in `commit2`.
+        """
         path, filename = os.path.split(path)
         try:
             blob1 = self.get_tree(commit1, path)
@@ -84,6 +106,7 @@ class RepoWrapper(dulwich.repo.Repo):
         return blob1 != blob2
 
     def get_tree(self, commit, path, noblobs=False):
+        """ Returns the Git tree object for `path` at `commit`. """
         tree = self[commit.tree]
         if path:
             for directory in path.strip('/').split('/'):
@@ -116,7 +139,7 @@ class RepoWrapper(dulwich.repo.Repo):
                 # Dulwich will handle that.
                 pass
 
-            stringio = StringIO()
+            stringio = cStringIO.StringIO()
             dulwich.patch.write_object_diff(stringio, self.object_store,
                                             (oldpath, oldmode, oldsha),
                                             (newpath, newmode, newsha))