1 # Copyright (c) 2008 David Aguilar
2 """Provides an inotify plugin for Linux and other systems with pyinotify"""
9 from pyinotify
import ProcessEvent
10 from pyinotify
import WatchManager
11 from pyinotify
import Notifier
12 from pyinotify
import EventsCodes
19 from PyQt4
import QtCore
22 from cola
import signals
23 from cola
import utils
25 INOTIFY_EVENT
= QtCore
.QEvent
.User
+ 0
32 if not utils
.is_linux():
34 msg
= ('inotify: disabled\n'
35 'Note: install python-pyinotify to enable inotify.\n')
38 msg
+= ('On Debian systems '
39 'try: sudo aptitude install python-pyinotify')
40 cola
.notifier().broadcast(signals
.log_cmd
, 0, msg
)
43 # Start the notification thread
44 _thread
= GitNotifier()
46 msg
= 'inotify support: enabled'
47 cola
.notifier().broadcast(signals
.log_cmd
, 0, msg
)
48 cola
.notifier().broadcast(signals
.inotify
, True)
54 _thread
.set_abort(True)
60 """Return True if pyinotify is available."""
61 return AVAILABLE
and _thread
and _thread
.isRunning()
64 class EventReceiver(QtCore
.QObject
):
66 """Overrides event() to handle custom inotify events."""
69 if msg
.type() == INOTIFY_EVENT
:
70 cola
.notifier().broadcast(signals
.rescan
)
76 class FileSysEvent(ProcessEvent
):
77 """Generated by GitNotifier in response to inotify events"""
79 def __init__(self
, parent
):
80 """Keep a reference to the QThread parent and maintain event state"""
81 ProcessEvent
.__init
__(self
)
84 ## Timer used to prevents notification floods
85 self
._last
_event
_time
= time
.time()
87 def process_default(self
, event
):
88 """Notifies the Qt parent when actions occur"""
89 ## Limit the notification frequency to 1 per second
90 if time
.time() - self
._last
_event
_time
> 1.0:
92 self
._last
_event
_time
= time
.time()
95 class GitNotifier(QtCore
.QThread
):
96 """Polls inotify for changes and generates FileSysEvents"""
98 def __init__(self
, timeout
=250):
99 """Set up the pyinotify thread"""
100 QtCore
.QThread
.__init
__(self
)
101 self
.setTerminationEnabled(True)
103 ## QApplication receiver of Qt events
104 self
._receiver
= EventReceiver()
105 ## Git command object
106 self
._git
= cola
.model().git
108 self
._timeout
= timeout
110 self
._path
= self
._git
.worktree()
111 ## Signals thread termination
113 ## Directories to watching
114 self
._dirs
_seen
= set()
115 ## The inotify watch manager instantiated in run()
118 self
._mask
= (EventsCodes
.ALL_FLAGS
['IN_CREATE'] |
119 EventsCodes
.ALL_FLAGS
['IN_DELETE'] |
120 EventsCodes
.ALL_FLAGS
['IN_MODIFY'] |
121 EventsCodes
.ALL_FLAGS
['IN_MOVED_TO'])
123 def set_abort(self
, abort
):
124 """Tells the GitNotifier to abort"""
128 """Post a Qt event in response to inotify updates"""
130 event_type
= QtCore
.QEvent
.Type(INOTIFY_EVENT
)
131 event
= QtCore
.QEvent(event_type
)
132 QtCore
.QCoreApplication
.postEvent(self
._receiver
, event
)
134 def _watch_directory(self
, directory
):
135 """Set up a directory for monitoring by inotify"""
138 directory
= os
.path
.realpath(directory
)
139 if directory
not in self
._dirs
_seen
:
140 self
._wmgr
.add_watch(directory
, self
._mask
)
141 self
._dirs
_seen
.add(directory
)
143 def _is_pyinotify_08x(self
):
144 """Is this pyinotify 0.8.x?
146 The pyinotify API changed between 0.7.x and 0.8.x.
147 This allows us to maintain backwards compatibility.
149 if hasattr(pyinotify
, '__version__'):
150 if pyinotify
.__version
__[:3] == '0.8':
155 """Create the inotify WatchManager and generate FileSysEvents"""
156 # Only capture events that git cares about
157 self
._wmgr
= WatchManager()
158 if self
._is
_pyinotify
_08x
():
159 notifier
= Notifier(self
._wmgr
, FileSysEvent(self
),
160 timeout
=self
._timeout
)
162 notifier
= Notifier(self
._wmgr
, FileSysEvent(self
))
165 # self._abort signals app termination. The timeout is a tradeoff
166 # between fast notification response and waiting too long to exit.
167 while not self
._abort
:
169 self
._watch
_directory
(self
._path
)
170 # Register files/directories known to git
171 for filename
in self
._git
.ls_files().splitlines():
172 directory
= os
.path
.dirname(filename
)
173 self
._watch
_directory
(directory
)
175 notifier
.process_events()
176 if self
._is
_pyinotify
_08x
():
177 check
= notifier
.check_events()
179 check
= notifier
.check_events(timeout
=self
._timeout
)
181 notifier
.read_events()