hgclient.py
109 lines
| 3.1 KiB
| text/x-python
|
PythonLexer
/ contrib / hgclient.py
Yuya Nishihara
|
r22566 | # A minimal client for Mercurial's command server | ||
Yuya Nishihara
|
r22993 | import os, sys, signal, struct, socket, subprocess, time, cStringIO | ||
Yuya Nishihara
|
r22566 | |||
Yuya Nishihara
|
r22992 | def connectpipe(path=None): | ||
Yuya Nishihara
|
r22566 | cmdline = ['hg', 'serve', '--cmdserver', 'pipe'] | ||
if path: | ||||
cmdline += ['-R', path] | ||||
server = subprocess.Popen(cmdline, stdin=subprocess.PIPE, | ||||
stdout=subprocess.PIPE) | ||||
return server | ||||
Yuya Nishihara
|
r22993 | class unixconnection(object): | ||
def __init__(self, sockpath): | ||||
self.sock = sock = socket.socket(socket.AF_UNIX) | ||||
sock.connect(sockpath) | ||||
self.stdin = sock.makefile('wb') | ||||
self.stdout = sock.makefile('rb') | ||||
def wait(self): | ||||
self.stdin.close() | ||||
self.stdout.close() | ||||
self.sock.close() | ||||
class unixserver(object): | ||||
def __init__(self, sockpath, logpath=None, repopath=None): | ||||
self.sockpath = sockpath | ||||
cmdline = ['hg', 'serve', '--cmdserver', 'unix', '-a', sockpath] | ||||
if repopath: | ||||
cmdline += ['-R', repopath] | ||||
if logpath: | ||||
stdout = open(logpath, 'a') | ||||
stderr = subprocess.STDOUT | ||||
else: | ||||
stdout = stderr = None | ||||
self.server = subprocess.Popen(cmdline, stdout=stdout, stderr=stderr) | ||||
# wait for listen() | ||||
while self.server.poll() is None: | ||||
if os.path.exists(sockpath): | ||||
break | ||||
time.sleep(0.1) | ||||
def connect(self): | ||||
return unixconnection(self.sockpath) | ||||
def shutdown(self): | ||||
os.kill(self.server.pid, signal.SIGTERM) | ||||
self.server.wait() | ||||
Yuya Nishihara
|
r22566 | def writeblock(server, data): | ||
server.stdin.write(struct.pack('>I', len(data))) | ||||
server.stdin.write(data) | ||||
server.stdin.flush() | ||||
def readchannel(server): | ||||
data = server.stdout.read(5) | ||||
if not data: | ||||
raise EOFError | ||||
channel, length = struct.unpack('>cI', data) | ||||
if channel in 'IL': | ||||
return channel, length | ||||
else: | ||||
return channel, server.stdout.read(length) | ||||
def sep(text): | ||||
return text.replace('\\', '/') | ||||
def runcommand(server, args, output=sys.stdout, error=sys.stderr, input=None, | ||||
outfilter=lambda x: x): | ||||
Yuya Nishihara
|
r22572 | print '*** runcommand', ' '.join(args) | ||
Yuya Nishihara
|
r22566 | sys.stdout.flush() | ||
server.stdin.write('runcommand\n') | ||||
writeblock(server, '\0'.join(args)) | ||||
if not input: | ||||
input = cStringIO.StringIO() | ||||
while True: | ||||
ch, data = readchannel(server) | ||||
if ch == 'o': | ||||
output.write(outfilter(data)) | ||||
output.flush() | ||||
elif ch == 'e': | ||||
error.write(data) | ||||
error.flush() | ||||
elif ch == 'I': | ||||
writeblock(server, input.read(data)) | ||||
elif ch == 'L': | ||||
writeblock(server, input.readline(data)) | ||||
elif ch == 'r': | ||||
ret, = struct.unpack('>i', data) | ||||
if ret != 0: | ||||
print ' [%d]' % ret | ||||
return ret | ||||
else: | ||||
print "unexpected channel %c: %r" % (ch, data) | ||||
if ch.isupper(): | ||||
return | ||||
Yuya Nishihara
|
r22992 | def check(func, connect=connectpipe): | ||
Yuya Nishihara
|
r22566 | sys.stdout.flush() | ||
Yuya Nishihara
|
r22991 | server = connect() | ||
Yuya Nishihara
|
r22566 | try: | ||
return func(server) | ||||
finally: | ||||
server.stdin.close() | ||||
server.wait() | ||||