# HG changeset patch # User Nicolas Dumazet # Date 2009-04-07 09:39:34 # Node ID 7089d9727867a20d8428024d3e265b9422e12264 # Parent cbd3a104637f3ffe6ea0fcc503c6ab427e3f01e1 inotify: modular architecture for inotify clients Put the socket init, query generation and response analysis in a more generic client class. diff --git a/hgext/inotify/__init__.py b/hgext/inotify/__init__.py --- a/hgext/inotify/__init__.py +++ b/hgext/inotify/__init__.py @@ -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 @@ def reposetup(ui, repo): 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 @@ def reposetup(ui, repo): 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]) diff --git a/hgext/inotify/client.py b/hgext/inotify/client.py --- a/hgext/inotify/client.py +++ b/hgext/inotify/client.py @@ -2,6 +2,7 @@ # # Copyright 2006, 2007, 2008 Bryan O'Sullivan # Copyright 2007, 2008 Brendan Cully +# Copyright 2009 Nicolas Dumazet # # 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 @@ from mercurial.i18n import _ 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)