~justin-fathomdb/nova/justinsb-openstack-api-volumes

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/internet/gtk2reactor.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- test-case-name: twisted.internet.test.test_gtk2reactor -*-
 
2
# Copyright (c) 2001-2010 Twisted Matrix Laboratories.
 
3
# See LICENSE for details.
 
4
 
 
5
 
 
6
"""
 
7
This module provides support for Twisted to interact with the glib/gtk2
 
8
mainloop.
 
9
 
 
10
In order to use this support, simply do the following::
 
11
 
 
12
    |  from twisted.internet import gtk2reactor
 
13
    |  gtk2reactor.install()
 
14
 
 
15
Then use twisted.internet APIs as usual.  The other methods here are not
 
16
intended to be called directly.
 
17
 
 
18
When installing the reactor, you can choose whether to use the glib
 
19
event loop or the GTK+ event loop which is based on it but adds GUI
 
20
integration.
 
21
"""
 
22
 
 
23
# System Imports
 
24
import sys
 
25
from zope.interface import implements
 
26
try:
 
27
    if not hasattr(sys, 'frozen'):
 
28
        # Don't want to check this for py2exe
 
29
        import pygtk
 
30
        pygtk.require('2.0')
 
31
except (ImportError, AttributeError):
 
32
    pass # maybe we're using pygtk before this hack existed.
 
33
import gobject
 
34
if hasattr(gobject, "threads_init"):
 
35
    # recent versions of python-gtk expose this. python-gtk=2.4.1
 
36
    # (wrapping glib-2.4.7) does. python-gtk=2.0.0 (wrapping
 
37
    # glib-2.2.3) does not.
 
38
    gobject.threads_init()
 
39
 
 
40
# Twisted Imports
 
41
from twisted.python import log, runtime, failure
 
42
from twisted.python.compat import set
 
43
from twisted.internet.interfaces import IReactorFDSet
 
44
from twisted.internet import main, posixbase, error, selectreactor
 
45
 
 
46
POLL_DISCONNECTED = gobject.IO_HUP | gobject.IO_ERR | gobject.IO_NVAL
 
47
 
 
48
# glib's iochannel sources won't tell us about any events that we haven't
 
49
# asked for, even if those events aren't sensible inputs to the poll()
 
50
# call.
 
51
INFLAGS = gobject.IO_IN | POLL_DISCONNECTED
 
52
OUTFLAGS = gobject.IO_OUT | POLL_DISCONNECTED
 
53
 
 
54
 
 
55
 
 
56
def _our_mainquit():
 
57
    # XXX: gtk.main_quit() (which is used for crash()) raises an exception if
 
58
    # gtk.main_level() == 0; however, all the tests freeze if we use this
 
59
    # function to stop the reactor.  what gives?  (I believe this may have been
 
60
    # a stupid mistake where I forgot to import gtk here... I will remove this
 
61
    # comment if the tests pass)
 
62
    import gtk
 
63
    if gtk.main_level():
 
64
        gtk.main_quit()
 
65
 
 
66
 
 
67
 
 
68
class Gtk2Reactor(posixbase.PosixReactorBase):
 
69
    """
 
70
    GTK+-2 event loop reactor.
 
71
 
 
72
    @ivar _sources: A dictionary mapping L{FileDescriptor} instances to gtk
 
73
        watch handles.
 
74
 
 
75
    @ivar _reads: A set of L{FileDescriptor} instances currently monitored for
 
76
        reading.
 
77
 
 
78
    @ivar _writes: A set of L{FileDescriptor} instances currently monitored for
 
79
        writing.
 
80
 
 
81
    @ivar _simtag: A gtk timeout handle for the next L{simulate} call.
 
82
    """
 
83
    implements(IReactorFDSet)
 
84
 
 
85
    def __init__(self, useGtk=True):
 
86
        self._simtag = None
 
87
        self._reads = set()
 
88
        self._writes = set()
 
