# # patch "monotone.py" # from [721acaadb1b9b3d40b62061db53f4bbb4ce4cb4c] # to [3931efcc05198a4ab183247f0453edf70a13ab6b] # ======================================================================== --- monotone.py 721acaadb1b9b3d40b62061db53f4bbb4ce4cb4c +++ monotone.py 3931efcc05198a4ab183247f0453edf70a13ab6b @@ -1,4 +1,5 @@ import subprocess +import threading import os.path import re @@ -36,7 +37,11 @@ def __init__(self, db, executable="monotone"): self.db = db self.executable = executable + self.process = None + def __del__(self): + self.ensure_stopped() + def init_db(self): self.run_monotone(["db", "init"]) @@ -48,18 +53,17 @@ self.init_db() def revisions_list(self): - output = self.run_monotone(["automate", "select", "i:"]) + output = self.automate("select", "i:") return output.split() def toposort(self, revisions): - output = self.run_monotone(["automate", "toposort", "address@hidden"], - "\n".join(revisions) + "\n") + output = self.automate("toposort", *revisions) sorted = output.split() assert len(sorted) == len(revisions) return sorted def get_revision(self, rid): - return self.run_monotone(["automate", "get_revision", rid]) + return self.automate("get_revision", rid) def get_pubkey_packet(self, keyid): return self.run_monotone(["pubkey", keyid]) @@ -92,7 +96,7 @@ return packets def key_names(self): - output = self.run_monotone(["automate", "keys"]) + output = self.automate("keys") print output keys_parsed = self.basic_io_parser(output) ids = {} @@ -105,6 +109,7 @@ # returns output as a string, raises an error on error def run_monotone(self, args, input=None): + self.ensure_stopped() process = subprocess.Popen([self.executable, "--db", self.db] + args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, @@ -113,7 +118,48 @@ if process.returncode: raise MonotoneError, stderr return stdout + + def ensure_running(self): + if not self.process: + self.process = subprocess.Popen([self.executable, "--db", self.db, "automate", "stdio"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + def ensure_stopped(self): + if self.process: + self.process.stdin.close() + self.process.wait() + self.process = None + + def automate(self, *args): + def formater(args): + length_prefixed_args = [str(len(command)) + ":" + command for command in args] + return "l" + "".join(length_prefixed_args) + "e" + def parser(pipe): + def get_fields(): + for i in range(0,4): + field = "" + sep = pipe.read(1) + while not sep == ":": + field += sep + sep = pipe.read(1) + yield field + + data = "" + while 1: + cmd, status, cont, size = get_fields() + data += pipe.read(int(size)) + if cont != "m": break + return data + + self.ensure_running() + stdin_write = threading.Thread(target=self.process.stdin.write, args=[formater(args)]) + stdin_write.setDaemon(True) + stdin_write.start() + return parser(self.process.stdout) + # feeds stuff into 'monotone read' def feeder(self): args = [self.executable, "--db", self.db, "read"]