Mercurial > public > mercurial-scm > hg-stable
diff hgext/fsmonitor/watchmanclient.py @ 28433:3b67f27bb908
fsmonitor: new experimental extension
Extension to plug into a Watchman daemon, speeding up hg status calls by
relying on OS events to tell us what files have changed.
Originally developed at https://bitbucket.org/facebook/hgwatchman
author | Martijn Pieters <mjpieters@fb.com> |
---|---|
date | Thu, 03 Mar 2016 14:29:19 +0000 |
parents | |
children | b0a0f7b9ed90 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/fsmonitor/watchmanclient.py Thu Mar 03 14:29:19 2016 +0000 @@ -0,0 +1,109 @@ +# watchmanclient.py - Watchman client for the fsmonitor extension +# +# Copyright 2013-2016 Facebook, Inc. +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +from __future__ import absolute_import + +import getpass + +from mercurial import util + +from . import pywatchman + +class Unavailable(Exception): + def __init__(self, msg, warn=True, invalidate=False): + self.msg = msg + self.warn = warn + if self.msg == 'timed out waiting for response': + self.warn = False + self.invalidate = invalidate + + def __str__(self): + if self.warn: + return 'warning: Watchman unavailable: %s' % self.msg + else: + return 'Watchman unavailable: %s' % self.msg + +class WatchmanNoRoot(Unavailable): + def __init__(self, root, msg): + self.root = root + super(WatchmanNoRoot, self).__init__(msg) + +class client(object): + def __init__(self, repo, timeout=1.0): + err = None + if not self._user: + err = "couldn't get user" + warn = True + if self._user in repo.ui.configlist('fsmonitor', 'blacklistusers'): + err = 'user %s in blacklist' % self._user + warn = False + + if err: + raise Unavailable(err, warn) + + self._timeout = timeout + self._watchmanclient = None + self._root = repo.root + self._ui = repo.ui + self._firsttime = True + + def settimeout(self, timeout): + self._timeout = timeout + if self._watchmanclient is not None: + self._watchmanclient.setTimeout(timeout) + + def getcurrentclock(self): + result = self.command('clock') + if not util.safehasattr(result, 'clock'): + raise Unavailable('clock result is missing clock value', + invalidate=True) + return result.clock + + def clearconnection(self): + self._watchmanclient = None + + def available(self): + return self._watchmanclient is not None or self._firsttime + + @util.propertycache + def _user(self): + try: + return getpass.getuser() + except KeyError: + # couldn't figure out our user + return None + + def _command(self, *args): + watchmanargs = (args[0], self._root) + args[1:] + try: + if self._watchmanclient is None: + self._firsttime = False + self._watchmanclient = pywatchman.client( + timeout=self._timeout, + useImmutableBser=True) + return self._watchmanclient.query(*watchmanargs) + except pywatchman.CommandError as ex: + if ex.msg.startswith('unable to resolve root'): + raise WatchmanNoRoot(self._root, ex.msg) + raise Unavailable(ex.msg) + except pywatchman.WatchmanError as ex: + raise Unavailable(str(ex)) + + def command(self, *args): + try: + try: + return self._command(*args) + except WatchmanNoRoot: + # this 'watch' command can also raise a WatchmanNoRoot if + # watchman refuses to accept this root + self._command('watch') + return self._command(*args) + except Unavailable: + # this is in an outer scope to catch Unavailable form any of the + # above _command calls + self._watchmanclient = None + raise