89
        self._sources = {}
 
90
        posixbase.PosixReactorBase.__init__(self)
 
91
        # pre 2.3.91 the glib iteration and mainloop functions didn't release
 
92
        # global interpreter lock, thus breaking thread and signal support.
 
93
        if getattr(gobject, "pygtk_version", ()) >= (2, 3, 91) and not useGtk:
 
94
            self.context = gobject.main_context_default()
 
95
            self.__pending = self.context.pending
 
96
            self.__iteration = self.context.iteration
 
97
            self.loop = gobject.MainLoop()
 
98
            self.__crash = self.loop.quit
 
99
            self.__run = self.loop.run
 
100
        else:
 
101
            import gtk
 
102
            self.__pending = gtk.events_pending
 
103
            self.__iteration = gtk.main_iteration
 
104
            self.__crash = _our_mainquit
 
105
            self.__run = gtk.main
 
106
 
 
107
    # The input_add function in pygtk1 checks for objects with a
 
108
    # 'fileno' method and, if present, uses the result of that method
 
109
    # as the input source. The pygtk2 input_add does not do this. The
 
110
    # function below replicates the pygtk1 functionality.
 
111
 
 
112
    # In addition, pygtk maps gtk.input_add to _gobject.io_add_watch, and
 
113
    # g_io_add_watch() takes different condition bitfields than
 
114
    # gtk_input_add(). We use g_io_add_watch() here in case pygtk fixes this
 
115
    # bug.
 
116
    def input_add(self, source, condition, callback):
 
117
        if hasattr(source, 'fileno'):
 
118
            # handle python objects
 
119
            def wrapper(source, condition, real_s=source, real_cb=callback):
 
120
                return real_cb(real_s, condition)
 
121
            return gobject.io_add_watch(source.fileno(), condition, wrapper)
 
122
        else:
 
123
            return gobject.io_add_watch(source, condition, callback)
 
124
 
 
125
 
 
126
    def _add(self, source, primary, other, primaryFlag, otherFlag):
 
127
        """
 
128
        Add the given L{FileDescriptor} for monitoring either for reading or
 
129
        writing. If the file is already monitored for the other operation, we
 
130
        delete the previous registration and re-register it for both reading
 
131
        and writing.
 
132
        """
 
133
        if source in primary:
 
134
            return
 
135
        flags = primaryFlag
 
136
        if source in other:
 
137
            gobject.source_remove(self._sources[source])
 
138
            flags |= otherFlag
 
139
        self._sources[source] = self.input_add(source, flags, self.callback)
 
140
        primary.add(source)
 
141
 
 
142
 
 
143
    def addReader(self, reader):
 
144
        """
 
145
        Add a L{FileDescriptor} for monitoring of data available to read.
 
146
        """
 
147
        self._add(reader, self._reads, self._writes, INFLAGS, OUTFLAGS)
 
148
 
 
149
 
 
150
    def addWriter(self, writer):
 
151
        """
 
152
        Add a L{FileDescriptor} for monitoring ability to write data.
 
153
        """
 
154
        self._add(writer, self._writes, self._reads, OUTFLAGS, INFLAGS)
 
155
 
 
156
 
 
157
    def getReaders(self):
 
158
        """
 
159
        Retrieve the list of current L{FileDescriptor} monitored for reading.
 
160
        """
 
161
        return list(self._reads)
 
162
 
 
163
 
 
164
    def getWriters(self):
 
165
        """
 
166
        Retrieve the list of current L{FileDescriptor} monitored for writing.
 
167
        """
 
168
        return list(self._writes)
 
169
 
 
170
 
 
171
    def removeAll(self):
 
172
        """
 
173
        Remove monitoring for all registered L{FileDescriptor}s.
 
174
        """
 
175
        return self._removeAll(self._reads, self._writes)
 
176
 
 
177
 
 
178
    def _remove(self, source, primary, other, flags):
 
