Below is the file 'viewmtn.py' from this revision. You can also download the file.

#!/usr/bin/env python

# Copyright (C) 2005 Grahame Bowland <grahame@angrygoats.net>
#
# This program is made available under the GNU GPL version 2.0 or
# greater. See the accompanying file COPYING for details.
#
# This program is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE.

import os, sys, urllib, types
from itertools import izip, chain, repeat
from urlparse import urljoin
import web
import mtn, handlers, links, branchdiv
from urls import common_urls, perdb_urls, nodefault_urls
from render import Renderer
import config

# purloined from: http://docs.python.org/lib/itertools-recipes.html
def grouper(n, iterable, padvalue=None):
    "grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
    return izip(*[chain(iterable, repeat(padvalue, n-1))]*n)

class RequestContext(object):
    def __init__ (self, **kwargs):
        for k in kwargs:
            setattr(self, k, kwargs[k])
        self.render_attrs = kwargs.keys()
        # make sure that any unread automate output is flushed away
        if not self.ops is None:
            self.ops.per_request()
        if self.dbname is None:
            self.perdb_join = self.nodb_join
        else:
            self.perdb_join = lambda path: urljoin(config.dynamic_uri_path, urllib.quote(self.dbname) + '/' + path)

    def link (self, *args, **kwargs):
        kwargs['ctxt'] = self
        return links.link (*args, **kwargs)

    def render(self, *args, **kwargs):
        for attr in self.render_attrs:
            kwargs[attr] = getattr(self, attr)
        self.renderer.render (self, *args, **kwargs)

class RequestContextFactory(object):
    def __init__ (self):
        self.dbstore = { 'ops' : {}, 'branchdivs' : {}, 'dbdescr' : {}}
        self.db_summary = {}
        self.default = None
        self.nodb_join = lambda path: urljoin(config.dynamic_uri_path, path)
        self.static_join = lambda path: urljoin(config.static_uri_path, path)
        # has the user specified a dbfiles hash? if so, use it
        if hasattr (config, "dbfiles"):
            # is dbfiles a function? if so, call it..
            dbfiles = config.dbfiles
            if isinstance(dbfiles, types.FunctionType):
                dbfiles = dbfiles()
            # we should have something iterable now; if not, abort
            if not hasattr(dbfiles, "__iter__"):
                raise Exception("dbfiles defined incorrectly. It must be an interable (eg. tuple or list) or a function taking no arguments which returns an iterable.")
            for name, dbfile, description in grouper(3, dbfiles):
                self.add_to_store(name, ops=mtn.Operations([config.monotone, dbfile]),
                                        branchdivs=branchdiv.BranchDivisions(),
                                        dbdescr=description)
                self.db_summary[name] = description
            if hasattr (config, "defaultdb"):
                self.default = config.defaultdb
            self.have_multidb = True
        else:
            self.add_to_store(None, ops=mtn.Operations([config.monotone, config.dbfile]),
                                    branchdivs=branchdiv.BranchDivisions(),
                                    dbdescr="")
            self.have_multidb = False
        self.renderer = Renderer(have_multidb=self.have_multidb)

    def add_to_store(self, name, **kwargs):
        for k in kwargs:
            self.dbstore[k][name] = kwargs[k]

    def get_from_store(self, k, name, default=None):
        return self.dbstore[k].get(name, default)

    def __getitem__(self, name):
        if name is None:
            name = self.default
        ops = self.get_from_store("ops", name)
        if ops is None:
            return None
        else:
            dbdescr = self.get_from_store("dbdescr", name)
            branchdivs = self.get_from_store("branchdivs", name)
            return RequestContext(dbname=name,
                                  ops=ops,
                                  dbdescr=dbdescr,
                                  nodb_join=self.nodb_join,
                                  static_join=self.static_join,
                                  branchdivs=branchdivs,
                                  renderer=self.renderer,
                                  db_summary=self.db_summary)

def runfcgi_apache(func):
    web.wsgi.runfcgi(func, None)

def assemble_urls():
    fvars = {}
    urls = ()

    factory = RequestContextFactory()

    def per_db_closure (handler):
        class PerDBClosure(object):
            def GET (self, *args, **kwargs):
                db, other_args = args[0], args[1:]
                # due to regexp we use, db passed in will always have a trailing slash
                if not db is None:
                    db = db[:-1]
                ctxt = factory[db]
                if ctxt is None:
                    return web.notfound()
                return handler.GET (ctxt, *other_args, **kwargs)
        return PerDBClosure

    def nodefault_closure (handler):
        class NoDefaultClosure(object):
            def GET (self, *args, **kwargs):
                return handler.GET (factory, *args, **kwargs)
        return NoDefaultClosure

    def assemble(prefix, url_def, url_fn, the_fn_fn):
        urls = ()
        for url, fn in grouper (2, url_def):
            if hasattr(handlers, fn):
                the_fn = getattr(handlers, fn)
                if url_fn:
                    url = url_fn(url)
                if the_fn_fn:
                    the_fn = the_fn_fn(the_fn)
                mangled = prefix + '_' + fn
                urls += (url, mangled)
                fvars[mangled] = the_fn
            else:
                print >>sys.stderr, "*** URL defined for non-existant handler %s: %s'" % (fn, url)
        return urls

    urls += assemble('c',
                     common_urls,
                     lambda u: r'^/' + u,
                     None)
    if factory.have_multidb and factory.default is None:
        urls += assemble('n',
                         nodefault_urls,
                         lambda u: r'^/' + u,
                         lambda f: nodefault_closure(f()))
    urls += assemble('d',
                     perdb_urls,
                     lambda u: r'^/([A-Za-z]+/)?' + u,
                     lambda f: per_db_closure(f()))

    # import pprint
    # pp = pprint.PrettyPrinter()
    # pp.pprint(urls)
    return urls, fvars

if __name__ == '__main__':
    if hasattr(config, "running_under_apache2") and config.running_under_apache2:
        web.wsgi.runwsgi = runfcgi_apache
    if hasattr(config, "debug") and config.debug:
        web.webapi.internalerror = web.debugerror
    urls, fvars = assemble_urls()
    web.run(urls, fvars)

###
### vi:expandtab:sw=4:ts=4
###