# # # add_file "tracvc/mtn/automate_new.py" # content [adb005de90937c315afd490b45505efd56cd43f9] # ============================================================ --- tracvc/mtn/automate_new.py adb005de90937c315afd490b45505efd56cd43f9 +++ tracvc/mtn/automate_new.py adb005de90937c315afd490b45505efd56cd43f9 @@ -0,0 +1,127 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Trac Plugin for Monotone + +Copyright 2007 Thomas Moschny (address@hidden) + +""" + +from threading import Thread, Event +from twisted.internet import reactor +from twisted.internet.protocol import ProcessProtocol +import re +from collections import deque + +PACKET_HEADER = re.compile(r''' + (?P\d+): + (?P\d+): + (?P(m|l)): + (?P\d+): +''', re.VERBOSE) + + +class AutomateProtocol(ProcessProtocol): + + def __init__(self): + self.connected = False + self.queue = deque() + self.cur = None + self.data = '' + + # header + self.status = 0 + self.size = 0 + self.last = 'l' + + def outReceived(self, data): + self.data += data + while True: + if self.size: + length = min(self.size, len(self.data)) + self.cur.result += self.data[:length] + self.data = self.data[length:] + self.size -= length + if self.size == 0: + if self.last == 'l': + self.cur.finished.set() + continue + else: + m = PACKET_HEADER.match(self.data) + if m: + if self.last == 'l': + self.cur = self.queue.popleft() + self.status, _size, self.last = \ + m.group('status', 'size', 'last') + self.size = int(_size) + self.data = self.data[m.end():] + continue + break + + + def connectionMade(self): + self.connected = True + + def errReceived(self, data): + print "errReceived: %s" % data + pass + + def inConnectionLost(self): + print "inConnectionLost" + pass + + def outConnectionLost(self): + print "outConnectionLost" + pass + + def errConnectionLost(self): + print "errConnectionLost" + pass + + def processEnded(self, status): + print "processEnded, status %d" % status.value.exitCode + pass + + def cmd(self, query): + if self.connected: + self.queue.append(query) + self.transport.write(query.cmd) + else: # postpone, fixme: avoid endless loop + reactor.callLater(0, self.cmd, query) + + +class Query(object): + + def __init__(self, cmd): + self.cmd = cmd + self.result = '' + self.finished = Event() + + +class AutomateThread(Thread): + + def __init__(self, binary, database): + self.binary = binary + self.database = database + self.protocol = AutomateProtocol() + Thread.__init__(self) + + def run(self): + reactor.spawnProcess(self.protocol, self.binary, + [self.binary, '--norc', '--root=.', + '--automate-stdio-size=%d' % (100*1024), + '--db=%s' % self.database, + 'automate', 'stdio']) + # we don't run the reactor in the main thread, so it can't use + # signals + reactor.run(installSignalHandlers=0) + + + def cmd(self, cmd): + query = Query(cmd) + reactor.callFromThread(self.protocol.cmd, query) + query.finished.wait() + return query.result + + def stop(self): + reactor.callFromThread(reactor.stop)