179
        """
 
180
        Remove monitoring the given L{FileDescriptor} for either reading or
 
181
        writing. If it's still monitored for the other operation, we
 
182
        re-register the L{FileDescriptor} for only that operation.
 
183
        """
 
184
        if source not in primary:
 
185
            return
 
186
        gobject.source_remove(self._sources[source])
 
187
        primary.remove(source)
 
188
        if source in other:
 
189
            self._sources[source] = self.input_add(
 
190
                source, flags, self.callback)
 
191
        else:
 
192
            self._sources.pop(source)
 
193
 
 
194
 
 
195
    def removeReader(self, reader):
 
196
        """
 
197
        Stop monitoring the given L{FileDescriptor} for reading.
 
198
        """
 
199
        self._remove(reader, self._reads, self._writes, OUTFLAGS)
 
200
 
 
201
 
 
202
    def removeWriter(self, writer):
 
203
        """
 
204
        Stop monitoring the given L{FileDescriptor} for writing.
 
205
        """
 
206
        self._remove(writer, self._writes, self._reads, INFLAGS)
 
207
 
 
208
 
 
209
    doIterationTimer = None
 
210
 
 
211
    def doIterationTimeout(self, *args):
 
212
        self.doIterationTimer = None
 
213
        return 0 # auto-remove
 
214
 
 
215
 
 
216
    def doIteration(self, delay):
 
217
        # flush some pending events, return if there was something to do
 
218
        # don't use the usual "while self.context.pending(): self.context.iteration()"
 
219
        # idiom because lots of IO (in particular test_tcp's
 
220
        # ProperlyCloseFilesTestCase) can keep us from ever exiting.
 
221
        log.msg(channel='system', event='iteration', reactor=self)
 
222
        if self.__pending():
 
223
            self.__iteration(0)
 
224
            return
 
225
        # nothing to do, must delay
 
226
        if delay == 0:
 
227
            return # shouldn't delay, so just return
 
228
        self.doIterationTimer = gobject.timeout_add(int(delay * 1000),
 
229
                                                self.doIterationTimeout)
 
230
        # This will either wake up from IO or from a timeout.
 
231
        self.__iteration(1) # block
 
232
        # note: with the .simulate timer below, delays > 0.1 will always be
 
233
        # woken up by the .simulate timer
 
234
        if self.doIterationTimer:
 
235
            # if woken by IO, need to cancel the timer
 
236
            gobject.source_remove(self.doIterationTimer)
 
237
            self.doIterationTimer = None
 
238
 
 
239
 
 
240
    def crash(self):
 
241
        posixbase.PosixReactorBase.crash(self)
 
242
        self.__crash()
 
243
 
 
244
 
 
245
    def run(self, installSignalHandlers=1):
 
246
        self.startRunning(installSignalHandlers=installSignalHandlers)
 
247
        gobject.timeout_add(0, self.simulate)
 
248
        if self._started:
 
249
            self.__run()
 
250
 
 
251
 
 
252
    def _doReadOrWrite(self, source, condition, faildict={
 
253
        error.ConnectionDone: failure.Failure(error.ConnectionDone()),
 
254
        error.ConnectionLost: failure.Failure(error.ConnectionLost()),
 
255
        }):
 
256
        why = None
 
257
        didRead = None
 
258
        if condition & POLL_DISCONNECTED and \
 
259
               not (condition & gobject.IO_IN):
 
260
            why = main.CONNECTION_LOST
 
261
        else:
 
262
            try:
 
263
                if condition & gobject.IO_IN:
 
264
                    why = source.doRead()
 
265
                    didRead = source.doRead
 
266
                if not why and condition & gobject.IO_OUT:
 
267
                    # if doRead caused connectionLost, don't call doWrite
 
268
                    # if doRead is doWrite, don't call it again.
 
269
                    if not source.disconnected and source.doWrite != didRead:
 
270
                        why = source.doWrite()
 
