#
#
# patch "branch.psp"
# from [f7ee6449c43df5afb9843bfd019f6216af7b04b4]
# to [016f5daf53d8047dc6cd8e924c679f4105bfa410]
#
# patch "common.py"
# from [87a1283680b253e297af32da14119a2546730fda]
# to [326b759a8cc797fb5f57b12c7c5531fe90c2a010]
#
# patch "error.psp"
# from [7152c3ff110418aca5d23c374ea9fb92a0e98379]
# to [e1defd5a7a1469119dcbe07f0624e3412c394a03]
#
# patch "file.psp"
# from [fa6a72321817631e86ee73ad28dacf97bf8d7218]
# to [d36cc97dee84c41f56e0d81272f0050229d39ea0]
#
# patch "fileinbranch.psp"
# from [1d3a5d5742dacc82879b558f8e785915653839c0]
# to [4a02ad29a9eb9486ddde1d67bf19851cc421009c]
#
# patch "headofbranch.psp"
# from [941f8cb85d9e1c05d5e1e9a7fe419e6c45bdaec3]
# to [a3561a6b2bbf54822d3d82621421f05e07fe49b0]
#
# patch "html.py"
# from [1a39d42e352389a7c9cd52a5037861895ed6d4b8]
# to [65567f42bebe8cba7faf23471d7c8e6031bc50b5]
#
# patch "index.psp"
# from [57d99c98f15d8761c2f8c1ca85b0aa5d959cb88d]
# to [6d0099247d31734a99caa03b8d2ac24acc034582]
#
# patch "manifest.psp"
# from [d6cec402657f3518eb8017a54a22428644dcc233]
# to [9bfe0452e706fb8fd3f84c91b137061795e2686b]
#
# patch "monotone.py"
# from [09a54ce28dfd9610ba5c53188ee17f27c4716ef7]
# to [27ba5f94c6849264fb9df4f695580591e1ee3146]
#
# patch "revision.psp"
# from [54379dc8fb3e0fd02a084fcd92bdb0f41a85de61]
# to [82d35235b0c254d2ec2552d53b72f17242f462b1]
#
# patch "tags.psp"
# from [9b74bcccf9f006be39cf615e707cbb0d4c85ede4]
# to [6fdb677d68ef8f35cf439a2d7c34e37ff29cd9af]
#
# patch "tarofbranch.psp"
# from [99f001ed6a4855e6bbe97dbaca580fd6673bfae1]
# to [1af97621a341b580ee0f92f701cf18ae95087c83]
#
# patch "utility.py"
# from [c1f6a1ef480d6270c208d9b74a6dcbe1b8be7a06]
# to [78cd2c53af05e63bb76c097dc832b6f75394e40d]
#
# patch "wrapper.py"
# from [cecd28873835961f94fcc047350c89e6bba4151f]
# to [904f434b11952f8634568a5de0f80298efb872df]
#
============================================================
--- branch.psp f7ee6449c43df5afb9843bfd019f6216af7b04b4
+++ branch.psp 016f5daf53d8047dc6cd8e924c679f4105bfa410
@@ -14,20 +14,20 @@
psp.set_error_page("error.psp")
def prettify(s):
- return ' '.join(map(lambda x: hq(x[0].upper() + x[1:]), s.replace("_", " ").split(" ")))
+ return ' '.join(map(lambda x: hq(x[0].upper() + x[1:]), s.replace("_", " ").split(" ")))
if not form.has_key('branch'):
- raise Exception("No branch specified.")
+ raise Exception("No branch specified.")
page = 1
if form.has_key('page'):
- try: page = int(form['page'])
- except: pass
+ try: page = int(form['page'])
+ except: pass
if form.has_key('rss'):
- output = 'rss'
+ output = 'rss'
else:
- output = 'html'
+ output = 'html'
if page < 1: page = 1
@@ -38,13 +38,13 @@
page_title = "Changes to branch %s" % (hq(branch))
if output == 'html':
- info = {
- 'title' : page_title,
- 'branch_rss' : [branch]
- }
- req.write(template.header(info))
+ info = {
+ 'title' : page_title,
+ 'branch_rss' : [branch]
+ }
+ req.write(template.header(info))
elif output == 'rss':
- req.write('''\
+ req.write('''\
en-us
@@ -58,12 +58,12 @@
recent = mt.toposort(mt.ancestors(heads) + heads)
if display > len(recent):
- display = len(recent)
+ display = len(recent)
if offset > len(recent):
- offset = len(recent)
- no_next = True
+ offset = len(recent)
+ no_next = True
else:
- no_next = False
+ no_next = False
start = -1*offset
stop = start + display
@@ -75,84 +75,84 @@
<%
if output == 'html':
- req.write('''\
+ req.write('''\
This page lists changes %s through %s to %s.
It may also be useful to view the %s of this branch.
''' % (-1*(stop or 0), (-1 * start), link('branch', branch), link('headofbranch', branch, 'current head revision')))
- req.write('
')
+ req.write('
')
%>
<%
for idx, id in enumerate(recent):
- certs = mt.certs(id)
+ certs = mt.certs(id)
- if output == 'rss':
- req.write('\n')
- req.write('%s\n' % (urllib.basejoin(config.base_url, 'revision.psp?id=%s' % (urllib.quote(id)))))
+ if output == 'rss':
+ req.write('\n')
+ req.write('%s\n' % (urllib.basejoin(config.base_url, 'revision.psp?id=%s' % (urllib.quote(id)))))
- changelog, certdate, quicklog, certinfo = None, None, "", ""
- for cert in certs:
- name, value = None, None
- for k, v in cert:
- if k == "name": name = v
- elif k == "value": value = v
- if name == None or value == None: continue
- if name == "date":
- certdate = apply(datetime.datetime, time.strptime(value, "%Y-%m-%dT%H:%M:%S")[:6])
- if output == 'rss': req.write("\t%s\n" % hq(certdate.strftime("%a, %d %b %Y %H:%M:%S +0000")))
- elif name == "branch":
- value = link("branch", value)
- else:
- if name == "changelog":
- quicklog = hq(value.strip().split('\n')[0])
- if quicklog.startswith('*'): quicklog = quicklog[1:].strip()
- if output == 'rss': req.write("\t%s\n%s\n" % (quicklog, hq(value)))
- elif name == "author":
- if output == 'rss': req.write("\t%s\n" % (hq(value)))
- value = ' '.join(map(hq, value.split('\n')))
- certinfo += '
%s:
%s
\n' % (prettify(name), value)
+ changelog, certdate, quicklog, certinfo = None, None, "", ""
+ for cert in certs:
+ name, value = None, None
+ for k, v in cert:
+ if k == "name": name = v
+ elif k == "value": value = v
+ if name == None or value == None: continue
+ if name == "date":
+ certdate = apply(datetime.datetime, time.strptime(value, "%Y-%m-%dT%H:%M:%S")[:6])
+ if output == 'rss': req.write("\t%s\n" % hq(certdate.strftime("%a, %d %b %Y %H:%M:%S +0000")))
+ elif name == "branch":
+ value = link("branch", value)
+ else:
+ if name == "changelog":
+ quicklog = hq(value.strip().split('\n')[0])
+ if quicklog.startswith('*'): quicklog = quicklog[1:].strip()
+ if output == 'rss': req.write("\t%s\n%s\n" % (quicklog, hq(value)))
+ elif name == "author":
+ if output == 'rss': req.write("\t%s\n" % (hq(value)))
+ value = ' '.join(map(hq, value.split('\n')))
+ certinfo += '
')
%>
============================================================
--- file.psp fa6a72321817631e86ee73ad28dacf97bf8d7218
+++ file.psp d36cc97dee84c41f56e0d81272f0050229d39ea0
@@ -20,33 +20,33 @@
psp.set_error_page("error.psp")
if not form.has_key('id'):
- raise Exception("No revision ID specified.")
+ raise Exception("No revision ID specified.")
id = form['id']
if not monotone.is_valid_id(id):
- raise Exception("Specified revision ID is not valid.")
+ raise Exception("Specified revision ID is not valid.")
if not form.has_key('path'):
- raise Exception("No path specified.")
+ raise Exception("No path specified.")
path = form['path']
revision = mt.revision(id)
if not revision.has_key('new_manifest'):
- raise Exception("There is no manifest in this revision ID.")
+ raise Exception("There is no manifest in this revision ID.")
manifest_id = revision['new_manifest'][0][0][1]
manifest = mt.manifest(manifest_id)
matching_file_id = None
for file_id, filename in manifest:
- if filename == path:
- matching_file_id = file_id
- break
+ if filename == path:
+ matching_file_id = file_id
+ break
if matching_file_id == None:
- raise Exception("File not found in this revision.")
+ raise Exception("File not found in this revision.")
info = {
- 'title' : "File '/%s' in revision %s" % (hq(path), hq(id)),
- }
+ 'title' : "File '/%s' in revision %s" % (hq(path), hq(id)),
+ }
req.write(template.header(info))
contents = mt.file(matching_file_id)
@@ -57,41 +57,41 @@
# is it binary?
def is_binary():
- nontext_chars = "\x01\x02\x03\x04\x05\x06\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1c\x1d\x1e\x1f"
- check = {}
- for char in nontext_chars:
- check[char] = True
- for i in contents:
- if check.has_key(i): return True
- return False
+ nontext_chars = "\x01\x02\x03\x04\x05\x06\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1c\x1d\x1e\x1f"
+ check = {}
+ for char in nontext_chars:
+ check[char] = True
+ for i in contents:
+ if check.has_key(i): return True
+ return False
mime_type = mimetypes.guess_type(path)[0]
if mime_type == None: mime_type = 'text/plain'
if mime_type == 'image/png' or mime_type == 'image/jpeg' or mime_type == 'image/gif':
- display_as_image = True
+ display_as_image = True
else: display_as_image = False
# okay; can we guess a valid enscript filter to run this through?
tsp = mime_type.split('/', 1)
filter = None
if tsp[0] == 'text':
- candidate = tsp[1]
- if candidate.startswith('x-'): candidate = candidate[2:]
- if candidate.endswith('src'): candidate = candidate[:-3]
- if candidate.endswith('hdr'): candidate = candidate[:-3]
- if candidate == 'c++': candidate = 'cpp' # ugly
- if candidate in enscript_langs: filter = candidate
+ candidate = tsp[1]
+ if candidate.startswith('x-'): candidate = candidate[2:]
+ if candidate.endswith('src'): candidate = candidate[:-3]
+ if candidate.endswith('hdr'): candidate = candidate[:-3]
+ if candidate == 'c++': candidate = 'cpp' # ugly
+ if candidate in enscript_langs: filter = candidate
if filter == None:
- # heh, will at least work for lua files
- last_dot = path.rfind('.')
- if last_dot == -1: last_dot = 0
- candidate = path[last_dot:]
- if candidate in enscript_langs: filter = candidate
+ # heh, will at least work for lua files
+ last_dot = path.rfind('.')
+ if last_dot == -1: last_dot = 0
+ candidate = path[last_dot:]
+ if candidate in enscript_langs: filter = candidate
# if no filter then let's check if it's binary or not; if not binary
# we'll just treat it as text; otherwise display a warning and a download
# link
if filter == None and not is_binary():
- filter = 'text'
+ filter = 'text'
req.write('
For more information about the revision containing this file, see its %s page. For other files contained in this revision, see the %s.
''')
req.write(template.footer(info))
============================================================
--- fileinbranch.psp 1d3a5d5742dacc82879b558f8e785915653839c0
+++ fileinbranch.psp 4a02ad29a9eb9486ddde1d67bf19851cc421009c
@@ -6,9 +6,9 @@
psp.set_error_page("error.psp")
if not form.has_key('branch'):
- raise Exception("No branch specified.")
+ raise Exception("No branch specified.")
if not form.has_key('path'):
- raise Exception("No path specified.")
+ raise Exception("No path specified.")
branch = form['branch']
path = form['path']
@@ -20,35 +20,35 @@
heads = mt.heads(branch)
if len(heads) == 0:
- raise Exception("No head ID can be determined for this branch.")
+ raise Exception("No head ID can be determined for this branch.")
# build a hash with a list of all files in each head which
# match PATH and their file ID.
file_version = {}
for id in heads:
- revision = mt.revision(id)
- manifest_id = revision['new_manifest'][0][0][1]
- file_revisions = filter(lambda x: x[1] == path, mt.manifest(manifest_id))
- if len(file_revisions) > 1:
- raise Exception("More than one file matches path?")
- elif len(file_revisions) == 1:
- file_version[id] = file_revisions[0][0]
+ revision = mt.revision(id)
+ manifest_id = revision['new_manifest'][0][0][1]
+ file_revisions = filter(lambda x: x[1] == path, mt.manifest(manifest_id))
+ if len(file_revisions) > 1:
+ raise Exception("More than one file matches path?")
+ elif len(file_revisions) == 1:
+ file_version[id] = file_revisions[0][0]
if len(file_version.values()) == 0:
- raise Exception("No file such file found in this branch.")
+ raise Exception("No file such file found in this branch.")
unique = True
last = None
values = file_version.values()
for file_revision in values:
- if last != None and file_revision != last:
- unique = False
- break
- last = file_revision
+ if last != None and file_revision != last:
+ unique = False
+ break
+ last = file_revision
if unique:
- psp.redirect("getfile.py?id=%s&path=%s" % (urllib.quote(values[0]), urllib.quote(path)))
+ psp.redirect("getfile.py?id=%s&path=%s" % (urllib.quote(values[0]), urllib.quote(path)))
else:
- info = {'title' : "Latest version of %s in branch %s" % (hq(path), hq(branch))}
- req.write(template.header(info))
+ info = {'title' : "Latest version of %s in branch %s" % (hq(path), hq(branch))}
+ req.write(template.header(info))
%>
The branch you have selected has multiple head revisions, and the file you are attempting
@@ -59,8 +59,8 @@
Head revision
View file
<%
- for id in file_version.keys():
- req.write('
%s
%s
' % (link("revision", id), link("download", [file_version[id], path], "view file")))
+ for id in file_version.keys():
+ req.write('
============================================================
--- headofbranch.psp 941f8cb85d9e1c05d5e1e9a7fe419e6c45bdaec3
+++ headofbranch.psp a3561a6b2bbf54822d3d82621421f05e07fe49b0
@@ -5,7 +5,7 @@
psp.set_error_page("error.psp")
if not form.has_key('branch'):
- raise Exception("No branch specified.")
+ raise Exception("No branch specified.")
branch = form['branch']
# headofbranch.psp
@@ -20,14 +20,14 @@
heads = mt.heads(branch)
if len(heads) == 0:
- raise Exception("No head ID can be determined for this branch.")
+ raise Exception("No head ID can be determined for this branch.")
elif len(heads) == 1:
- # a single head ID; redirect to it
- id = heads[0]
- psp.redirect("revision.psp?id=%s" % (urllib.quote(id)))
+ # a single head ID; redirect to it
+ id = heads[0]
+ psp.redirect("revision.psp?id=%s" % (urllib.quote(id)))
else:
- info = {'title' : "Branch details for %s" % (hq(branch))}
- req.write(template.header(info))
+ info = {'title' : "Branch details for %s" % (hq(branch))}
+ req.write(template.header(info))
%>
The following head IDs are available. Please select one to view.
@@ -36,8 +36,8 @@
Head revision
<%
- for id in heads:
- req.write('
%s
' % (link("revision", id)))
+ for id in heads:
+ req.write('
%s
' % (link("revision", id)))
%>
============================================================
--- html.py 1a39d42e352389a7c9cd52a5037861895ed6d4b8
+++ html.py 65567f42bebe8cba7faf23471d7c8e6031bc50b5
@@ -10,71 +10,71 @@
hq = common.html_escape()
def get_icon(filename, mime_type=None, is_directory=False):
- if config.gnome_mimetype_icon_path == None:
- return None
- if is_directory:
- filename = 'gnome-fs-directory.png'
- else:
- if mime_type == None:
- mime_type = mimetypes.guess_type(filename)[0]
- if not mime_type:
- mime_type = "text"
- if mime_type.endswith('hdr'):
- mime_type = mime_type[:-3] + 'src'
- # some specific fixups
- filename = 'gnome-mime-' + mime_type.replace('/', '-') + '.png'
- if os.access(os.path.join(config.gnome_mimetype_icon_path, filename), os.R_OK):
- return '/'.join([config.gnome_mimetype_uri, filename])
- else:
- return None
+ if config.gnome_mimetype_icon_path == None:
+ return None
+ if is_directory:
+ filename = 'gnome-fs-directory.png'
+ else:
+ if mime_type == None:
+ mime_type = mimetypes.guess_type(filename)[0]
+ if not mime_type:
+ mime_type = "text"
+ if mime_type.endswith('hdr'):
+ mime_type = mime_type[:-3] + 'src'
+ # some specific fixups
+ filename = 'gnome-mime-' + mime_type.replace('/', '-') + '.png'
+ if os.access(os.path.join(config.gnome_mimetype_icon_path, filename), os.R_OK):
+ return '/'.join([config.gnome_mimetype_uri, filename])
+ else:
+ return None
class TableWriter:
- def __init__(self, req):
- self.req = req
- self.odd = False
- def start(self):
- self.req.write('
')
- def write(self, row, is_header=False):
- if self.odd: c = 'odd'
- else: c = 'even'
- if is_header: self.odd = False
- else: self.odd = not self.odd
- self.req.write('
' % (icon_uri, \
+ link("file", [id, file], name), \
+ link("revision", rev, (age or "").replace(' ', ' '), no_quote=True),
+ author, \
+ quicklog))
tr.stop()
%>
============================================================
--- monotone.py 09a54ce28dfd9610ba5c53188ee17f27c4716ef7
+++ monotone.py 27ba5f94c6849264fb9df4f695580591e1ee3146
@@ -26,334 +26,334 @@
log_entry_re = re.compile(r'^(\S+): (.*)$')
def colour_from_string(str):
- def f(off):
- return ord(hashval[off]) / 256.0
- hashval = sha.new(str).digest()
- hue = f(5)
- li = f(1) * 0.15 + 0.55
- sat = f(2) * 0.5 + .5
- return ''.join(map(lambda x: "%.2x" % int(x * 256), hls_to_rgb(hue, li, sat)))
+ def f(off):
+ return ord(hashval[off]) / 256.0
+ hashval = sha.new(str).digest()
+ hue = f(5)
+ li = f(1) * 0.15 + 0.55
+ sat = f(2) * 0.5 + .5
+ return ''.join(map(lambda x: "%.2x" % int(x * 256), hls_to_rgb(hue, li, sat)))
class Automation:
- def __init__(self, base_command):
- self.command = "%s automate stdio" % (base_command)
- self.process = None
- def __del__(self):
- self.stop()
- self.process = None
- def start(self):
- self.process = popen2.Popen3(self.command)
- set_nonblocking(self.process.fromchild)
- def stop(self):
- if not self.process: return
- try:
- self.process.tochild.close()
- self.process.fromchild.close()
- if self.process.poll() == -1:
- # the process is still running, so kill it.
- os.kill(self.process.pid, signal.SIGKILL)
- self.process.wait()
- except:
- pass
- def run(self, command, args):
- if self.process == None: self.start()
- 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()
- r = RecvPacket()
- complete = False
- result_string = ""
- result_code = None
- while not complete:
- ro, rw, re = select.select([self.process.fromchild], [], [], None)
- if not ro and not rw and not re:
- break
- if self.process.fromchild in ro:
- recv = self.process.fromchild.read()
- if recv == "": break
- tv = r.process_data(recv)
- if tv != None:
- cmdnum, error, length, is_last, result = tv
- if result_code == None: result_code = int(error)
- result_string += result
- if is_last:
- complete = True
- else:
- # in case there is anything left over we
- # didn't parse
- r = RecvPacket(r.buffer)
- return result_code, result_string
+ def __init__(self, base_command):
+ self.command = "%s automate stdio" % (base_command)
+ self.process = None
+ def __del__(self):
+ self.stop()
+ self.process = None
+ def start(self):
+ self.process = popen2.Popen3(self.command)
+ set_nonblocking(self.process.fromchild)
+ def stop(self):
+ if not self.process: return
+ try:
+ self.process.tochild.close()
+ self.process.fromchild.close()
+ if self.process.poll() == -1:
+ # the process is still running, so kill it.
+ os.kill(self.process.pid, signal.SIGKILL)
+ self.process.wait()
+ except:
+ pass
+ def run(self, command, args):
+ if self.process == None: self.start()
+ 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()
+ r = RecvPacket()
+ complete = False
+ result_string = ""
+ result_code = None
+ while not complete:
+ ro, rw, re = select.select([self.process.fromchild], [], [], None)
+ if not ro and not rw and not re:
+ break
+ if self.process.fromchild in ro:
+ recv = self.process.fromchild.read()
+ if recv == "": break
+ tv = r.process_data(recv)
+ if tv != None:
+ cmdnum, error, length, is_last, result = tv
+ if result_code == None: result_code = int(error)
+ result_string += result
+ if is_last:
+ complete = True
+ else:
+ # in case there is anything left over we
+ # didn't parse
+ r = RecvPacket(r.buffer)
+ return result_code, result_string
class Monotone:
- def __init__(self, mt, dbfile):
- self.mt = mt
- self.dbfile = dbfile
- self.base_command = "%s --db=%s" % (self.mt, pipes.quote(self.dbfile))
- self.automate = Automation(self.base_command)
- def branches(self):
- result = utility.run_command(self.base_command + " ls branches")
- if result['exitcode'] != 0:
- raise Exception("Unable to list branches: %s" % (result['childerr']))
- else:
- return filter(None, result['fromchild'].split('\n'))
- def tags(self):
- result = utility.run_command(self.base_command + " ls tags")
- if result['exitcode'] != 0:
- raise Exception("Unable to list tags: %s" % (result['childerr']))
- else:
- return map(lambda x: x.split(' ', 2), filter(None, result['fromchild'].split('\n')))
- def heads(self, branch):
- error, result = self.automate.run('heads', [branch])
- if error != 0:
- raise Exception("Unable to get list of heads")
- else:
- return filter(None, result.split('\n'))
- def basic_io_parser(self, data):
- """returns a list of lists of (key, value) tuples. hashes are returned with []s around
- them; strings are returned raw."""
- def unescape_string_value(str):
- rv = ""
- is_terminated = False
- in_escape = False
- if str[0] != '"':
- raise Exception("basic_io parse error; not a string.")
- for c in str[1:]:
- if in_escape:
- if c != '\\' and c != '\"':
- raise Exception(r'basic_io parse error; expected \" or \\')
- rv += c
- in_escape = False
- else:
- if c == '\\':
- in_escape = True
- elif c == '"':
- if is_terminated:
- raise Exception("basic_io parse error; string ends twice!")
- is_terminated = True
- else:
- rv += c
- return is_terminated, rv
- rv = []
- stanza = []
- ongoing_string = None
- for line in data.split('\n'):
- if not ongoing_string:
- if line == '' and len(stanza) != 0:
- rv.append(stanza)
- stanza = []
- m = basic_io_hex_re.match(line)
- if m:
- key, value = m.groups()
- stanza.append((key, value))
- continue
- m = basic_io_string_re.match(line)
- if m:
- key, value = m.groups()
- is_terminated, e_value = unescape_string_value(value)
- if not is_terminated: ongoing_string = value
- else: stanza.append((key, e_value))
- continue
- else:
- ongoing_string += '\n' + line
- is_terminated, e_value = unescape_string_value(ongoing_string)
- if is_terminated:
- stanza.append((key, e_value))
- ongoing_string = None
- return rv
- def certs(self, id):
- error, data = self.automate.run('certs', [id])
- if error != 0: raise Exception("Error obtaining cert for %s: %s" % (id, data))
- return self.basic_io_parser(data)
- def ancestors(self, ids):
- error, result = self.automate.run('ancestors', ids)
- if error != 0:
- raise Exception("Unable to get ancestors")
- else:
- return filter(None, result.split('\n'))
- def toposort(self, ids):
- error, result = self.automate.run('toposort', ids)
- if error != 0:
- raise Exception("Unable to toposort")
- else:
- return filter(None, result.split('\n'))
- def revision(self, id):
- error, result = self.automate.run('get_revision', [id])
- if error != 0:
- raise Exception("Unable to get revision: %s" % id)
- rv = {}
- cv = []
- for line in result.split('\n'):
- if not line:
- if len(cv) != 0:
- stanza_type = cv[0][0]
- if not rv.has_key(stanza_type): rv[stanza_type] = []
- rv[stanza_type].append(cv)
- cv = []
- continue
- m = basic_io_re.match(line)
- if not m: continue
- cv.append(m.groups())
- return rv
- def manifest(self, id):
- error, result = self.automate.run('get_manifest', [id])
- if error != 0:
- raise Exception("Unable to get manifest: %s" % id)
- rv = []
- for line in result.split('\n'):
- m = manifest_entry_re.match(line)
- if not m: continue
- rv.append(m.groups())
- return rv
- def file(self, id):
- error, result = self.automate.run('get_file', [id])
- if error != 0:
- raise Exception("Unable to get file: %s" % id)
- else:
- return result
+ def __init__(self, mt, dbfile):
+ self.mt = mt
+ self.dbfile = dbfile
+ self.base_command = "%s --db=%s" % (self.mt, pipes.quote(self.dbfile))
+ self.automate = Automation(self.base_command)
+ def branches(self):
+ result = utility.run_command(self.base_command + " ls branches")
+ if result['exitcode'] != 0:
+ raise Exception("Unable to list branches: %s" % (result['childerr']))
+ else:
+ return filter(None, result['fromchild'].split('\n'))
+ def tags(self):
+ result = utility.run_command(self.base_command + " ls tags")
+ if result['exitcode'] != 0:
+ raise Exception("Unable to list tags: %s" % (result['childerr']))
+ else:
+ return map(lambda x: x.split(' ', 2), filter(None, result['fromchild'].split('\n')))
+ def heads(self, branch):
+ error, result = self.automate.run('heads', [branch])
+ if error != 0:
+ raise Exception("Unable to get list of heads")
+ else:
+ return filter(None, result.split('\n'))
+ def basic_io_parser(self, data):
+ """returns a list of lists of (key, value) tuples. hashes are returned with []s around
+ them; strings are returned raw."""
+ def unescape_string_value(str):
+ rv = ""
+ is_terminated = False
+ in_escape = False
+ if str[0] != '"':
+ raise Exception("basic_io parse error; not a string.")
+ for c in str[1:]:
+ if in_escape:
+ if c != '\\' and c != '\"':
+ raise Exception(r'basic_io parse error; expected \" or \\')
+ rv += c
+ in_escape = False
+ else:
+ if c == '\\':
+ in_escape = True
+ elif c == '"':
+ if is_terminated:
+ raise Exception("basic_io parse error; string ends twice!")
+ is_terminated = True
+ else:
+ rv += c
+ return is_terminated, rv
+ rv = []
+ stanza = []
+ ongoing_string = None
+ for line in data.split('\n'):
+ if not ongoing_string:
+ if line == '' and len(stanza) != 0:
+ rv.append(stanza)
+ stanza = []
+ m = basic_io_hex_re.match(line)
+ if m:
+ key, value = m.groups()
+ stanza.append((key, value))
+ continue
+ m = basic_io_string_re.match(line)
+ if m:
+ key, value = m.groups()
+ is_terminated, e_value = unescape_string_value(value)
+ if not is_terminated: ongoing_string = value
+ else: stanza.append((key, e_value))
+ continue
+ else:
+ ongoing_string += '\n' + line
+ is_terminated, e_value = unescape_string_value(ongoing_string)
+ if is_terminated:
+ stanza.append((key, e_value))
+ ongoing_string = None
+ return rv
+ def certs(self, id):
+ error, data = self.automate.run('certs', [id])
+ if error != 0: raise Exception("Error obtaining cert for %s: %s" % (id, data))
+ return self.basic_io_parser(data)
+ def ancestors(self, ids):
+ error, result = self.automate.run('ancestors', ids)
+ if error != 0:
+ raise Exception("Unable to get ancestors")
+ else:
+ return filter(None, result.split('\n'))
+ def toposort(self, ids):
+ error, result = self.automate.run('toposort', ids)
+ if error != 0:
+ raise Exception("Unable to toposort")
+ else:
+ return filter(None, result.split('\n'))
+ def revision(self, id):
+ error, result = self.automate.run('get_revision', [id])
+ if error != 0:
+ raise Exception("Unable to get revision: %s" % id)
+ rv = {}
+ cv = []
+ for line in result.split('\n'):
+ if not line:
+ if len(cv) != 0:
+ stanza_type = cv[0][0]
+ if not rv.has_key(stanza_type): rv[stanza_type] = []
+ rv[stanza_type].append(cv)
+ cv = []
+ continue
+ m = basic_io_re.match(line)
+ if not m: continue
+ cv.append(m.groups())
+ return rv
+ def manifest(self, id):
+ error, result = self.automate.run('get_manifest', [id])
+ if error != 0:
+ raise Exception("Unable to get manifest: %s" % id)
+ rv = []
+ for line in result.split('\n'):
+ m = manifest_entry_re.match(line)
+ if not m: continue
+ rv.append(m.groups())
+ return rv
+ def file(self, id):
+ error, result = self.automate.run('get_file', [id])
+ if error != 0:
+ raise Exception("Unable to get file: %s" % id)
+ else:
+ return result
- def annotate(self, id, file):
- result = utility.run_command(self.base_command + " annotate --revision=%s %s" % (pipes.quote(id), pipes.quote(file)))
- if result['exitcode'] != 0:
- raise Exception("Unable to annotate file: %s using command '%s'" % (result['childerr'], result['run_command']))
- else:
- return result['fromchild']
-
- def diff(self, rev_from, rev_to, files=None):
- command = self.base_command + " diff -r %s -r %s" % (pipes.quote(rev_from), pipes.quote(rev_to))
- if files != None: command += ' ' + ' '.join(map(pipes.quote, files))
- import syslog
- syslog.syslog(command)
- result = utility.run_command(command)
- if result['exitcode'] != 0:
- raise Exception("Unable to calculate diff: %s" % (result['childerr']))
- else:
- return result['fromchild']
- def log(self, ids, limit=0):
- rv = []
- entry = None
- command = self.base_command + " log " + ' '.join(map(lambda x: '-r ' + pipes.quote(x), ids))
- if limit > 0: command += " --last=%d" % (limit)
- iterator = utility.iter_command(command)
- for line in iterator:
- if dash_re.match(line):
- entry = {}
- if entry == None: continue
- if not line:
- rv.append(entry)
- entry = None
- continue
- m = log_entry_re.match(line)
- if m:
- attr, value = m.groups()
- if not entry.has_key(attr): entry[attr] = []
- entry[attr].append(value)
- # clean up; otherwise we'll leak filehandlers
- map(None, iterator)
- if entry: rv.append(entry)
- return rv
- def ancestry_graph(self, graphopts, id, limit=0):
- def dot_escape(s):
- # kinda paranoid, should probably revise later
- permitted=string.digits + string.letters + ' -<>-:,address@hidden&.+_~?/'
- return ''.join(filter(lambda x: x in permitted, s))
- graphdir = graphopts['directory']
- graphuri = graphopts['uri']
- graph_id = "%s.%d" % (id, limit)
- rv = {
- 'dot_file' : os.path.join(graphdir, graph_id + ".dot"),
- 'image_file' : os.path.join(graphdir, graph_id + ".png"),
- 'imagemap_file' : os.path.join(graphdir, graph_id + ".html"),
- 'dot_uri' : "%s/%s.dot" % (graphuri, urllib.quote(graph_id)),
- 'image_uri' : "%s/%s.png" % (graphuri, urllib.quote(graph_id)),
- 'imagemap_uri' : "%s/%s.html" % (graphuri, urllib.quote(graph_id)),
- }
- need_access = ['dot_file', 'image_file', 'imagemap_file']
- missing = filter(lambda x: x != True, map(lambda x: os.access(rv[x], os.R_OK), need_access))
- if len(missing) == 0:
- rv['cached'] = True
- return rv
- contents = 'digraph ancestry {\nratio=compress\nnodesep=0.1\nranksep=0.2\nedge [dir=back];\n'
- revisions = {}
- for attrs in self.log([id], limit):
- if not attrs.has_key("Revision") or not attrs.has_key("Ancestor"):
- continue
- revision = attrs['Revision'][0]
- revisions[revision] = attrs
- for ancestor in attrs['Ancestor']:
- if len(ancestor) == 0: continue
- if not revisions.has_key(ancestor): revisions[ancestor] = None
- contents += '"%s"->"%s"[href="getdiff.py?id1=%s&id2=%s"]\n' % (urllib.quote(revision), urllib.quote(ancestor), urllib.quote(ancestor), urllib.quote(revision))
- for revision in revisions.keys():
- label = "%s..." % (revision[0:8])
- attrs = revisions[revision]
- if attrs == None:
- # fill in the gaps; would be nice to clean this up.
- # shouldn't take long, anyway.
- attrs = self.log([revision], 1)[0]
- if attrs.has_key('Date'):
- d = dot_escape(attrs['Date'][0])
- d = d[0:d.find("T")]
- label += " on %s" % d
- if attrs.has_key('Author'):
- label += "\\n%s" % (dot_escape(attrs['Author'][0]))
- override_fillcolor = colour_from_string(attrs['Author'][0])
- else: override_fillcolor = None
- #opts = 'fontname=Windsor,fontsize=8,shape=box,href="revision.psp?id=%s",label="%s"' % (urllib.quote(revision), label)
- opts = 'label="%s"' % label #revision[0:8]
- for opt in graphopts['nodeopts']:
- if opt == 'fillcolor' and override_fillcolor != None: continue
- opts += ',%s="%s"' % (opt, graphopts['nodeopts'][opt])
- if revision == id: opts += ",color=blue"
- opts += ',href="revision.psp?id=%s"' % urllib.quote(revision)
- if override_fillcolor != None: opts += ',fillcolor="#%s"' % (override_fillcolor)
- #opts += ',tooltip="by %s at %s on %s"' % (dot_escape(attrs['ChangeLog'][0]),
- # dot_escape(attrs['Date'][0]),
- # dot_escape(attrs['Branch'][0]))
- contents += '"%s" [%s]\n' % (revision, opts)
- contents += "}\n"
- open(rv['dot_file'], 'w').write(contents)
- os.system("%s -Tcmapx -o %s -Tpng -o %s %s" % (graphopts['dot'], pipes.quote(rv['imagemap_file']), rv['image_file'], rv['dot_file']))
- rv['cached'] = False
- return rv
+ def annotate(self, id, file):
+ result = utility.run_command(self.base_command + " annotate --revision=%s %s" % (pipes.quote(id), pipes.quote(file)))
+ if result['exitcode'] != 0:
+ raise Exception("Unable to annotate file: %s using command '%s'" % (result['childerr'], result['run_command']))
+ else:
+ return result['fromchild']
+
+ def diff(self, rev_from, rev_to, files=None):
+ command = self.base_command + " diff -r %s -r %s" % (pipes.quote(rev_from), pipes.quote(rev_to))
+ if files != None: command += ' ' + ' '.join(map(pipes.quote, files))
+ import syslog
+ syslog.syslog(command)
+ result = utility.run_command(command)
+ if result['exitcode'] != 0:
+ raise Exception("Unable to calculate diff: %s" % (result['childerr']))
+ else:
+ return result['fromchild']
+ def log(self, ids, limit=0):
+ rv = []
+ entry = None
+ command = self.base_command + " log " + ' '.join(map(lambda x: '-r ' + pipes.quote(x), ids))
+ if limit > 0: command += " --last=%d" % (limit)
+ iterator = utility.iter_command(command)
+ for line in iterator:
+ if dash_re.match(line):
+ entry = {}
+ if entry == None: continue
+ if not line:
+ rv.append(entry)
+ entry = None
+ continue
+ m = log_entry_re.match(line)
+ if m:
+ attr, value = m.groups()
+ if not entry.has_key(attr): entry[attr] = []
+ entry[attr].append(value)
+ # clean up; otherwise we'll leak filehandlers
+ map(None, iterator)
+ if entry: rv.append(entry)
+ return rv
+ def ancestry_graph(self, graphopts, id, limit=0):
+ def dot_escape(s):
+ # kinda paranoid, should probably revise later
+ permitted=string.digits + string.letters + ' -<>-:,address@hidden&.+_~?/'
+ return ''.join(filter(lambda x: x in permitted, s))
+ graphdir = graphopts['directory']
+ graphuri = graphopts['uri']
+ graph_id = "%s.%d" % (id, limit)
+ rv = {
+ 'dot_file' : os.path.join(graphdir, graph_id + ".dot"),
+ 'image_file' : os.path.join(graphdir, graph_id + ".png"),
+ 'imagemap_file' : os.path.join(graphdir, graph_id + ".html"),
+ 'dot_uri' : "%s/%s.dot" % (graphuri, urllib.quote(graph_id)),
+ 'image_uri' : "%s/%s.png" % (graphuri, urllib.quote(graph_id)),
+ 'imagemap_uri' : "%s/%s.html" % (graphuri, urllib.quote(graph_id)),
+ }
+ need_access = ['dot_file', 'image_file', 'imagemap_file']
+ missing = filter(lambda x: x != True, map(lambda x: os.access(rv[x], os.R_OK), need_access))
+ if len(missing) == 0:
+ rv['cached'] = True
+ return rv
+ contents = 'digraph ancestry {\nratio=compress\nnodesep=0.1\nranksep=0.2\nedge [dir=back];\n'
+ revisions = {}
+ for attrs in self.log([id], limit):
+ if not attrs.has_key("Revision") or not attrs.has_key("Ancestor"):
+ continue
+ revision = attrs['Revision'][0]
+ revisions[revision] = attrs
+ for ancestor in attrs['Ancestor']:
+ if len(ancestor) == 0: continue
+ if not revisions.has_key(ancestor): revisions[ancestor] = None
+ contents += '"%s"->"%s"[href="getdiff.py?id1=%s&id2=%s"]\n' % (urllib.quote(revision), urllib.quote(ancestor), urllib.quote(ancestor), urllib.quote(revision))
+ for revision in revisions.keys():
+ label = "%s..." % (revision[0:8])
+ attrs = revisions[revision]
+ if attrs == None:
+ # fill in the gaps; would be nice to clean this up.
+ # shouldn't take long, anyway.
+ attrs = self.log([revision], 1)[0]
+ if attrs.has_key('Date'):
+ d = dot_escape(attrs['Date'][0])
+ d = d[0:d.find("T")]
+ label += " on %s" % d
+ if attrs.has_key('Author'):
+ label += "\\n%s" % (dot_escape(attrs['Author'][0]))
+ override_fillcolor = colour_from_string(attrs['Author'][0])
+ else: override_fillcolor = None
+ #opts = 'fontname=Windsor,fontsize=8,shape=box,href="revision.psp?id=%s",label="%s"' % (urllib.quote(revision), label)
+ opts = 'label="%s"' % label #revision[0:8]
+ for opt in graphopts['nodeopts']:
+ if opt == 'fillcolor' and override_fillcolor != None: continue
+ opts += ',%s="%s"' % (opt, graphopts['nodeopts'][opt])
+ if revision == id: opts += ",color=blue"
+ opts += ',href="revision.psp?id=%s"' % urllib.quote(revision)
+ if override_fillcolor != None: opts += ',fillcolor="#%s"' % (override_fillcolor)
+ #opts += ',tooltip="by %s at %s on %s"' % (dot_escape(attrs['ChangeLog'][0]),
+ # dot_escape(attrs['Date'][0]),
+ # dot_escape(attrs['Branch'][0]))
+ contents += '"%s" [%s]\n' % (revision, opts)
+ contents += "}\n"
+ open(rv['dot_file'], 'w').write(contents)
+ os.system("%s -Tcmapx -o %s -Tpng -o %s %s" % (graphopts['dot'], pipes.quote(rv['imagemap_file']), rv['image_file'], rv['dot_file']))
+ rv['cached'] = False
+ return rv
def is_valid_id(s):
- return len(s) == 40 and id_re.match(s) != None
+ return len(s) == 40 and id_re.match(s) != None
import popen2
import select
from utility import set_nonblocking
-
+
packet_header_re = re.compile(r'(\d+):(\d+):([lm]):(\d+):')
class RecvPacket:
- "A packet received from monotone automate stdio"
- def __init__(self, init_buffer=""):
- self.buffer = init_buffer
- self.cmdnum = None
- self.error = None
- self.length = None
- self.is_last = None
- self.result = ""
- def process_data(self, data):
- self.buffer += data
- if self.length == None:
- m = packet_header_re.match(data)
- if m:
- self.cmdnum, self.error, pstate, self.length = m.groups()
- self.length = int(self.length)
- self.is_last = pstate == "l"
- self.buffer = self.buffer[m.end(m.lastindex)+1:]
- if self.length != None and len(self.result) < self.length:
- needed = self.length - len(self.result)
- if len(self.buffer) >= needed:
- available = needed
- else:
- available = len(self.buffer)
- self.result += self.buffer[:available]
- self.buffer = self.buffer[available:]
- if len(self.result) == self.length:
- return (self.cmdnum, self.error, self.length, self.is_last, self.result)
- else: return None
+ "A packet received from monotone automate stdio"
+ def __init__(self, init_buffer=""):
+ self.buffer = init_buffer
+ self.cmdnum = None
+ self.error = None
+ self.length = None
+ self.is_last = None
+ self.result = ""
+ def process_data(self, data):
+ self.buffer += data
+ if self.length == None:
+ m = packet_header_re.match(data)
+ if m:
+ self.cmdnum, self.error, pstate, self.length = m.groups()
+ self.length = int(self.length)
+ self.is_last = pstate == "l"
+ self.buffer = self.buffer[m.end(m.lastindex)+1:]
+ if self.length != None and len(self.result) < self.length:
+ needed = self.length - len(self.result)
+ if len(self.buffer) >= needed:
+ available = needed
+ else:
+ available = len(self.buffer)
+ self.result += self.buffer[:available]
+ self.buffer = self.buffer[available:]
+ if len(self.result) == self.length:
+ return (self.cmdnum, self.error, self.length, self.is_last, self.result)
+ else: return None
============================================================
--- revision.psp 54379dc8fb3e0fd02a084fcd92bdb0f41a85de61
+++ revision.psp 82d35235b0c254d2ec2552d53b72f17242f462b1
@@ -11,47 +11,47 @@
#
def prettify(s):
- return ' '.join(map(lambda x: hq(x[0].upper() + x[1:]), s.replace("_", " ").split(" ")))
+ return ' '.join(map(lambda x: hq(x[0].upper() + x[1:]), s.replace("_", " ").split(" ")))
psp.set_error_page("error.psp")
if not form.has_key('id'):
- raise Exception("No revision ID specified.")
+ raise Exception("No revision ID specified.")
id = form['id']
if not monotone.is_valid_id(id):
- raise Exception("Specified revision ID is not valid.")
+ raise Exception("Specified revision ID is not valid.")
# read certificates
certs = mt.certs(id)
branches, cert_table = [], ""
for cert in certs:
- name, value = None, None
- for k, v in cert:
- if k == "name": name = v
- elif k == "value": value = v
- if name == None or value == None: continue
- if name == "branch":
- branches.append(value)
- value = link("branch", value)
- else:
- value = ' '.join(map(hq, value.split('\n')))
- cert_table += '
%s
%s
' % (prettify(name), value)
+ name, value = None, None
+ for k, v in cert:
+ if k == "name": name = v
+ elif k == "value": value = v
+ if name == None or value == None: continue
+ if name == "branch":
+ branches.append(value)
+ value = link("branch", value)
+ else:
+ value = ' '.join(map(hq, value.split('\n')))
+ cert_table += '