inotify: modular architecture for inotify clients
Put the socket init, query generation and response analysis in
a more generic client class.
--- a/hgext/inotify/__init__.py Sat May 23 13:43:11 2009 +0200
+++ b/hgext/inotify/__init__.py Tue Apr 07 18:39:34 2009 +0900
@@ -13,8 +13,9 @@
from mercurial.i18n import _
from mercurial import cmdutil, util
-import client, errno, os, server, socket
+import errno, os, server, socket
from weakref import proxy
+from client import client
def serve(ui, repo, **opts):
'''start an inotify server for this repository'''
@@ -54,10 +55,11 @@
files = match.files()
if '.' in files:
files = []
+ cli = client(ui, repo)
try:
if not ignored and not self.inotifyserver:
- result = client.query(ui, repo, files, match, False,
- clean, unknown)
+ result = cli.statusquery(files, match, False,
+ clean, unknown)
if result and ui.config('inotify', 'debug'):
r2 = super(inotifydirstate, self).status(
match, False, clean, unknown)
@@ -94,8 +96,8 @@
else:
# server is started, send query again
try:
- return client.query(ui, repo, files, match,
- ignored, clean, unknown)
+ 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])
--- a/hgext/inotify/client.py Sat May 23 13:43:11 2009 +0200
+++ b/hgext/inotify/client.py Tue Apr 07 18:39:34 2009 +0900
@@ -2,6 +2,7 @@
#
# Copyright 2006, 2007, 2008 Bryan O'Sullivan <bos@serpentine.com>
# Copyright 2007, 2008 Brendan Cully <brendan@kublai.com>
+# Copyright 2009 Nicolas Dumazet <nicdumz@gmail.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2, incorporated herein by reference.
@@ -10,56 +11,84 @@
import common
import os, socket, struct
-def query(ui, repo, names, match, ignored, clean, unknown=True):
- sock = socket.socket(socket.AF_UNIX)
- sockpath = repo.join('inotify.sock')
- try:
- sock.connect(sockpath)
- except socket.error, err:
- if err[0] == "AF_UNIX path too long":
- sockpath = os.readlink(sockpath)
- sock.connect(sockpath)
- else:
- raise
+class client(object):
+ def __init__(self, ui, repo):
+ self.ui = ui
+ self.repo = repo
+ self.sock = socket.socket(socket.AF_UNIX)
+
+ def _connect(self):
+ sockpath = self.repo.join('inotify.sock')
+ try:
+ self.sock.connect(sockpath)
+ except socket.error, err:
+ if err[0] == "AF_UNIX path too long":
+ sockpath = os.readlink(sockpath)
+ self.sock.connect(sockpath)
+ else:
+ raise
- def genquery():
- for n in names:
- yield n
- states = 'almrx!'
- if ignored:
- raise ValueError('this is insanity')
- if clean: states += 'c'
- if unknown: states += '?'
- yield states
+ def _send(self, data):
+ """Sends protocol version number, and the data"""
+ self.sock.sendall(chr(common.version) + data)
+
+ self.sock.shutdown(socket.SHUT_WR)
- req = '\0'.join(genquery())
+ def _receive(self):
+ """
+ Read data, check version number, extract headers,
+ and returns a tuple (data descriptor, header)
+ Returns (None, None) 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
- sock.sendall(chr(common.version))
- sock.sendall(req)
- sock.shutdown(socket.SHUT_WR)
+ # only one type of request is supported for now
+ type = 'STAT'
+ hdrfmt = common.resphdrfmts[type]
+ hdrsize = common.resphdrsizes[type]
+ try:
+ resphdr = struct.unpack(hdrfmt, cs.read(hdrsize))
+ except struct.error:
+ return None, None
- cs = common.recvcs(sock)
- version = ord(cs.read(1))
+ return cs, resphdr
+
+ def query(self, req):
+ self._connect()
- if version != common.version:
- ui.warn(_('(inotify: received response from incompatible server '
- 'version %d)\n') % version)
- return None
+ self._send(req)
+
+ return self._receive()
+
+ def statusquery(self, names, match, ignored, clean, unknown=True):
- # only one type of request is supported for now
- type = 'STAT'
- hdrfmt = common.resphdrfmts[type]
- hdrsize = common.resphdrsizes[type]
- try:
- resphdr = struct.unpack(hdrfmt, cs.read(hdrsize))
- except struct.error:
- return None
+ def genquery():
+ for n in names:
+ yield n
+ states = 'almrx!'
+ if ignored:
+ raise ValueError('this is insanity')
+ if clean: states += 'c'
+ if unknown: states += '?'
+ yield states
+
+ req = '\0'.join(genquery())
- def readnames(nbytes):
- if nbytes:
- names = cs.read(nbytes)
- if names:
- return filter(match, names.split('\0'))
- return []
+ cs, resphdr = self.query(req)
+
+ if not cs:
+ return None
- return map(readnames, resphdr)
+ def readnames(nbytes):
+ if nbytes:
+ names = cs.read(nbytes)
+ if names:
+ return filter(match, names.split('\0'))
+ return []
+
+ return map(readnames, resphdr)