# # # rename "MochiKit" # to "static/MochiKit" # # rename "about.psp" # to "old_version/about.psp" # # rename "branch.psp" # to "old_version/branch.psp" # # rename "diff.psp" # to "old_version/diff.psp" # # rename "error.psp" # to "old_version/error.psp" # # rename "file.psp" # to "old_version/file.psp" # # rename "fileinbranch.psp" # to "old_version/fileinbranch.psp" # # rename "headofbranch.psp" # to "old_version/headofbranch.psp" # # rename "help.psp" # to "old_version/help.psp" # # rename "index.psp" # to "old_version/index.psp" # # rename "manifest.psp" # to "old_version/manifest.psp" # # rename "revision.psp" # to "old_version/revision.psp" # # rename "rss_feed.gif" # to "static/rss_feed.gif" # # rename "tags.psp" # to "old_version/tags.psp" # # rename "tarofbranch.psp" # to "old_version/tarofbranch.psp" # # rename "viewmtn.css" # to "static/viewmtn.css" # # rename "viewmtn.js" # to "static/viewmtn.js" # # add_dir "old_version" # # add_dir "static" # # patch "mtn.py" # from [402aba6497fb89242b82920b3a3222fbf7088eaf] # to [56f9a515d67b8516d43b1b65cadd2183166b04ad] # # patch "viewmtn.py" # from [fb9d3d9fa86e7ae6b4e9ee3be5ac53fa944c6147] # to [7728db1166843eaeee789a1c821dcc4f804c96c7] # ============================================================ --- mtn.py 402aba6497fb89242b82920b3a3222fbf7088eaf +++ mtn.py 56f9a515d67b8516d43b1b65cadd2183166b04ad @@ -8,7 +8,7 @@ import web import popen2 import web -debug = web.debug +from web import debug # regular expressions that are of general use when # validating monotone output @@ -25,32 +25,32 @@ class Revision(str): class Revision(str): def __init__(self, v): - str.__init__(v) - if not revision_re_c.match(self): - raise MonotoneException("Not a valid revision ID") + str.__init__(v) + if not revision_re_c.match(self): + raise MonotoneException("Not a valid revision ID") def abbrev(self): - return '[' + self[:8] + '..]' + return '[' + self[:8] + '..]' def set_nonblocking(fd): fl = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NDELAY) def terminate_popen3(process): - debug("%s stopping %s" % (os.getpid(), process.pid)) + debug("[%s] stopping process: %s" % (os.getpid(), process.pid)) try: process.tochild.close() process.fromchild.close() - process.childerr.close() + process.childerr.close() if process.poll() == -1: # the process is still running, so kill it. os.kill(process.pid, signal.SIGKILL) process.wait() except: - debug("%s failed_to_stop %s" % (os.getpid(), self.process.pid)) + debug("%s failed_to_stop %s" % (os.getpid(), self.process.pid)) class Runner: def __init__(self, monotone, database): - self.base_command = [monotone, "--db=%s" % pipes.quote(database)] + self.base_command = [monotone, "--db=%s" % pipes.quote(database)] packet_header_re = re.compile(r'^(\d+):(\d+):([lm]):(\d+):') import web @@ -58,133 +58,136 @@ class Automate(Runner): class Automate(Runner): """Runs commands via a particular monotone process. This process is started the first time run() is called, and - stopped when this class instance is deleted. + stopped when this class instance is deleted or the stop() + method is called. If an error occurs, the monotone process may need to be stopped and a new one created. """ def __init__(self, *args, **kwargs): - Runner.__init__(*[self] + list(args), **kwargs) - self.lock = threading.Lock() - self.process = None + Runner.__init__(*[self] + list(args), **kwargs) + self.lock = threading.Lock() + self.process = None def stop(self): if not self.process: - return - terminate_popen3(self.process) + return + terminate_popen3(self.process) def __process_required(self): - if self.process != None: - return - to_run = self.base_command + ['automate', 'stdio'] - self.process = popen2.Popen3(to_run, capturestderr=True) - map (set_nonblocking, [ self.process.fromchild, - self.process.tochild, - self.process.childerr ]) + if self.process != None: + return + to_run = self.base_command + ['automate', 'stdio'] + self.process = popen2.Popen3(to_run, capturestderr=True) + map (set_nonblocking, [ self.process.fromchild, + self.process.tochild, + self.process.childerr ]) def run(self, *args, **kwargs): - if not self.lock.acquire(False): - raise MonotoneException("Automate process can't be called: it is already locked.") - try: - rv = apply(self.__run, args, kwargs) - finally: - self.lock.release() - return rv + debug(("automate is running:", args, kwargs)) + if not self.lock.acquire(False): + raise MonotoneException("Automate process can't be called: it is already locked.") + try: + rv = apply(self.__run, args, kwargs) + finally: + self.lock.release() + return rv def __run(self, command, args): - self.__process_required() + self.__process_required() enc = "l%d:%s" % (len(command), command) enc += ''.join(map(lambda x: "%d:%s" % (len(x), x), args)) + 'e' - self.process.tochild.write(enc) - self.process.tochild.flush() + self.process.tochild.write(enc) + self.process.tochild.flush() - import sys - def read_result_packets(): - buffer = "" - while True: - r_stdin, r_stdout, r_stderr = select.select([self.process.fromchild], [], [], None) - if not r_stdin and not r_stdout and not r_stderr: - break + import sys + def read_result_packets(): + buffer = "" + while True: + r_stdin, r_stdout, r_stderr = select.select([self.process.fromchild], [], [], None) + if not r_stdin and not r_stdout and not r_stderr: + break - if self.process.fromchild in r_stdin: - data = self.process.fromchild.read() - if data == "": - break - buffer += data + if self.process.fromchild in r_stdin: + data = self.process.fromchild.read() + if data == "": + break + buffer += data - # loop, trying to get complete packets out of our buffer - complete, in_packet = False, False - while not complete and buffer != '': - if not in_packet: - m = packet_header_re.match(buffer) - if not m: - break - in_packet = True - cmdnum, errnum, pstate, length = m.groups() - errnum = int(errnum) - length = int(length) - header_length = m.end(m.lastindex) + 1 # the '1' is the colon + # loop, trying to get complete packets out of our buffer + complete, in_packet = False, False + while not complete and buffer != '': + if not in_packet: + m = packet_header_re.match(buffer) + if not m: + break + in_packet = True + cmdnum, errnum, pstate, length = m.groups() + errnum = int(errnum) + length = int(length) + header_length = m.end(m.lastindex) + 1 # the '1' is the colon - if len(buffer) < length + header_length: - # not enough data read from client yet; go round - break - else: - result = buffer[header_length:header_length+length] - buffer = buffer[header_length+length:] - complete = pstate == 'l' - in_packet = False - yield errnum, complete, result + if len(buffer) < length + header_length: + # not enough data read from client yet; go round + break + else: + result = buffer[header_length:header_length+length] + buffer = buffer[header_length+length:] + complete = pstate == 'l' + in_packet = False + yield errnum, complete, result - if complete: - break - - # get our response, and yield() it back one line at a time - code_max = -1 - for code, is_last, data in read_result_packets(): - if code and code > code_max: - code_max = code - for line in data.split('\n'): - yield line + '\n' - if code_max > 0: - raise MonotoneException("error code %d in automate packet." % (code_max)) + if complete: + break + + # get our response, and yield() it back one line at a time + code_max = -1 + for code, is_last, data in read_result_packets(): + if code and code > code_max: + code_max = code + for line in data.split('\n'): + yield line + '\n' + if code_max > 0: + raise MonotoneException("error code %d in automate packet." % (code_max)) class Standalone(Runner): """Runs commands by running monotone. One monotone process per command""" def run(self, command, args): - # as we pass popen3 as sequence, it executes monotone with these - # arguments - and does not pass them through the shell according - # to help(os.popen3) - to_run = self.base_command + [command] + args - process = popen2.Popen3(to_run, capturestderr=True) - for line in process.fromchild: - yield line - stderr_data = process.childerr.read() - if len(stderr_data) > 0: - raise MonotoneException("data on stderr for command '%s': %s" % (command, - stderr_data)) - terminate_popen3(process) + # as we pass popen3 as sequence, it executes monotone with these + # arguments - and does not pass them through the shell according + # to help(os.popen3) + debug(("standalone is running:", command, args)) + to_run = self.base_command + [command] + args + process = popen2.Popen3(to_run, capturestderr=True) + for line in process.fromchild: + yield line + stderr_data = process.childerr.read() + if len(stderr_data) > 0: + raise MonotoneException("data on stderr for command '%s': %s" % (command, + stderr_data)) + terminate_popen3(process) class MtnObject: def __init__(self, obj_type): - self.obj_type = obj_type + self.obj_type = obj_type class Tag(MtnObject): def __init__(self, name, revision, author): - MtnObject.__init__(self, "tag") - self.name, self.revision, self.author = name, Revision(revision), author + MtnObject.__init__(self, "tag") + self.name, self.revision, self.author = name, Revision(revision), author class Branch(MtnObject): def __init__(self, name): - MtnObject.__init__(self, "branch") - self.name = name + MtnObject.__init__(self, "branch") + self.name = name class File(MtnObject): def __init__(self, name, in_revision): - MtnObject.__init__(self, "file") - self.name = name - self.in_revision = in_revision + MtnObject.__init__(self, "file") + self.name = name + self.in_revision = in_revision basic_io_name_tok = re.compile(r'^(\S+)') @@ -194,113 +197,113 @@ def basic_io_from_stream(gen): # new value of line (eg. with consumed tokens removed) def hex_consume(line): - m = revision_re_c.match(line[1:]) - if line[0] != '[' or not m: - raise MonotoneException("This is not a hex token: %s" % line) - end_of_match = m.end(m.lastindex) - if line[end_of_match+1] != ']': - raise MonotoneException("Hex token ends in character other than ']': %s" % line) - return Revision(m.groups()[0]), choose_consume, line[end_of_match+2:] + m = revision_re_c.match(line[1:]) + if line[0] != '[' or not m: + raise MonotoneException("This is not a hex token: %s" % line) + end_of_match = m.end(m.lastindex) + if line[end_of_match+1] != ']': + raise MonotoneException("Hex token ends in character other than ']': %s" % line) + return Revision(m.groups()[0]), choose_consume, line[end_of_match+2:] def name_consume(line): - m = name_re_c.match(line) - if not m: - raise MonotoneException("Not a name: %s" % line) - end_of_match = m.end(m.lastindex) - return m.groups()[0], choose_consume, line[end_of_match:] + m = name_re_c.match(line) + if not m: + raise MonotoneException("Not a name: %s" % line) + end_of_match = m.end(m.lastindex) + return m.groups()[0], choose_consume, line[end_of_match:] def choose_consume(line): - line = line.lstrip() - if line == '': - consumer = choose_consume - elif line[0] == '[': - consumer = hex_consume - elif line[0] == '"': - consumer = string_consume - else: - consumer = name_consume - return None, consumer, line + line = line.lstrip() + if line == '': + consumer = choose_consume + elif line[0] == '[': + consumer = hex_consume + elif line[0] == '"': + consumer = string_consume + else: + consumer = name_consume + return None, consumer, line class StringState: - def __init__(self): - self.in_escape = False - self.has_started = False - self.has_ended = False - self.value = '' + def __init__(self): + self.in_escape = False + self.has_started = False + self.has_ended = False + self.value = '' def string_consume(line, state=None): - if not state: - state = StringState() + if not state: + state = StringState() - if not state.has_started: - if line[0] != '"': - raise MonotoneException("Not a string: %s" % line) - line = line[1:] - state.has_started = True + if not state.has_started: + if line[0] != '"': + raise MonotoneException("Not a string: %s" % line) + line = line[1:] + state.has_started = True - for idx, c in enumerate(line): - if state.in_escape: - if c != '\\' or c != '\"': - raise MonotoneException("Invalid escape code: %s\n" % line) - state.value += c - state.in_escape = False - else: - if c == '\\': - state.in_escape = True - elif c == '"': - state.has_ended = True - break - else: - state.value += c + for idx, c in enumerate(line): + if state.in_escape: + if c != '\\' or c != '\"': + raise MonotoneException("Invalid escape code: %s\n" % line) + state.value += c + state.in_escape = False + else: + if c == '\\': + state.in_escape = True + elif c == '"': + state.has_ended = True + break + else: + state.value += c - if state.has_ended: - return state.value, choose_consume, line[idx+1:] - else: - return (None, - lambda s: string_consume(s, state), - line[idx+1:]) + if state.has_ended: + return state.value, choose_consume, line[idx+1:] + else: + return (None, + lambda s: string_consume(s, state), + line[idx+1:]) consumer = choose_consume current_stanza = [] for line in gen: - # if we're not in an actual consumer (which we shouldn't be, unless - # we're parsing some sort of multi-line token) and we have a blank - # line, it indicates the end of any current stanza - if (consumer == choose_consume) and (line == '' or line == '\n') and current_stanza: - yield current_stanza - current_stanza = [] - continue + # if we're not in an actual consumer (which we shouldn't be, unless + # we're parsing some sort of multi-line token) and we have a blank + # line, it indicates the end of any current stanza + if (consumer == choose_consume) and (line == '' or line == '\n') and current_stanza: + yield current_stanza + current_stanza = [] + continue - while line != '' and line != '\n': - new_token, consumer, line = consumer(line) - if new_token != None: - current_stanza.append(new_token) + while line != '' and line != '\n': + new_token, consumer, line = consumer(line) + if new_token != None: + current_stanza.append(new_token) class Operations: def __init__(self, runner_args): - self.standalone = apply(Standalone, runner_args) - self.automate = apply(Automate, runner_args) + self.standalone = apply(Standalone, runner_args) + self.automate = apply(Automate, runner_args) def tags(self): - for line in (t.strip() for t in self.standalone.run('ls', ['tags'])): - if not line: - continue - yield apply(Tag, line.split(' ', 2)) + for line in (t.strip() for t in self.standalone.run('ls', ['tags'])): + if not line: + continue + yield apply(Tag, line.split(' ', 2)) def branches(self): - for line in (t.strip() for t in self.standalone.run('ls', ['branches'])): - if not line: - continue - yield apply(Branch, (line,)) + for line in (t.strip() for t in self.standalone.run('ls', ['branches'])): + if not line: + continue + yield apply(Branch, (line,)) def graph(self): - for line in self.automate.run('graph', []): - yield line + for line in self.automate.run('graph', []): + yield line def get_revision(self, revision): - for stanza in basic_io_from_stream(self.automate.run('get_revision', [revision])): - yield stanza + for stanza in basic_io_from_stream(self.automate.run('get_revision', [revision])): + yield stanza def certs(self, revision): + for stanza in basic_io_from_stream(self.automate.run('certs', [revision])): + yield stanza - for stanza in basic_io_from_stream(self.automate.run('certs', [revision])): - yield stanza ============================================================ --- viewmtn.py fb9d3d9fa86e7ae6b4e9ee3be5ac53fa944c6147 +++ viewmtn.py 7728db1166843eaeee789a1c821dcc4f804c96c7 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2.4 import cgi import mtn @@ -35,117 +35,116 @@ class Link: class Link: def __init__(self, description=None): - self.relative_uri = None - self.description = description + self.relative_uri = None + self.description = description def html(self): - return '%s' % (self.relative_uri, - self.description) + return '%s' % (self.relative_uri, + self.description) class RevisionLink(Link): def __init__(self, revision, **kwargs): - Link.__init__(*(self, ), **kwargs) - self.relative_uri = 'revision/info/%s' % (revision) - self.description = revision.abbrev() + Link.__init__(*(self, ), **kwargs) + self.relative_uri = 'revision/info/%s' % (revision) + self.description = revision.abbrev() class TagLink(Link): def __init__(self, tag, **kwargs): - Link.__init__(*(self, ), **kwargs) - self.relative_uri = 'revision/info/%s' % (tag.revision) - self.description = tag.name + Link.__init__(*(self, ), **kwargs) + self.relative_uri = 'revision/info/%s' % (tag.revision) + self.description = tag.name class BranchLink(Link): def __init__(self, branch, **kwargs): - Link.__init__(*(self, ), **kwargs) - self.relative_uri = 'branch/changes/' + urllib.quote(branch.name) - self.description = hq(branch.name) + Link.__init__(*(self, ), **kwargs) + self.relative_uri = 'branch/changes/' + urllib.quote(branch.name) + self.description = hq(branch.name) class DiffLink(Link): def __init__(self, diff, **kwargs): - Link.__init__(*(self, ), **kwargs) - self.relative_uri = 'revision/diff/' + diff.from_rev + '/with/' + diff.to_rev - if diff.fname: - self.relative_uri += '/'+urllib.quote(diff.fname) - self.description = "diff" + Link.__init__(*(self, ), **kwargs) + self.relative_uri = 'revision/diff/' + diff.from_rev + '/with/' + diff.to_rev + if diff.fname: + self.relative_uri += '/'+urllib.quote(diff.fname) + self.description = "diff" class FileLink(Link): def __init__(self, file, **kwargs): - Link.__init__(*(self, ), **kwargs) - self.relative_uri = 'revision/file/' + file.in_revision + '/' + urllib.quote("goat") + Link.__init__(*(self, ), **kwargs) + self.relative_uri = 'revision/file/' + file.in_revision + '/' + urllib.quote("goat") class Diff: def __init__(self, from_rev, to_rev, fname=None): - self.obj_type = 'diff' - self.fname = fname - self.from_rev = from_rev - self.to_rev = to_rev + self.obj_type = 'diff' + self.fname = fname + self.from_rev = from_rev + self.to_rev = to_rev def prettify(s): return ' '.join( - map(lambda x: hq(x[0].upper() + x[1:]), - s.replace("_", " ").split(" "))) + map(lambda x: hq(x[0].upper() + x[1:]), + s.replace("_", " ").split(" "))) def certs_for_template(cert_gen): for cert in cert_gen: - if cert[0] == 'key' and len(cert) != 10: - raise Exception("Not a correcly formatted certificate: %s" % cert) - if cert[3] != 'ok': - raise Exception("Certificate failed check.") + if cert[0] == 'key' and len(cert) != 10: + raise Exception("Not a correcly formatted certificate: %s" % cert) + if cert[3] != 'ok': + raise Exception("Certificate failed check.") - key = cert[1] - name = cert[5] - value = cert[7] - if name == "branch": - value = link(mtn.Branch(value)).html() - else: - value = '
'.join(map(hq, value.split('\n'))) + key = cert[1] + name = cert[5] + value = cert[7] + if name == "branch": + value = link(mtn.Branch(value)).html() + else: + value = '
'.join(map(hq, value.split('\n'))) - yield { 'key' : key, - 'name' : prettify(name), - 'value' : value } + yield { 'key' : key, + 'name' : prettify(name), + 'value' : value } def revisions_for_template(revision, rev_gen): old_revisions = [] stanzas = [] grouping = None for stanza in rev_gen: - stanza_type = stanza[0] - description, value = prettify(stanza_type), None + stanza_type = stanza[0] + description, value = prettify(stanza_type), None - if grouping == None: - grouping = description - if description != grouping: - if len(stanzas) > 0: - yield grouping, stanzas - grouping, stanzas = description, [] + if grouping == None: + grouping = description + if description != grouping: + if len(stanzas) > 0: + yield grouping, stanzas + grouping, stanzas = description, [] - if stanza_type == "format_version" or \ - stanza_type == "new_manifest": - continue - elif stanza_type == "patch": + if stanza_type == "format_version" or \ + stanza_type == "new_manifest": + continue + elif stanza_type == "patch": fname, from_id, to_id = stanza[1], stanza[3], stanza[5] - debug(stanza) - # if from_id is null, this is a new file - # since we're showing that information under "Add", so - # skip it here - if not from_id: - continue - diff_links = ','.join([link(Diff(old_revision, revision, fname)).html() for old_revision in old_revisions]) - value = "Patch file %s (%s)" % (hq(fname), diff_links) - elif stanza_type == "old_revision": - old_revision = mtn.Revision(stanza[1]) - old_revisions.append(old_revision) - value = "Old revision is: %s (%s)" % (old_revision.abbrev(), link(Diff(old_revision, revision)).html()) - elif stanza_type == "add_file": - fname = stanza - value = "Add file: %s" % (link(mtn.File(fname, revision))) - else: - value = "(this stanza type is not explicitly rendered; please report this.)\n%s" % hq(str(stanza)) + # if from_id is null, this is a new file + # since we're showing that information under "Add", so + # skip it here + if not from_id: + continue + diff_links = ','.join([link(Diff(old_revision, revision, fname)).html() for old_revision in old_revisions]) + value = "Patch file %s (%s)" % (hq(fname), diff_links) + elif stanza_type == "old_revision": + old_revision = mtn.Revision(stanza[1]) + old_revisions.append(old_revision) + value = "Old revision is: %s (%s)" % (old_revision.abbrev(), link(Diff(old_revision, revision)).html()) + elif stanza_type == "add_file": + fname = stanza + value = "Add file: %s" % (link(mtn.File(fname, revision))) + else: + value = "(this stanza type is not explicitly rendered; please report this.)\n%s" % hq(str(stanza)) - if description != None: - stanzas.append(value) + if description != None: + stanzas.append(value) if len(stanzas) > 0: - yield grouping, stanzas + yield grouping, stanzas type_to_link_class = { 'tag' : TagLink, @@ -157,104 +156,105 @@ def link(obj): def link(obj): link_class = type_to_link_class.get(obj.obj_type) if not link_class: - raise LinkException("Unable to link to objects of type: '%s'" % (obj.obj_type)) + raise LinkException("Unable to link to objects of type: '%s'" % (obj.obj_type)) return link_class(obj) class Renderer: def __init__(self): - # any templates that can be inherited from, should be added to the list here - self.templates = [ ('base.html', 'base'), - ('revision.html', 'revision'), ] - self._templates_loaded = False + # any templates that can be inherited from, should be added to the list here + self.templates = [ ('base.html', 'base'), + ('revision.html', 'revision'), ] + self._templates_loaded = False - # these variables will be available to any template - self.terms = { - 'context' : web.context, # fugly - 'dynamic_uri_path' : config.dynamic_uri_path, - 'dynamic_join' : lambda path: urlparse.urljoin(config.dynamic_uri_path, path), - 'link' : link, - 'static_uri_path' : config.static_uri_path, - 'static_join' : lambda path: urlparse.urljoin(config.static_uri_path, path), - } + # these variables will be available to any template + self.terms = { + 'context' : web.context, # fugly + 'dynamic_uri_path' : config.dynamic_uri_path, + 'dynamic_join' : lambda path: urlparse.urljoin(config.dynamic_uri_path, path), + 'link' : link, + 'static_uri_path' : config.static_uri_path, + 'static_join' : lambda path: urlparse.urljoin(config.static_uri_path, path), + } def load_templates(self): - if self._templates_loaded: return - for template, mod_name in self.templates: - web.render(template, None, True, mod_name) - self._templates_loaded = True + if self._templates_loaded: return + for template, mod_name in self.templates: + web.render(template, None, True, mod_name) + self._templates_loaded = True def render(self, template, **kwargs): - self.load_templates() - terms = self.terms.copy() - terms.update(kwargs) - web.render(template, terms) + self.load_templates() + terms = self.terms.copy() + terms.update(kwargs) + web.render(template, terms) renderer = Renderer() ops = mtn.Operations([config.monotone, config.dbfile]) class Index: def GET(self): - renderer.render('index.html', page_title="Branches", branches=ops.branches()) + renderer.render('index.html', page_title="Branches", branches=ops.branches()) class About: def GET(self): - renderer.render('about.html', page_title="About") + renderer.render('about.html', page_title="About") class Tags: def GET(self): - renderer.render('tags.html', page_title="Tags", tags=ops.tags()) + renderer.render('tags.html', page_title="Tags", tags=ops.tags()) class Help: def GET(self): - renderer.render('help.html', page_title="Help") + renderer.render('help.html', page_title="Help") class RevisionInfo: def GET(self, revision): - revision = mtn.Revision(revision) - certs = ops.certs(revision) - revisions = ops.get_revision(revision) - renderer.render('revisioninfo.html', - page_title="Revision %s" % revision.abbrev(), - revision=revision, - certs=certs_for_template(certs), - revisions=revisions_for_template(revision, revisions)) + revision = mtn.Revision(revision) + certs = ops.certs(revision) + revisions = ops.get_revision(revision) + renderer.render('revisioninfo.html', + page_title="Revision %s" % revision.abbrev(), + revision=revision, + certs=certs_for_template(certs), + revisions=revisions_for_template(revision, revisions)) class RevisionDiff: def GET(self, revision_from, revision_to): - revision_from = mtn.Revision(revision_from) - revision_to = mtn.Revision(revision_to) - renderer.render('revisiondiff.html', - page_title="Diff from %s to %s" % (revision_from.abbrev(), revision_to.abbrev()), - revision_from=revision_from, - revision_to=revision_to) + revision_from = mtn.Revision(revision_from) + revision_to = mtn.Revision(revision_to) + renderer.render('revisiondiff.html', + page_title="Diff from %s to %s" % (revision_from.abbrev(), revision_to.abbrev()), + revision=revision_from, + revision_from=revision_from, + revision_to=revision_to) class RevisionFile: def GET(self, revision, file): - revision = mtn.Revision(revision) - print "file %s from revision %s" % (file, revision) + revision = mtn.Revision(revision) + print "file %s from revision %s" % (file, revision) class RevisionBrowse: def GET(self, revision, path): - revision = mtn.Revision(revision) - renderer.render('revisionpath.html', - page_title=revision, - path=path) + revision = mtn.Revision(revision) + renderer.render('revisionpath.html', + page_title=revision, + path=path) class RevisionTar: def GET(self, revision): - revision = mtn.Revision(revision) - print "not implemented" + revision = mtn.Revision(revision) + print "not implemented" class Json: def GET(self, method, data): - print "Bah." + print "Bah." branch_re = r'' urls = ( - '/', 'Index', - '/about', 'About', - '/tags', 'Tags', - '/help', 'Help', + '/', 'Index', #done + '/about', 'About', #done + '/tags', 'Tags', #done + '/help', 'Help', #done '/json/(A-Za-z)/(.*)', 'Json', '/revision/browse/('+mtn.revision_re+')/(.*)', 'RevisionBrowse',