271
                        didRead = source.doWrite # if failed it was in write
 
272
            except:
 
273
                why = sys.exc_info()[1]
 
274
                log.msg('Error In %s' % source)
 
275
                log.deferr()
 
276
 
 
277
        if why:
 
278
            self._disconnectSelectable(source, why, didRead == source.doRead)
 
279
 
 
280
 
 
281
    def callback(self, source, condition):
 
282
        log.callWithLogger(source, self._doReadOrWrite, source, condition)
 
283
        self.simulate() # fire Twisted timers
 
284
        return 1 # 1=don't auto-remove the source
 
285
 
 
286
 
 
287
    def simulate(self):
 
288
        """
 
289
        Run simulation loops and reschedule callbacks.
 
290
        """
 
291
        if self._simtag is not None:
 
292
            gobject.source_remove(self._simtag)
 
293
        self.runUntilCurrent()
 
294
        timeout = min(self.timeout(), 0.1)
 
295
        if timeout is None:
 
296
            timeout = 0.1
 
297
        # grumble
 
298
        self._simtag = gobject.timeout_add(int(timeout * 1010), self.simulate)
 
299
 
 
300
 
 
301
 
 
302
class PortableGtkReactor(selectreactor.SelectReactor):
 
303
    """
 
304
    Reactor that works on Windows.
 
305
 
 
306
    Sockets aren't supported by GTK+'s input_add on Win32.
 
307
    """
 
308
    _simtag = None
 
309
 
 
310
    def crash(self):
 
311
        selectreactor.SelectReactor.crash(self)
 
312
        import gtk
 
313
        # mainquit is deprecated in newer versions
 
314
        if gtk.main_level():
 
315
            if hasattr(gtk, 'main_quit'):
 
316
                gtk.main_quit()
 
317
            else:
 
318
                gtk.mainquit()
 
319
 
 
320
 
 
321
    def run(self, installSignalHandlers=1):
 
322
        import gtk
 
323
        self.startRunning(installSignalHandlers=installSignalHandlers)
 
324
        gobject.timeout_add(0, self.simulate)
 
325
        # mainloop is deprecated in newer versions
 
326
        if hasattr(gtk, 'main'):
 
327
            gtk.main()
 
328
        else:
 
329
            gtk.mainloop()
 
330
 
 
331
 
 
332
    def simulate(self):
 
333
        """
 
334
        Run simulation loops and reschedule callbacks.
 
335
        """
 
336
        if self._simtag is not None:
 
337
            gobject.source_remove(self._simtag)
 
338
        self.iterate()
 
339
        timeout = min(self.timeout(), 0.1)
 
340
        if timeout is None:
 
341
            timeout = 0.1
 
342
        # grumble
 
343
        self._simtag = gobject.timeout_add(int(timeout * 1010), self.simulate)
 
344
 
 
345
 
 
346
 
 
347
def install(useGtk=True):
 
348
    """
 
349
    Configure the twisted mainloop to be run inside the gtk mainloop.
 
350
 
 
351
    @param useGtk: should glib rather than GTK+ event loop be
 
352
        used (this will be slightly faster but does not support GUI).
 
353
    """
 
354
    reactor = Gtk2Reactor(useGtk)
 
355
    from twisted.internet.main import installReactor
 
356
    installReactor(reactor)
 
357
    return reactor
 
358
 
 
359
 
 
360
 
 
361
def portableInstall(useGtk=True):
 
362
    """
 
363
    Configure the twisted mainloop to be run inside the gtk mainloop.
 
364
    """
 
365
    reactor = PortableGtkReactor()
 
366
    from twisted.internet.main import installReactor
 
367
    installReactor(reactor)
 
368
    return reactor
 
369
 
 
370
 
 
371
 
 
372
if runtime.platform.getType() != 'posix':
 
373
    install = portableInstall
 
374
 
 
375
 
 
376
 
 
377
__all__ = ['install']