##// END OF EJS Templates
test-bundle2: also test the argument of the changegroup hook...
test-bundle2: also test the argument of the changegroup hook We also track execution of the changegroup hook. The important information here is to make sure the information that the transaction was processing a bundle2 is passed to hook. This will let most hooks disable themselves while waiting for the hook concluding bundle2 processing (the one we discovered to be not called for pull in the previous changesets).

File last commit:

r22563:8cc5e673 default
r22939:e4827754 default
Show More
commandserver.py
250 lines | 7.0 KiB | text/x-python | PythonLexer
Idan Kamara
serve: add --cmdserver option to communicate with hg over a pipe
r14647 # commandserver.py - communicate with Mercurial's API over a pipe
#
# Copyright Matt Mackall <mpm@selenic.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
from i18n import _
import struct
Idan Kamara
cmdserver: restore old working dir after dispatch when we have --cwd
r14864 import sys, os
Idan Kamara
serve: add --cmdserver option to communicate with hg over a pipe
r14647 import dispatch, encoding, util
logfile = None
def log(*args):
if not logfile:
return
for a in args:
logfile.write(str(a))
logfile.flush()
class channeledoutput(object):
"""
Yuya Nishihara
cmdserver: correct doc of channeledoutput...
r22561 Write data to out in the following format:
Idan Kamara
serve: add --cmdserver option to communicate with hg over a pipe
r14647
data length (unsigned int),
data
"""
Yuya Nishihara
cmdserver: drop useless in_ attribute from channeledoutput...
r22563 def __init__(self, out, channel):
Idan Kamara
serve: add --cmdserver option to communicate with hg over a pipe
r14647 self.out = out
self.channel = channel
def write(self, data):
if not data:
return
self.out.write(struct.pack('>cI', self.channel, len(data)))
self.out.write(data)
self.out.flush()
def __getattr__(self, attr):
if attr in ('isatty', 'fileno'):
Augie Fackler
commandserver: clean up use of two-argument raise...
r18174 raise AttributeError(attr)
Yuya Nishihara
cmdserver: drop useless in_ attribute from channeledoutput...
r22563 return getattr(self.out, attr)
Idan Kamara
serve: add --cmdserver option to communicate with hg over a pipe
r14647
class channeledinput(object):
"""
Read data from in_.
Requests for input are written to out in the following format:
channel identifier - 'I' for plain input, 'L' line based (1 byte)
how many bytes to send at most (unsigned int),
The client replies with:
data length (unsigned int), 0 meaning EOF
data
"""
maxchunksize = 4 * 1024
def __init__(self, in_, out, channel):
self.in_ = in_
self.out = out
self.channel = channel
def read(self, size=-1):
if size < 0:
# if we need to consume all the clients input, ask for 4k chunks
# so the pipe doesn't fill up risking a deadlock
size = self.maxchunksize
s = self._read(size, self.channel)
buf = s
while s:
Idan Kamara
cmdserver: fix read-loop string concatenation
r14728 s = self._read(size, self.channel)
Idan Kamara
serve: add --cmdserver option to communicate with hg over a pipe
r14647 buf += s
return buf
else:
return self._read(size, self.channel)
def _read(self, size, channel):
if not size:
return ''
assert size > 0
# tell the client we need at most size bytes
self.out.write(struct.pack('>cI', channel, size))
self.out.flush()
length = self.in_.read(4)
length = struct.unpack('>I', length)[0]
if not length:
return ''
else:
return self.in_.read(length)
def readline(self, size=-1):
if size < 0:
size = self.maxchunksize
s = self._read(size, 'L')
buf = s
# keep asking for more until there's either no more or
# we got a full line
while s and s[-1] != '\n':
Idan Kamara
cmdserver: fix read-loop string concatenation
r14728 s = self._read(size, 'L')
Idan Kamara
serve: add --cmdserver option to communicate with hg over a pipe
r14647 buf += s
return buf
else:
return self._read(size, 'L')
def __iter__(self):
return self
def next(self):
l = self.readline()
if not l:
raise StopIteration
return l
def __getattr__(self, attr):
if attr in ('isatty', 'fileno'):
Augie Fackler
commandserver: clean up use of two-argument raise...
r18174 raise AttributeError(attr)
Idan Kamara
serve: add --cmdserver option to communicate with hg over a pipe
r14647 return getattr(self.in_, attr)
class server(object):
"""
Listens for commands on stdin, runs them and writes the output on a channel
based stream to stdout.
"""
def __init__(self, ui, repo, mode):
Idan Kamara
cmdserver: restore old working dir after dispatch when we have --cwd
r14864 self.cwd = os.getcwd()
Idan Kamara
serve: add --cmdserver option to communicate with hg over a pipe
r14647
logpath = ui.config("cmdserver", "log", None)
if logpath:
global logfile
if logpath == '-':
Mads Kiilerich
fix wording and not-completely-trivial spelling errors and bad docstrings
r17425 # write log on a special 'd' (debug) channel
Yuya Nishihara
cmdserver: drop useless in_ attribute from channeledoutput...
r22563 logfile = channeledoutput(sys.stdout, 'd')
Idan Kamara
serve: add --cmdserver option to communicate with hg over a pipe
r14647 else:
logfile = open(logpath, 'a')
Yuya Nishihara
cmdserver: allow to start server without repository...
r20650 if repo:
# the ui here is really the repo ui so take its baseui so we don't
# end up with its local configuration
self.ui = repo.baseui
self.repo = repo
self.repoui = repo.ui
else:
self.ui = ui
self.repo = self.repoui = None
Idan Kamara
serve: add --cmdserver option to communicate with hg over a pipe
r14647
if mode == 'pipe':
Yuya Nishihara
cmdserver: drop useless in_ attribute from channeledoutput...
r22563 self.cerr = channeledoutput(sys.stdout, 'e')
self.cout = channeledoutput(sys.stdout, 'o')
Idan Kamara
serve: add --cmdserver option to communicate with hg over a pipe
r14647 self.cin = channeledinput(sys.stdin, sys.stdout, 'I')
Yuya Nishihara
cmdserver: drop useless in_ attribute from channeledoutput...
r22563 self.cresult = channeledoutput(sys.stdout, 'r')
Idan Kamara
serve: add --cmdserver option to communicate with hg over a pipe
r14647
self.client = sys.stdin
else:
raise util.Abort(_('unknown mode %s') % mode)
def _read(self, size):
Idan Kamara
cmdserver: don't raise EOFError when trying to read 0 bytes from the client
r14706 if not size:
return ''
Idan Kamara
serve: add --cmdserver option to communicate with hg over a pipe
r14647 data = self.client.read(size)
# is the other end closed?
if not data:
Brodie Rao
cleanup: "raise SomeException()" -> "raise SomeException"
r16687 raise EOFError
Idan Kamara
serve: add --cmdserver option to communicate with hg over a pipe
r14647
return data
def runcommand(self):
""" reads a list of \0 terminated arguments, executes
and writes the return code to the result channel """
length = struct.unpack('>I', self._read(4))[0]
Idan Kamara
cmdserver, runcommand: properly handle the client sending no arguments...
r14707 if not length:
args = []
else:
args = self._read(length).split('\0')
Idan Kamara
serve: add --cmdserver option to communicate with hg over a pipe
r14647
Idan Kamara
cmdserver: copy repo.ui before running commands
r14750 # copy the uis so changes (e.g. --config or --verbose) don't
# persist between requests
Idan Kamara
cmdserver: assign repo.baseui before running commands...
r14751 copiedui = self.ui.copy()
Yuya Nishihara
cmdserver: forcibly use L channel to read password input (issue3161)...
r21195 uis = [copiedui]
Yuya Nishihara
cmdserver: allow to start server without repository...
r20650 if self.repo:
self.repo.baseui = copiedui
# clone ui without using ui.copy because this is protected
repoui = self.repoui.__class__(self.repoui)
repoui.copy = copiedui.copy # redo copy protection
Yuya Nishihara
cmdserver: forcibly use L channel to read password input (issue3161)...
r21195 uis.append(repoui)
Yuya Nishihara
cmdserver: allow to start server without repository...
r20650 self.repo.ui = self.repo.dirstate._ui = repoui
self.repo.invalidateall()
Idan Kamara
cmdserver: assign repo.baseui before running commands...
r14751
Yuya Nishihara
cmdserver: forcibly use L channel to read password input (issue3161)...
r21195 for ui in uis:
# any kind of interaction must use server channels
ui.setconfig('ui', 'nontty', 'true', 'commandserver')
Idan Kamara
cmdserver: restore old working dir after dispatch when we have --cwd
r14864 req = dispatch.request(args[:], copiedui, self.repo, self.cin,
Idan Kamara
serve: add --cmdserver option to communicate with hg over a pipe
r14647 self.cout, self.cerr)
Yuya Nishihara
cmdserver: mask return code of runcommand in the same way as dispatch.run...
r20631 ret = (dispatch.dispatch(req) or 0) & 255 # might return None
Idan Kamara
serve: add --cmdserver option to communicate with hg over a pipe
r14647
Idan Kamara
cmdserver: restore old working dir after dispatch when we have --cwd
r14864 # restore old cwd
if '--cwd' in args:
os.chdir(self.cwd)
Idan Kamara
serve: add --cmdserver option to communicate with hg over a pipe
r14647 self.cresult.write(struct.pack('>i', int(ret)))
def getencoding(self):
""" writes the current encoding to the result channel """
self.cresult.write(encoding.encoding)
def serveone(self):
cmd = self.client.readline()[:-1]
if cmd:
handler = self.capabilities.get(cmd)
if handler:
handler(self)
else:
# clients are expected to check what commands are supported by
# looking at the servers capabilities
raise util.Abort(_('unknown command %s') % cmd)
return cmd != ''
capabilities = {'runcommand' : runcommand,
'getencoding' : getencoding}
def serve(self):
Mads Kiilerich
commandserver: report capabilities sorted
r18359 hellomsg = 'capabilities: ' + ' '.join(sorted(self.capabilities))
Idan Kamara
cmdserver: write the hello message as one chunk on the 'o' channel...
r14719 hellomsg += '\n'
hellomsg += 'encoding: ' + encoding.encoding
# write the hello msg in -one- chunk
self.cout.write(hellomsg)
Idan Kamara
serve: add --cmdserver option to communicate with hg over a pipe
r14647
try:
while self.serveone():
pass
except EOFError:
# we'll get here if the client disconnected while we were reading
# its request
return 1
return 0