changeset 8552:06561793778e

inotify: Separate query sending logic from Server starting. Use a decorator around the public statusquery method of Client: start_server(query_to_server): try: query_to_server() except QueryFailed: [error recovery, inotify Server (re)starting] query_to_server() This way, introducing a new xxxquery Client method is easy: one has only to code the protocol part of xxxquery, ignoring errors, and decorating it using start_server to handle server recovery and (re)starts
author Nicolas Dumazet <nicdumz.commits@gmail.com>
date Tue, 07 Apr 2009 19:30:01 +0900
parents 7089d9727867
children e387ecd7a6ed
files hgext/inotify/__init__.py hgext/inotify/client.py
diffstat 2 files changed, 68 insertions(+), 55 deletions(-) [+]
line wrap: on
line diff
--- a/hgext/inotify/__init__.py	Tue Apr 07 18:39:34 2009 +0900
+++ b/hgext/inotify/__init__.py	Tue Apr 07 19:30:01 2009 +0900
@@ -13,9 +13,9 @@
 
 from mercurial.i18n import _
 from mercurial import cmdutil, util
-import errno, os, server, socket
+import os, server
 from weakref import proxy
-from client import client
+from client import client, QueryFailed
 
 def serve(ui, repo, **opts):
     '''start an inotify server for this repository'''
@@ -55,12 +55,16 @@
             files = match.files()
             if '.' in files:
                 files = []
-            cli = client(ui, repo)
-            try:
-                if not ignored and not self.inotifyserver:
+            if not ignored and not self.inotifyserver:
+                cli = client(ui, repo)
+                try:
                     result = cli.statusquery(files, match, False,
-                                             clean, unknown)
-                    if result and ui.config('inotify', 'debug'):
+                                            clean, unknown)
+                except QueryFailed, instr:
+                    ui.debug(str(instr))
+                    pass
+                else:
+                    if ui.config('inotify', 'debug'):
                         r2 = super(inotifydirstate, self).status(
                             match, False, clean, unknown)
                         for c,a,b in zip('LMARDUIC', result, r2):
@@ -71,46 +75,7 @@
                                 if f not in a:
                                     ui.warn('*** inotify: %s -%s\n' % (c, f))
                         result = r2
-
-                    if result is not None:
-                        return result
-            except (OSError, socket.error), err:
-                autostart = ui.configbool('inotify', 'autostart', True)
-
-                if err[0] == errno.ECONNREFUSED:
-                    ui.warn(_('(found dead inotify server socket; '
-                                   'removing it)\n'))
-                    os.unlink(repo.join('inotify.sock'))
-                if err[0] in (errno.ECONNREFUSED, errno.ENOENT) and autostart:
-                    ui.debug(_('(starting inotify server)\n'))
-                    try:
-                        try:
-                            server.start(ui, repo)
-                        except server.AlreadyStartedException, inst:
-                            # another process may have started its own
-                            # inotify server while this one was starting.
-                            ui.debug(str(inst))
-                    except Exception, inst:
-                        ui.warn(_('could not start inotify server: '
-                                       '%s\n') % inst)
-                    else:
-                        # server is started, send query again
-                        try:
-                            return cli.statusquery(files, match, ignored,
-                                                   clean, unknown)
-                        except socket.error, err:
-                            ui.warn(_('could not talk to new inotify '
-                                           'server: %s\n') % err[-1])
-                elif err[0] in (errno.ECONNREFUSED, errno.ENOENT):
-                    # silently ignore normal errors if autostart is False
-                    ui.debug(_('(inotify server not running)\n'))
-                else:
-                    ui.warn(_('failed to contact inotify server: %s\n')
-                             % err[-1])
-                ui.traceback()
-                # replace by old status function
-                self.status = super(inotifydirstate, self).status
-
+                    return result
             return super(inotifydirstate, self).status(
                 match, ignored, clean, unknown)
 
--- a/hgext/inotify/client.py	Tue Apr 07 18:39:34 2009 +0900
+++ b/hgext/inotify/client.py	Tue Apr 07 19:30:01 2009 +0900
@@ -8,8 +8,58 @@
 # GNU General Public License version 2, incorporated herein by reference.
 
 from mercurial.i18n import _
-import common
-import os, socket, struct
+import common, server
+import errno, os, socket, struct
+
+class QueryFailed(Exception): pass
+
+def start_server(function):
+    """
+    Decorator.
+    Tries to call function, if it fails, try to (re)start inotify server.
+    Raise QueryFailed if something went wrong
+    """
+    def decorated_function(self, *args):
+        result = None
+        try:
+            return function(self, *args)
+        except (OSError, socket.error), err:
+            autostart = self.ui.configbool('inotify', 'autostart', True)
+
+            if err[0] == errno.ECONNREFUSED:
+                self.ui.warn(_('(found dead inotify server socket; '
+                               'removing it)\n'))
+                os.unlink(self.repo.join('inotify.sock'))
+            if err[0] in (errno.ECONNREFUSED, errno.ENOENT) and autostart:
+                self.ui.debug(_('(starting inotify server)\n'))
+                try:
+                    try:
+                        server.start(self.ui, self.repo)
+                    except server.AlreadyStartedException, inst:
+                        # another process may have started its own
+                        # inotify server while this one was starting.
+                        self.ui.debug(str(inst))
+                except Exception, inst:
+                    self.ui.warn(_('could not start inotify server: '
+                                   '%s\n') % inst)
+                else:
+                    try:
+                        return function(self, *args)
+                    except socket.error, err:
+                        self.ui.warn(_('could not talk to new inotify '
+                                       'server: %s\n') % err[-1])
+            elif err[0] in (errno.ECONNREFUSED, errno.ENOENT):
+                # silently ignore normal errors if autostart is False
+                self.ui.debug(_('(inotify server not running)\n'))
+            else:
+                self.ui.warn(_('failed to contact inotify server: %s\n')
+                         % err[-1])
+
+        self.ui.traceback()
+        raise QueryFailed('inotify query failed')
+
+    return decorated_function
+
 
 class client(object):
     def __init__(self, ui, repo):
@@ -38,14 +88,14 @@
         """
         Read data, check version number, extract headers,
         and returns a tuple (data descriptor, header)
-        Returns (None, None) on error
+        Raises QueryFailed on error
         """
         cs = common.recvcs(self.sock)
         version = ord(cs.read(1))
         if version != common.version:
             self.ui.warn(_('(inotify: received response from incompatible '
                       'server version %d)\n') % version)
-            return None, None
+            raise QueryFailed('incompatible server version')
 
         # only one type of request is supported for now
         type = 'STAT'
@@ -54,7 +104,7 @@
         try:
             resphdr = struct.unpack(hdrfmt, cs.read(hdrsize))
         except struct.error:
-            return None, None
+            raise QueryFailed('unable to retrieve query response headers')
 
         return cs, resphdr
 
@@ -65,6 +115,7 @@
 
         return self._receive()
 
+    @start_server
     def statusquery(self, names, match, ignored, clean, unknown=True):
 
         def genquery():
@@ -81,9 +132,6 @@
 
         cs, resphdr = self.query(req)
 
-        if not cs:
-            return None
-
         def readnames(nbytes):
             if nbytes:
                 names = cs.read(nbytes)