commandserver: pass around option to hook repo instance creation
authorYuya Nishihara <>
Wed, 31 Oct 2018 21:57:11 +0900
changeset 53641 e7110f44ee2d0e14ce909ace35b39882dcc29f97
parent 53639 348352658e4b0b0206e04c9bccd27a036edd5584
child 53642 dc9901558e3c6a78bad3f6594b3888f95104c443
push id1079
push dateMon, 10 Dec 2018 19:44:59 +0000
commandserver: pass around option to hook repo instance creation This is necessary to wrap a repo instance so the master process will be notified on repo.close().
--- a/mercurial/
+++ b/mercurial/
@@ -308,20 +308,21 @@ class channeledsystem(object):
 _iochannels = [
     #, ui.fp, mode
     ('cin', 'fin', r'rb'),
     ('cout', 'fout', r'wb'),
     ('cerr', 'ferr', r'wb'),
 class chgcmdserver(commandserver.server):
-    def __init__(self, ui, repo, fin, fout, sock, hashstate, baseaddress):
+    def __init__(self, ui, repo, fin, fout, sock, prereposetups,
+                 hashstate, baseaddress):
         super(chgcmdserver, self).__init__(
             _newchgui(ui, channeledsystem(fin, fout, 'S'), self.attachio),
-            repo, fin, fout)
+            repo, fin, fout, prereposetups)
         self.clientsock = sock
         self._ioattached = False
         self._oldios = []  # original (, ui.fp, fd) before "attachio"
         self.hashstate = hashstate
         self.baseaddress = baseaddress
         if hashstate is not None:
             self.capabilities = self.capabilities.copy()
             self.capabilities['validate'] = chgcmdserver.validate
@@ -612,18 +613,18 @@ class chgunixservicehandler(object):
         if time.time() - self._lastactive > self._idletimeout:
             self.ui.log(b'chgserver', b'being idle too long. exiting.\n')
             return True
         return False
     def newconnection(self):
         self._lastactive = time.time()
-    def createcmdserver(self, repo, conn, fin, fout):
-        return chgcmdserver(self.ui, repo, fin, fout, conn,
+    def createcmdserver(self, repo, conn, fin, fout, prereposetups):
+        return chgcmdserver(self.ui, repo, fin, fout, conn, prereposetups,
                             self._hashstate, self._baseaddress)
 def chgunixservice(ui, repo, opts):
     # CHGINTERNALMARK is set by chg client. It is an indication of things are
     # started by chg so other code can do things accordingly, like disabling
     # demandimport or detecting chg client started by chg client. When executed
     # here, CHGINTERNALMARK is no longer useful and hence dropped to make
     # environ cleaner.
--- a/mercurial/
+++ b/mercurial/
@@ -191,28 +191,29 @@ def _selectmessageencoder(ui):
     raise error.Abort(b'no supported message encodings: %s'
                       % b' '.join(encnames))
 class server(object):
     Listens for commands on fin, runs them and writes the output on a channel
     based stream to fout.
-    def __init__(self, ui, repo, fin, fout):
+    def __init__(self, ui, repo, fin, fout, prereposetups=None):
         self.cwd = encoding.getcwd()
         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
             self.ui = ui
             self.repo = self.repoui = None
+        self._prereposetups = prereposetups
         self.cdebug = channeledoutput(fout, 'd')
         self.cerr = channeledoutput(fout, 'e')
         self.cout = channeledoutput(fout, 'o')
         self.cin = channeledinput(fin, fout, 'I')
         self.cresult = channeledoutput(fout, 'r')
         if self.ui.config(b'cmdserver', b'log') == b'-':
@@ -289,17 +290,18 @@ class server(object):
             # any kind of interaction must use server channels, but chg may
             # replace channels by fully functional tty files. so nontty is
             # enforced only if cin is a channel.
             if not util.safehasattr(self.cin, 'fileno'):
                 ui.setconfig('ui', 'nontty', 'true', 'commandserver')
         req = dispatch.request(args[:], copiedui, self.repo, self.cin,
-                               self.cout, self.cerr, self.cmsg)
+                               self.cout, self.cerr, self.cmsg,
+                               prereposetups=self._prereposetups)
             ret = dispatch.dispatch(req) & 255
             self.cresult.write(struct.pack('>i', int(ret)))
             # restore old cwd
             if '--cwd' in args:
@@ -415,22 +417,22 @@ def _initworkerprocess():
     #    SIGINT (Ctrl+C) and process-exit-generated SIGHUP. our child
     #    processes like ssh will be killed properly, without affecting
     #    unrelated processes.
     os.setpgid(0, 0)
     # change random state otherwise forked request handlers would have a
     # same state inherited from parent.
-def _serverequest(ui, repo, conn, createcmdserver):
+def _serverequest(ui, repo, conn, createcmdserver, prereposetups):
     fin = conn.makefile(r'rb')
     fout = conn.makefile(r'wb')
     sv = None
-        sv = createcmdserver(repo, conn, fin, fout)
+        sv = createcmdserver(repo, conn, fin, fout, prereposetups)
         # handle exceptions that may be raised by command server. most of
         # known exceptions are caught by dispatch.
         except error.Abort as inst:
             ui.error(_('abort: %s\n') % inst)
         except IOError as inst:
             if inst.errno != errno.EPIPE:
@@ -479,20 +481,20 @@ class unixservicehandler(object):
     def shouldexit(self):
         """True if server should shut down; checked per pollinterval"""
         return False
     def newconnection(self):
         """Called when main process notices new connection"""
-    def createcmdserver(self, repo, conn, fin, fout):
+    def createcmdserver(self, repo, conn, fin, fout, prereposetups):
         """Create new command server instance; called in the process that
         serves for the current connection"""
-        return server(self.ui, repo, fin, fout)
+        return server(self.ui, repo, fin, fout, prereposetups)
 class unixforkingservice(object):
     Listens on unix domain socket and forks server per connection
     def __init__(self, ui, repo, opts, handler=None):
         self.ui = ui
@@ -614,11 +616,12 @@ class unixforkingservice(object):
             self.ui.log(b'cmdserver', b'worker process exited (pid=%d)\n', pid)
     def _runworker(self, conn):
         signal.signal(signal.SIGCHLD, self._oldsigchldhandler)
         h = self._servicehandler
-            _serverequest(self.ui, self.repo, conn, h.createcmdserver)
+            _serverequest(self.ui, self.repo, conn, h.createcmdserver,
+                          prereposetups=None)  # TODO: pass in hook functions
             gc.collect()  # trigger __del__ since worker process uses os._exit
--- a/tests/test-commandserver.t
+++ b/tests/test-commandserver.t
@@ -843,20 +843,20 @@ unix domain socket:
   $ rm .hg/server.log
  if server crashed before hello, traceback will be sent to 'e' channel as
  last ditch:
   $ cat <<'EOF' > ../
   > from mercurial import commandserver, extensions
-  > def _serverequest(orig, ui, repo, conn, createcmdserver):
+  > def _serverequest(orig, ui, repo, conn, createcmdserver, prereposetups):
   >     def createcmdserver(*args, **kwargs):
   >         raise Exception('crash')
-  >     return orig(ui, repo, conn, createcmdserver)
+  >     return orig(ui, repo, conn, createcmdserver, prereposetups)
   > def extsetup(ui):
   >     extensions.wrapfunction(commandserver, b'_serverequest', _serverequest)
   > EOF
   $ cat <<EOF >> .hg/hgrc
   > [extensions]
   > earlycrasher = ../
   > EOF
   >>> from hgclient import bprint, check, readchannel, unixserver