##// END OF EJS Templates
win32mbcs: allow win32mbcs extension to be enabled on cygwin platform...
win32mbcs: allow win32mbcs extension to be enabled on cygwin platform this patch allows win32mbcs extension to be enabled on cygwin platform for problematic character encodings. on recent cygwin platform, even though "os.path.supports_unicode_filenames" is False, "os.listdir()" and other path manipulation functions can return the result correctly decoded in unicode for invocations with unicode arguments, if locale is configured properly. existing code to check "os.path.supports_unicode_filenames" is kept to prevent win32mbcs from being enabled on unexpected platform.

File last commit:

r14939:b4c06b97 default
r15724:9e6a13c2 default
Show More
commandserver.py
237 lines | 6.5 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):
"""
Write data from in_ to out in the following format:
data length (unsigned int),
data
"""
def __init__(self, in_, out, channel):
self.in_ = in_
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'):
raise AttributeError, attr
return getattr(self.in_, attr)
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'):
raise AttributeError, attr
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 == '-':
# write log on a special 'd'ebug channel
logfile = channeledoutput(sys.stdout, sys.stdout, 'd')
else:
logfile = open(logpath, 'a')
Idan Kamara
cmdserver: take repo.baseui as our ui...
r14882 # 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
Idan Kamara
serve: add --cmdserver option to communicate with hg over a pipe
r14647 self.repo = repo
Idan Kamara
cmdserver: copy repo.ui before running commands
r14750 self.repoui = repo.ui
Idan Kamara
serve: add --cmdserver option to communicate with hg over a pipe
r14647
if mode == 'pipe':
self.cerr = channeledoutput(sys.stderr, sys.stdout, 'e')
self.cout = channeledoutput(sys.stdout, sys.stdout, 'o')
self.cin = channeledinput(sys.stdin, sys.stdout, 'I')
self.cresult = channeledoutput(sys.stdout, sys.stdout, 'r')
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:
raise EOFError()
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()
self.repo.baseui = copiedui
Idan Kamara
cmdserver: copy repo.ui before running commands
r14750 self.repo.ui = self.repo.dirstate._ui = self.repoui.copy()
Idan Kamara
cmdserver: repo.invalidate() on every runcommand...
r14939 self.repo.invalidate()
Idan Kamara
cmdserver: assign repo.baseui before running commands...
r14751
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)
ret = dispatch.dispatch(req) or 0 # might return None
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):
Idan Kamara
cmdserver: write the hello message as one chunk on the 'o' channel...
r14719 hellomsg = 'capabilities: ' + ' '.join(self.capabilities.keys())
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