mercurial/commandserver.py
changeset 40589 054d0fcba2c4
parent 40588 9683dfb6f13a
child 40590 83dd8c63a0c6
equal deleted inserted replaced
40588:9683dfb6f13a 40589:054d0fcba2c4
    24 
    24 
    25 from .i18n import _
    25 from .i18n import _
    26 from . import (
    26 from . import (
    27     encoding,
    27     encoding,
    28     error,
    28     error,
       
    29     pycompat,
    29     util,
    30     util,
    30 )
    31 )
    31 from .utils import (
    32 from .utils import (
       
    33     cborutil,
    32     procutil,
    34     procutil,
    33 )
    35 )
    34 
    36 
    35 logfile = None
    37 logfile = None
    36 
    38 
    67 
    69 
    68     def __getattr__(self, attr):
    70     def __getattr__(self, attr):
    69         if attr in (r'isatty', r'fileno', r'tell', r'seek'):
    71         if attr in (r'isatty', r'fileno', r'tell', r'seek'):
    70             raise AttributeError(attr)
    72             raise AttributeError(attr)
    71         return getattr(self.out, attr)
    73         return getattr(self.out, attr)
       
    74 
       
    75 class channeledmessage(object):
       
    76     """
       
    77     Write encoded message and metadata to out in the following format:
       
    78 
       
    79     data length (unsigned int),
       
    80     encoded message and metadata, as a flat key-value dict.
       
    81     """
       
    82 
       
    83     # teach ui that write() can take **opts
       
    84     structured = True
       
    85 
       
    86     def __init__(self, out, channel, encodename, encodefn):
       
    87         self._cout = channeledoutput(out, channel)
       
    88         self.encoding = encodename
       
    89         self._encodefn = encodefn
       
    90 
       
    91     def write(self, data, **opts):
       
    92         opts = pycompat.byteskwargs(opts)
       
    93         opts[b'data'] = data
       
    94         self._cout.write(self._encodefn(opts))
       
    95 
       
    96     def __getattr__(self, attr):
       
    97         return getattr(self._cout, attr)
    72 
    98 
    73 class channeledinput(object):
    99 class channeledinput(object):
    74     """
   100     """
    75     Read data from in_.
   101     Read data from in_.
    76 
   102 
   154     def __getattr__(self, attr):
   180     def __getattr__(self, attr):
   155         if attr in (r'isatty', r'fileno', r'tell', r'seek'):
   181         if attr in (r'isatty', r'fileno', r'tell', r'seek'):
   156             raise AttributeError(attr)
   182             raise AttributeError(attr)
   157         return getattr(self.in_, attr)
   183         return getattr(self.in_, attr)
   158 
   184 
       
   185 _messageencoders = {
       
   186     b'cbor': lambda v: b''.join(cborutil.streamencode(v)),
       
   187 }
       
   188 
       
   189 def _selectmessageencoder(ui):
       
   190     # experimental config: cmdserver.message-encodings
       
   191     encnames = ui.configlist(b'cmdserver', b'message-encodings')
       
   192     for n in encnames:
       
   193         f = _messageencoders.get(n)
       
   194         if f:
       
   195             return n, f
       
   196     raise error.Abort(b'no supported message encodings: %s'
       
   197                       % b' '.join(encnames))
       
   198 
   159 class server(object):
   199 class server(object):
   160     """
   200     """
   161     Listens for commands on fin, runs them and writes the output on a channel
   201     Listens for commands on fin, runs them and writes the output on a channel
   162     based stream to fout.
   202     based stream to fout.
   163     """
   203     """
   186 
   226 
   187         self.cerr = channeledoutput(fout, 'e')
   227         self.cerr = channeledoutput(fout, 'e')
   188         self.cout = channeledoutput(fout, 'o')
   228         self.cout = channeledoutput(fout, 'o')
   189         self.cin = channeledinput(fin, fout, 'I')
   229         self.cin = channeledinput(fin, fout, 'I')
   190         self.cresult = channeledoutput(fout, 'r')
   230         self.cresult = channeledoutput(fout, 'r')
       
   231 
       
   232         # TODO: add this to help/config.txt when stabilized
       
   233         # ``channel``
       
   234         #   Use separate channel for structured output. (Command-server only)
       
   235         self.cmsg = None
       
   236         if ui.config(b'ui', b'message-output') == b'channel':
       
   237             encname, encfn = _selectmessageencoder(ui)
       
   238             self.cmsg = channeledmessage(fout, b'm', encname, encfn)
   191 
   239 
   192         self.client = fin
   240         self.client = fin
   193 
   241 
   194     def cleanup(self):
   242     def cleanup(self):
   195         """release and restore resources taken during server session"""
   243         """release and restore resources taken during server session"""
   252             # enforced only if cin is a channel.
   300             # enforced only if cin is a channel.
   253             if not util.safehasattr(self.cin, 'fileno'):
   301             if not util.safehasattr(self.cin, 'fileno'):
   254                 ui.setconfig('ui', 'nontty', 'true', 'commandserver')
   302                 ui.setconfig('ui', 'nontty', 'true', 'commandserver')
   255 
   303 
   256         req = dispatch.request(args[:], copiedui, self.repo, self.cin,
   304         req = dispatch.request(args[:], copiedui, self.repo, self.cin,
   257                                self.cout, self.cerr)
   305                                self.cout, self.cerr, self.cmsg)
   258 
   306 
   259         try:
   307         try:
   260             ret = dispatch.dispatch(req) & 255
   308             ret = dispatch.dispatch(req) & 255
   261             self.cresult.write(struct.pack('>i', int(ret)))
   309             self.cresult.write(struct.pack('>i', int(ret)))
   262         finally:
   310         finally:
   287     def serve(self):
   335     def serve(self):
   288         hellomsg = 'capabilities: ' + ' '.join(sorted(self.capabilities))
   336         hellomsg = 'capabilities: ' + ' '.join(sorted(self.capabilities))
   289         hellomsg += '\n'
   337         hellomsg += '\n'
   290         hellomsg += 'encoding: ' + encoding.encoding
   338         hellomsg += 'encoding: ' + encoding.encoding
   291         hellomsg += '\n'
   339         hellomsg += '\n'
       
   340         if self.cmsg:
       
   341             hellomsg += 'message-encoding: %s\n' % self.cmsg.encoding
   292         hellomsg += 'pid: %d' % procutil.getpid()
   342         hellomsg += 'pid: %d' % procutil.getpid()
   293         if util.safehasattr(os, 'getpgid'):
   343         if util.safehasattr(os, 'getpgid'):
   294             hellomsg += '\n'
   344             hellomsg += '\n'
   295             hellomsg += 'pgid: %d' % os.getpgid(0)
   345             hellomsg += 'pgid: %d' % os.getpgid(0)
   296 
   346