#
#
# patch "index.psp"
# from [43de300221be3969e8017655a613f196e9633466]
# to [91b20f95e6c81b15fa20307e27ab590700ea8200]
#
# patch "monotone.py"
# from [d29711cd1c0d4ac4e3f48ca98444bba31f25a399]
# to [fe810be90d92a76684a2035d9e7aa6b1a77347c9]
#
# patch "revision.psp"
# from [d9bd56ea820aa74a61bd5e640b80c3f722f1108b]
# to [51027eadc7443245e40030623274abe7202eef2b]
#
# patch "wrapper.py"
# from [7a219e0f0134aa363238ed8fdfa0175d03410d89]
# to [7444ff00e621412e16056b61cc1e9663219b417b]
#
============================================================
--- index.psp 43de300221be3969e8017655a613f196e9633466
+++ index.psp 91b20f95e6c81b15fa20307e27ab590700ea8200
@@ -15,8 +15,8 @@
hq = common.html_escape()
mt = Monotone(config.monotone, config.dbfile)
-
branches = mt.branches()
+del mt
%>
@@ -48,6 +48,8 @@
<%
+
req.write(template.footer(info))
+
%>
============================================================
--- monotone.py d29711cd1c0d4ac4e3f48ca98444bba31f25a399
+++ monotone.py fe810be90d92a76684a2035d9e7aa6b1a77347c9
@@ -16,14 +16,12 @@
#
id_re = re.compile(r'^[0-9a-f]+$')
-
dash_re = re.compile(r'^-+$')
cert_value_re = re.compile(r'^(\S*) *: (.*)$')
-
basic_io_re = re.compile(r'^ *(\S+) [\"\[](.*)[\"\]]$')
-
+basic_io_hex_re = re.compile(r'^ *(\S+) (\[[0-9A-Za-z]+\])$')
+basic_io_string_re = re.compile(r'^ *(\S+) (\".*)$')
manifest_entry_re = re.compile(r'^(\S+) *(.*)$')
-
log_entry_re = re.compile(r'^(\S+): (.*)$')
def colour_from_string(str):
@@ -35,11 +33,61 @@
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, mt, dbfile):
+ self.mt = mt
+ self.dbfile = dbfile
+ self.command = "%s --db=%s automate stdio" % (self.mt, pipes.quote(self.dbfile))
+ #self.command = "/bin/cat"
+ self.process = None
+ def __del__(self):
+ if self.process:
+ try:
+ self.process.tochild.close()
+ self.process.fromchild.close()
+ self.process.wait()
+ except: pass
+ self.process = None
+ def start(self):
+ self.process = popen2.Popen3(self.command)
+ set_nonblocking(self.process.fromchild)
+ 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.mt, self.dbfile)
def branches(self):
result = utility.run_command(self.base_command + " ls branches")
if result['exitcode'] != 0:
@@ -53,39 +101,64 @@
else:
return map(lambda x: x.split(' ', 2), filter(None, result['fromchild'].split('\n')))
def heads(self, branch):
- result = utility.run_command(self.base_command + " automate heads %s" % (pipes.quote(branch)))
- if result['exitcode'] != 0:
+ error, result = self.automate.run('heads', [branch])
+ if error != 0:
raise Exception("Unable to get list of heads: %s" % (result['childerr']))
else:
- return filter(None, result['fromchild'].split('\n'))
- def certs(self, id):
+ return filter(None, result.split('\n'))
+ def basic_io_parser(self, data):
+ "returns a list of lists of (key, type, value) tuples."
+ 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("basic_io parse error; expected \" or \\")
+ rv += c
+ in_escape = False
+ else:
+ if c == '\\': in_escape = True
+ if c == '"':
+ if is_terminated:
+ raise Exception("basic_io parse error; string ends twice!")
+ is_terminated = True
+ else: rv += c
+ return is_terminated, rv
rv = []
- c_cert = None
- c_key = c_value = None
- for line in utility.iter_command(self.base_command + " ls certs %s" % (pipes.quote(id))):
- if dash_re.match(line):
- if c_cert:
- if c_key != None:
- c_cert[c_key] = c_value
- c_key = c_value = None
- rv.append(c_cert)
- c_cert = {}
- elif c_cert != None:
- m = cert_value_re.match(line)
- if not m: continue
- key, value = m.groups()
- if key != '':
- # then we don't have a continuation
- if c_key != None: c_cert[c_key] = c_value
- c_key, c_value = key, [value]
- else:
- c_value.append(value)
- if c_cert:
- if c_key != None:
- c_cert[c_key] = c_value
- c_key = c_value = None
- rv.append(c_cert)
+ 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 revision(self, id):
rv = {}
cv = []
@@ -256,52 +329,3 @@
if len(self.result) == self.length:
return (self.cmdnum, self.error, self.length, self.is_last, self.result)
else: return None
-
-class Automation:
- def __init__(self, mt, dbfile):
- self.mt = mt
- self.dbfile = dbfile
- self.command = "%s --db=%s automate stdio" % (self.mt, pipes.quote(self.dbfile))
- #self.command = "/bin/cat"
- self.process = None
- def __del__(self):
- if self.process:
- try:
- self.process.tochild.close()
- self.process.fromchild.close()
- self.process.wait()
- except: pass
- self.process = None
- def start(self):
- self.process = popen2.Popen3(self.command)
- set_nonblocking(self.process.fromchild)
- 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 = 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
-
============================================================
--- revision.psp d9bd56ea820aa74a61bd5e640b80c3f722f1108b
+++ revision.psp 51027eadc7443245e40030623274abe7202eef2b
@@ -7,6 +7,7 @@
import template
from template import header,footer
from monotone import Monotone
+reload(monotone)
from common import link
#
@@ -55,8 +56,11 @@
<%
certs = mt.certs(id)
for cert in certs:
- if not cert.has_key("Name") or not cert.has_key("Value"): continue
- name, value = cert["Name"][0], hq(cert["Value"])
+ name, value = None, None
+ for k, v in cert:
+ if k == "name": name = v
+ elif k == "value": value = '
'.join(v.split('\n'))
+ if name == None or value == None: continue
if name == "branch":
value = link("branch", value)
req.write('