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
--- 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)