text: # vim:fileencoding=utf-8:noet
  from __future__ import unicode_literals, absolute_import
  
  __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
  __docformat__ = 'restructuredtext en'
  
  import os
  import sys
  from time import sleep
  from threading import RLock
  
  from powerline.lib.monotonic import monotonic
  from powerline.lib.inotify import INotify, INotifyError
  
  
  class INotifyWatch(INotify):
  	is_stat_based = False
  
  	def __init__(self, expire_time=10):
  		super(INotifyWatch, self).__init__()
  		self.watches = {}
  		self.modified = {}
  		self.last_query = {}
  		self.lock = RLock()
  		self.expire_time = expire_time * 60
  
  	def expire_watches(self):
  		now = monotonic()
  		for path, last_query in tuple(self.last_query.items()):
  			if last_query - now > self.expire_time:
  				self.unwatch(path)
  
  	def process_event(self, wd, mask, cookie, name):
  		if wd == -1 and (mask & self.Q_OVERFLOW):
  			# We missed some INOTIFY events, so we dont
  			# know the state of any tracked files.
  			for path in tuple(self.modified):
  				if os.path.exists(path):
  					self.modified[path] = True
  				else:
  					self.watches.pop(path, None)
  					self.modified.pop(path, None)
  					self.last_query.pop(path, None)
  			return
  
  		for path, num in tuple(self.watches.items()):
  			if num == wd:
  				if mask & self.IGNORED:
  					self.watches.pop(path, None)
  					self.modified.pop(path, None)
  					self.last_query.pop(path, None)
  				else:
  					self.modified[path] = True
  
  	def unwatch(self, path):
  		''' Remove the watch for path. Raises an OSError if removing the watch
  		fails for some reason. '''
  		path = self.os.path.abspath(path)
  		with self.lock:
  			self.modified.pop(path, None)
  			self.last_query.pop(path, None)
  			wd = self.watches.pop(path, None)
  			if wd is not None:
  				if self._rm_watch(self._inotify_fd, wd) != 0:
  					self.handle_error()
  
  	def watch(self, path):
  		''' Register a watch for the file named path. Raises an OSError if path
  		does not exist. '''
  		import ctypes
  		path = self.os.path.abspath(path)
  		with self.lock:
  			if path not in self.watches:
  				bpath = path if isinstance(path, bytes) else path.encode(self.fenc)
  				wd = self._add_watch(self._inotify_fd, ctypes.c_char_p(bpath),
  						self.MODIFY | self.ATTRIB | self.MOVE_SELF | self.DELETE_SELF)
  				if wd == -1:
  					self.handle_error()
  				self.watches[path] = wd
  				self.modified[path] = False
  
  	def __call__(self, path):
  		''' Return True if path has been modified since the last call. Can
  		raise OSError if the path does not exist. '''
  		path = self.os.path.abspath(path)
  		with self.lock:
  			self.last_query[path] = monotonic()
  			self.expire_watches()
  			if path not in self.watches:
  				# Try to re-add the watch, it will fail if the file does not
  				# exist/you dont have permission
  				self.watch(path)
  				return True
  			self.read(get_name=False)
  			if path not in self.modified:
  				# An ignored event was received which means the path has been
  				# automatically unwatched
  				return True
  			ans = self.modified[path]
  			if ans:
  				self.modified[path] = False
  			return ans
  
  	def close(self):
  		with self.lock:
  			for path in tuple(self.watches):
  				try:
  					self.unwatch(path)
  				except OSError:
  					pass
  			super(INotifyWatch, self).close()
  
  
  class StatWatch(object):
  	is_stat_based = True
  
  	def __init__(self):
  		self.watches = {}
  		self.lock = RLock()
  
  	def watch(self, path):
  		path = os.path.abspath(path)
  		with self.lock:
  			self.watches[path] = os.path.getmtime(path)
  
  	def unwatch(self, path):
  		path = os.path.abspath(path)
  		with self.lock:
  			self.watches.pop(path, None)
  
  	def __call__(self, path):
  		path = os.path.abspath(path)
  		with self.lock:
  			if path not in self.watches:
  				self.watches[path] = os.path.getmtime(path)
  				return True
  			mtime = os.path.getmtime(path)
  			if mtime != self.watches[path]:
  				self.watches[path] = mtime
  				return True
  			return False
  
  	def close(self):
  		with self.lock:
  			self.watches.clear()
  
  
  def create_file_watcher(use_stat=False, expire_time=10):
  	'''
  	Create an object that can watch for changes to specified files. To use:
  
  	watcher = create_file_watcher()
  	watcher(path1) # Will return True if path1 has changed since the last time this was called. Always returns True the first time.
  	watcher.unwatch(path1)
  
  	Uses inotify if available, otherwise tracks mtimes. expire_time is the
  	number of minutes after the last query for a given path for the inotify
  	watch for that path to be automatically removed. This conserves kernel
  	resources.
  	'''
  	if use_stat:
  		return StatWatch()
  	try:
  		return INotifyWatch(expire_time=expire_time)
  	except INotifyError:
  		pass
  	return StatWatch()
  
  if __name__ == '__main__':
  	watcher = create_file_watcher()
  	print ('Using watcher: %s' % watcher.__class__.__name__)
  	print ('Watching %s, press Ctrl-C to quit' % sys.argv[-1])
  	watcher.watch(sys.argv[-1])
  	try:
  		while True:
  			if watcher(sys.argv[-1]):
  				print ('%s has changed' % sys.argv[-1])
  			sleep(1)
  	except KeyboardInterrupt:
  		pass
  	watcher.close()
  