Ticket #4558: gi-support.2.patch

File gi-support.2.patch, 31.4 KB (added by dobey, 3 years ago)

Updated patch per review comments

  • twisted/internet/_glibbase.py

    === added file 'twisted/internet/_glibbase.py'
     
     1# -*- test-case-name: twisted.internet.test -*- 
     2# Copyright (c) Twisted Matrix Laboratories. 
     3# Copyright (c) 2011-2012 Canonical Ltd. 
     4# See LICENSE for details. 
     5""" 
     6This module provides base support for Twisted to interact with the glib/gtk 
     7mainloops. 
     8 
     9The classes in this module should not be used directly, but rather you should 
     10import gireactor or gtk3reactor for GObject Introspection based applications, 
     11or glib2reactor or gtk2reactor for applications using legacy static bindings. 
     12""" 
     13 
     14import signal 
     15 
     16from twisted.internet import base, posixbase, selectreactor 
     17from twisted.internet.interfaces import IReactorFDSet 
     18from twisted.python import log, runtime 
     19from twisted.python.compat import set 
     20from zope.interface import implements 
     21 
     22class GlibSignalMixin(object): 
     23 
     24    if runtime.platformType == 'posix': 
     25 
     26        def _handleSignals(self): 
     27            # Let the base class do its thing, but pygtk is probably 
     28            # going to stomp on us so go beyond that and set up some 
     29            # signal handling which pygtk won't mess with.  This would 
     30            # be better done by letting this reactor select a 
     31            # different implementation of installHandler for 
     32            # _SIGCHLDWaker to use.  Then, at least, we could fall 
     33            # back to our extension module.  See #4286. 
     34            from twisted.internet.process import ( 
     35                reapAllProcesses as _reapAllProcesses) 
     36            base._SignalReactorMixin._handleSignals(self) 
     37            signal.signal(signal.SIGCHLD, 
     38                          lambda *a: self.callFromThread(_reapAllProcesses)) 
     39            if getattr(signal, "siginterrupt", None) is not None: 
     40                signal.siginterrupt(signal.SIGCHLD, False) 
     41            # Like the base, reap processes now in case a process 
     42            # exited before the handlers above were installed. 
     43            _reapAllProcesses() 
     44 
     45 
     46class GlibWaker(posixbase._UnixWaker): 
     47    """ 
     48    Run scheduled events after waking up. 
     49    """ 
     50 
     51    def doRead(self): 
     52        posixbase._UnixWaker.doRead(self) 
     53        self.reactor._simulate() 
     54 
     55 
     56class GlibReactorBase(GlibSignalMixin, 
     57                      posixbase.PosixReactorBase, posixbase._PollLikeMixin): 
     58    """ 
     59    GObject event loop reactor. 
     60 
     61    @ivar _sources: A dictionary mapping L{FileDescriptor} instances to 
     62        GSource handles. 
     63 
     64    @ivar _reads: A set of L{FileDescriptor} instances currently monitored for 
     65        reading. 
     66 
     67    @ivar _writes: A set of L{FileDescriptor} instances currently monitored for 
     68        writing. 
     69 
     70    @ivar _simtag: A GSource handle for the next L{simulate} call. 
     71    """ 
     72    implements(IReactorFDSet) 
     73 
     74    # Install a waker that knows it needs to call C{_simulate} in order to run 
     75    # callbacks queued from a thread: 
     76    _wakerFactory = GlibWaker 
     77 
     78    def __init__(self): 
     79        self._simtag = None 
     80        self._reads = set() 
     81        self._writes = set() 
     82        self._sources = {} 
     83        posixbase.PosixReactorBase.__init__(self) 
     84 
     85    def input_add(self, source, condition, callback): 
     86        """This is a stub to be implemented by inheriting classes.""" 
     87        raise NotImplementedError() 
     88 
     89    def _ioEventCallback(self, source, condition): 
     90        """ 
     91        Called by event loop when an I/O event occurs. 
     92        """ 
     93        log.callWithLogger( 
     94            source, self._doReadOrWrite, source, source, condition) 
     95        return True  # True = don't auto-remove the source 
     96 
     97    def _add(self, source, primary, other, primaryFlag, otherFlag): 
     98        """ 
     99        Add the given L{FileDescriptor} for monitoring either for reading or 
     100        writing. If the file is already monitored for the other operation, we 
     101        delete the previous registration and re-register it for both reading 
     102        and writing. 
     103        """ 
     104        if source in primary: 
     105            return 
     106        flags = primaryFlag 
     107        if source in other: 
     108            self._source_remove(self._sources[source]) 
     109            flags |= otherFlag 
     110        self._sources[source] = self.input_add( 
     111            source, flags, self._ioEventCallback) 
     112        primary.add(source) 
     113 
     114    def addReader(self, reader): 
     115        """ 
     116        Add a L{FileDescriptor} for monitoring of data available to read. 
     117        """ 
     118        self._add(reader, self._reads, self._writes, 
     119                  self.INFLAGS, self.OUTFLAGS) 
     120 
     121    def addWriter(self, writer): 
     122        """ 
     123        Add a L{FileDescriptor} for monitoring ability to write data. 
     124        """ 
     125        self._add(writer, self._writes, self._reads, 
     126                  self.OUTFLAGS, self.INFLAGS) 
     127 
     128    def getReaders(self): 
     129        """ 
     130        Retrieve the list of current L{FileDescriptor} monitored for reading. 
     131        """ 
     132        return list(self._reads) 
     133 
     134    def getWriters(self): 
     135        """ 
     136        Retrieve the list of current L{FileDescriptor} monitored for writing. 
     137        """ 
     138        return list(self._writes) 
     139 
     140    def removeAll(self): 
     141        """ 
     142        Remove monitoring for all registered L{FileDescriptor}s. 
     143        """ 
     144        return self._removeAll(self._reads, self._writes) 
     145 
     146    def _remove(self, source, primary, other, flags): 
     147        """ 
     148        Remove monitoring the given L{FileDescriptor} for either reading or 
     149        writing. If it's still monitored for the other operation, we 
     150        re-register the L{FileDescriptor} for only that operation. 
     151        """ 
     152        if source not in primary: 
     153            return 
     154        self._source_remove(self._sources[source]) 
     155        primary.remove(source) 
     156        if source in other: 
     157            self._sources[source] = self.input_add( 
     158                source, flags, self._ioEventCallback) 
     159        else: 
     160            self._sources.pop(source) 
     161 
     162    def removeReader(self, reader): 
     163        """ 
     164        Stop monitoring the given L{FileDescriptor} for reading. 
     165        """ 
     166        self._remove(reader, self._reads, self._writes, self.OUTFLAGS) 
     167 
     168    def removeWriter(self, writer): 
     169        """ 
     170        Stop monitoring the given L{FileDescriptor} for writing. 
     171        """ 
     172        self._remove(writer, self._writes, self._reads, self.INFLAGS) 
     173 
     174    def iterate(self, delay=0): 
     175        """ 
     176        One iteration of the event loop, for trial's use. 
     177 
     178        This is not used for actual reactor runs. 
     179        """ 
     180        self.runUntilCurrent() 
     181        while self._pending(): 
     182            self._iteration(0) 
     183 
     184    def crash(self): 
     185        """ 
     186        Crash the reactor. 
     187        """ 
     188        posixbase.PosixReactorBase.crash(self) 
     189        self._crash() 
     190 
     191    def stop(self): 
     192        """ 
     193        Stop the reactor. 
     194        """ 
     195        posixbase.PosixReactorBase.stop(self) 
     196        # The base implementation only sets a flag, to ensure shutting down is 
     197        # not reentrant. Unfortunately, this flag is not meaningful to the 
     198        # gobject event loop. We therefore call wakeUp() to ensure the event 
     199        # loop will call back into Twisted once this iteration is done. This 
     200        # will result in self.runUntilCurrent() being called, where the stop 
     201        # flag will trigger the actual shutdown process, eventually calling 
     202        # crash() which will do the actual gobject event loop shutdown. 
     203        self.wakeUp() 
     204 
     205    def run(self, installSignalHandlers=True): 
     206        """ 
     207        Run the reactor. 
     208        """ 
     209        self.callWhenRunning(self._reschedule) 
     210        self.startRunning(installSignalHandlers=installSignalHandlers) 
     211        if self._started: 
     212            self._run() 
     213 
     214    def callLater(self, *args, **kwargs): 
     215        """ 
     216        Schedule a C{DelayedCall}. 
     217        """ 
     218        result = posixbase.PosixReactorBase.callLater(self, *args, **kwargs) 
     219        # Make sure we'll get woken up at correct time to handle this new 
     220        # scheduled call: 
     221        self._reschedule() 
     222        return result 
     223 
     224    def _reschedule(self): 
     225        """ 
     226        Schedule a glib timeout for C{_simulate}. 
     227        """ 
     228        if self._simtag is not None: 
     229            self._source_remove(self._simtag) 
     230            self._simtag = None 
     231        timeout = self.timeout() 
     232        if timeout is not None: 
     233            self._simtag = self._timeout_add(int(timeout * 1000), 
     234                                             self._simulate) 
     235 
     236    def _simulate(self): 
     237        """ 
     238        Run timers, and then reschedule glib timeout for next scheduled event. 
     239        """ 
     240        self.runUntilCurrent() 
     241        self._reschedule() 
     242 
     243 
     244class PortableGlibReactorBase(GlibSignalMixin, selectreactor.SelectReactor): 
     245    """ 
     246    Portable GObject event loop reactor. 
     247    """ 
     248    def __init__(self): 
     249        self._simtag = None 
     250        selectreactor.SelectReactor.__init__(self) 
     251 
     252    def crash(self): 
     253        selectreactor.SelectReactor.crash(self) 
     254        self._crash() 
     255 
     256    def run(self, installSignalHandlers=True): 
     257        self.startRunning(installSignalHandlers=installSignalHandlers) 
     258        self.idle_add(self.simulate) 
     259        self._run() 
     260 
     261    def simulate(self): 
     262        """ 
     263        Run simulation loops and reschedule callbacks. 
     264        """ 
     265        if self._simtag is not None: 
     266            self._source_remove(self._simtag) 
     267        self.iterate() 
     268        timeout = min(self.timeout(), 0.1) 
     269        if timeout is None: 
     270            timeout = 0.1 
     271        self._simtag = self._timeout_add(int(timeout * 1000), self.simulate) 
  • twisted/internet/gireactor.py

    === added file 'twisted/internet/gireactor.py'
     
     1# -*- test-case-name: twisted.internet.test -*- 
     2# Copyright (c) Twisted Matrix Laboratories. 
     3# Copyright (c) 2011-2012 Canonical Ltd. 
     4# See LICENSE for details. 
     5 
     6""" 
     7This module provides support for Twisted to interact with the glib/gtk3 
     8mainloop via GObject Introspection. 
     9 
     10In order to use this support, simply do the following:: 
     11 
     12    |  from twisted.internet import gireactor 
     13    |  gireactor.install() 
     14 
     15Then use twisted.internet APIs as usual.  The other methods here are not 
     16intended to be called directly. 
     17 
     18When installing the reactor, you can choose whether to use the glib 
     19event loop or the GTK+ event loop which is based on it but adds GUI 
     20integration. 
     21""" 
     22 
     23import sys 
     24 
     25if 'gobject' in sys.modules: 
     26    raise ImportError(('Introspected and static bindings must not be mixed.' 
     27                       ' Use glib2reactor or gtk2reactor instead.')) 
     28 
     29 
     30from gi.repository import GLib 
     31# We need to override sys.modules with these to prevent imports. 
     32# This is required, as importing these can result in SEGFAULTs. 
     33sys.modules['glib'] = None 
     34sys.modules['gobject'] = None 
     35sys.modules['gio'] = None 
     36sys.modules['gtk'] = None 
     37 
     38from twisted.internet import _glibbase 
     39from twisted.python import runtime 
     40 
     41GLib.threads_init() 
     42 
     43 
     44class GIReactor(_glibbase.GlibReactorBase): 
     45    """ 
     46    GObject event loop reactor. 
     47 
     48    @ivar _sources: A dictionary mapping L{FileDescriptor} instances to 
     49        GSource handles. 
     50 
     51    @ivar _reads: A set of L{FileDescriptor} instances currently monitored for 
     52        reading. 
     53 
     54    @ivar _writes: A set of L{FileDescriptor} instances currently monitored for 
     55        writing. 
     56 
     57    @ivar _simtag: A GSource handle for the next L{simulate} call. 
     58    """ 
     59    _POLL_DISCONNECTED = (GLib.IOCondition.HUP | GLib.IOCondition.ERR | 
     60                          GLib.IOCondition.NVAL) 
     61    _POLL_IN = GLib.IOCondition.IN 
     62    _POLL_OUT = GLib.IOCondition.OUT 
     63 
     64    # glib's iochannel sources won't tell us about any events that we haven't 
     65    # asked for, even if those events aren't sensible inputs to the poll() 
     66    # call. 
     67    INFLAGS = _POLL_IN | _POLL_DISCONNECTED 
     68    OUTFLAGS = _POLL_OUT | _POLL_DISCONNECTED 
     69 
     70    def __init__(self, useGtk=False): 
     71        _glibbase.GlibReactorBase.__init__(self) 
     72 
     73        self._source_remove = GLib.source_remove 
     74        self._timeout_add = GLib.timeout_add 
     75 
     76        if useGtk: 
     77            from gi.repository import Gtk 
     78 
     79            self._pending = Gtk.events_pending 
     80            self._iteration = Gtk.main_iteration_do 
     81            self._crash = Gtk.main_quit 
     82            self._run = Gtk.main 
     83        else: 
     84            self.context = GLib.main_context_default() 
     85            self._pending = self.context.pending 
     86            self._iteration = self.context.iteration 
     87            self.loop = GLib.MainLoop() 
     88            self._crash = lambda: GLib.idle_add(self.loop.quit) 
     89            self._run = self.loop.run 
     90 
     91    # The input_add function in pygtk1 checks for objects with a 
     92    # 'fileno' method and, if present, uses the result of that method 
     93    # as the input source. The pygtk2 input_add does not do this. The 
     94    # function below replicates the pygtk1 functionality. 
     95 
     96    # In addition, pygtk maps gtk.input_add to _gobject.io_add_watch, and 
     97    # g_io_add_watch() takes different condition bitfields than 
     98    # gtk_input_add(). We use g_io_add_watch() here in case pygtk fixes this 
     99    # bug. 
     100    def input_add(self, source, condition, callback): 
     101        if hasattr(source, 'fileno'): 
     102            # handle python objects 
     103            def wrapper(source, condition, real_s=source, real_cb=callback): 
     104                return real_cb(real_s, condition) 
     105            return GLib.io_add_watch(source.fileno(), condition, wrapper) 
     106        else: 
     107            return GLib.io_add_watch(source, condition, callback) 
     108 
     109 
     110class PortableGIReactor(_glibbase.PortableGlibReactorBase): 
     111    """ 
     112    Portable GObject Introspection event loop reactor. 
     113    """ 
     114    def __init__(self, useGtk=False): 
     115        _glibbase.PortableGlibReactorBase.__init__(self) 
     116 
     117        self._source_remove = GLib.source_remove 
     118        self._idle_add = GLib.idle_add 
     119        self._timeout_add = GLib.timeout_add 
     120 
     121        if useGtk: 
     122            from gi.repository import Gtk 
     123 
     124            self._crash = Gtk.main_quit 
     125            self._run = Gtk.main 
     126        else: 
     127            self.loop = GLib.MainLoop() 
     128            self._crash = lambda: GLib.idle_add(self.loop.quit) 
     129            self._run = self.loop.run 
     130 
     131 
     132def install(useGtk=False): 
     133    """ 
     134    Configure the twisted mainloop to be run inside the glib mainloop. 
     135 
     136    @param useGtk: should GTK+ rather than glib event loop be 
     137        used (this will be slightly slower but does support GUI). 
     138    """ 
     139    if runtime.platform.getType() == 'posix': 
     140        reactor = GIReactor(useGtk=useGtk) 
     141    else: 
     142        reactor = PortableGIReactor(useGtk=useGtk) 
     143 
     144    from twisted.internet.main import installReactor 
     145    installReactor(reactor) 
     146    return reactor 
     147 
     148 
     149__all__ = ['install'] 
  • twisted/internet/gtk2reactor.py

    === modified file 'twisted/internet/gtk2reactor.py'
    integration. 
    2121""" 
    2222 
    2323# System Imports 
    24 import sys, signal 
     24import sys 
    2525 
    26 from zope.interface import implements 
     26if 'gi' in sys.modules: 
     27    raise ImportError(('Introspected and static bindings must not be mixed.' 
     28                       ' Use twisted.internet.gireactor instead.')) 
     29 
     30# Disable gi imports to avoid potential problems. 
     31sys.modules['gi'] = None 
    2732 
    2833try: 
    2934    if not hasattr(sys, 'frozen'): 
    if hasattr(gobject, "threads_init"): 
    4045    gobject.threads_init() 
    4146 
    4247# Twisted Imports 
    43 from twisted.python import log, runtime 
    44 from twisted.python.compat import set 
    45 from twisted.internet.interfaces import IReactorFDSet 
    46 from twisted.internet import base, posixbase, selectreactor 
    47  
    48 POLL_DISCONNECTED = gobject.IO_HUP | gobject.IO_ERR | gobject.IO_NVAL 
    49  
    50 # glib's iochannel sources won't tell us about any events that we haven't 
    51 # asked for, even if those events aren't sensible inputs to the poll() 
    52 # call. 
    53 INFLAGS = gobject.IO_IN | POLL_DISCONNECTED 
    54 OUTFLAGS = gobject.IO_OUT | POLL_DISCONNECTED 
    55  
    56  
    57  
    58 def _our_mainquit(): 
    59     # XXX: gtk.main_quit() (which is used for crash()) raises an exception if 
    60     # gtk.main_level() == 0; however, all the tests freeze if we use this 
    61     # function to stop the reactor.  what gives?  (I believe this may have been 
    62     # a stupid mistake where I forgot to import gtk here... I will remove this 
    63     # comment if the tests pass) 
    64     import gtk 
    65     if gtk.main_level(): 
    66         gtk.main_quit() 
    67  
    68  
    69  
    70 class _Gtk2SignalMixin(object): 
    71     if runtime.platformType == 'posix': 
    72         def _handleSignals(self): 
    73             # Let the base class do its thing, but pygtk is probably 
    74             # going to stomp on us so go beyond that and set up some 
    75             # signal handling which pygtk won't mess with.  This would 
    76             # be better done by letting this reactor select a 
    77             # different implementation of installHandler for 
    78             # _SIGCHLDWaker to use.  Then, at least, we could fall 
    79             # back to our extension module.  See #4286. 
    80             from twisted.internet.process import reapAllProcesses as _reapAllProcesses 
    81             base._SignalReactorMixin._handleSignals(self) 
    82             signal.signal(signal.SIGCHLD, lambda *a: self.callFromThread(_reapAllProcesses)) 
    83             if getattr(signal, "siginterrupt", None) is not None: 
    84                 signal.siginterrupt(signal.SIGCHLD, False) 
    85             # Like the base, reap processes now in case a process 
    86             # exited before the handlers above were installed. 
    87             _reapAllProcesses() 
    88  
     48from twisted.internet import _glibbase 
     49from twisted.python import runtime 
    8950 
    9051 
    91 class _Gtk2Waker(posixbase._UnixWaker): 
    92     """ 
    93     Run scheduled events after waking up. 
    94     """ 
    95  
    96     def doRead(self): 
    97         posixbase._UnixWaker.doRead(self) 
    98         self.reactor._simulate() 
    99  
    100  
    101  
    102 class Gtk2Reactor(_Gtk2SignalMixin, posixbase.PosixReactorBase, posixbase._PollLikeMixin): 
     52class Gtk2Reactor(_glibbase.GlibReactorBase): 
    10353    """ 
    10454    GTK+-2 event loop reactor. 
    10555 
    class Gtk2Reactor(_Gtk2SignalMixin, posi 
    13080 
    13181    @ivar _simtag: A gtk timeout handle for the next L{_simulate} call. 
    13282    """ 
    133     implements(IReactorFDSet) 
    134  
    135     _POLL_DISCONNECTED = POLL_DISCONNECTED 
     83    _POLL_DISCONNECTED = gobject.IO_HUP | gobject.IO_ERR | gobject.IO_NVAL 
    13684    _POLL_IN = gobject.IO_IN 
    13785    _POLL_OUT = gobject.IO_OUT 
    13886 
    139     # Install a waker that knows it needs to call C{_simulate} in order to run 
    140     # callbacks queued from a thread: 
    141     _wakerFactory = _Gtk2Waker 
     87    # glib's iochannel sources won't tell us about any events that we haven't 
     88    # asked for, even if those events aren't sensible inputs to the poll() 
     89    # call. 
     90    INFLAGS = _POLL_IN | _POLL_DISCONNECTED 
     91    OUTFLAGS = _POLL_OUT | _POLL_DISCONNECTED 
    14292 
    14393    def __init__(self, useGtk=True): 
    144         self._simtag = None 
    145         self._reads = set() 
    146         self._writes = set() 
    147         self._sources = {} 
    148         posixbase.PosixReactorBase.__init__(self) 
     94        _glibbase.GlibReactorBase.__init__(self) 
     95 
     96        self._source_remove = gobject.source_remove 
     97        self._timeout_add = gobject.timeout_add 
     98 
    14999        # pre 2.3.91 the glib iteration and mainloop functions didn't release 
    150100        # global interpreter lock, thus breaking thread and signal support. 
    151101        if getattr(gobject, "pygtk_version", ()) >= (2, 3, 91) and not useGtk: 
    152102            self.context = gobject.main_context_default() 
    153             self.__pending = self.context.pending 
    154             self.__iteration = self.context.iteration 
     103            self._pending = self.context.pending 
     104            self._iteration = self.context.iteration 
    155105            self.loop = gobject.MainLoop() 
    156             self.__crash = self.loop.quit 
    157             self.__run = self.loop.run 
     106            self._crash = self.loop.quit 
     107            self._run = self.loop.run 
    158108        else: 
    159109            import gtk 
    160             self.__pending = gtk.events_pending 
    161             self.__iteration = gtk.main_iteration 
    162             self.__crash = _our_mainquit 
    163             self.__run = gtk.main 
     110 
     111            def mainquit(): 
     112                if gtk.main_level(): 
     113                    gtk.main_quit() 
     114 
     115            self._pending = gtk.events_pending 
     116            self._iteration = gtk.main_iteration 
     117            self._crash = mainquit 
     118            self._run = gtk.main 
    164119 
    165120 
    166121    # The input_add function in pygtk1 checks for objects with a 
    class Gtk2Reactor(_Gtk2SignalMixin, posi 
    182137            return gobject.io_add_watch(source, condition, callback) 
    183138 
    184139 
    185     def _ioEventCallback(self, source, condition): 
    186         """ 
    187         Called by event loop when an I/O event occurs. 
    188         """ 
    189         log.callWithLogger( 
    190             source, self._doReadOrWrite, source, source, condition) 
    191         return 1 # 1=don't auto-remove the source 
    192  
    193  
    194     def _add(self, source, primary, other, primaryFlag, otherFlag): 
    195         """ 
    196         Add the given L{FileDescriptor} for monitoring either for reading or 
    197         writing. If the file is already monitored for the other operation, we 
    198         delete the previous registration and re-register it for both reading 
    199         and writing. 
    200         """ 
    201         if source in primary: 
    202             return 
    203         flags = primaryFlag 
    204         if source in other: 
    205             gobject.source_remove(self._sources[source]) 
    206             flags |= otherFlag 
    207         self._sources[source] = self.input_add( 
    208             source, flags, self._ioEventCallback) 
    209         primary.add(source) 
    210  
    211  
    212     def addReader(self, reader): 
    213         """ 
    214         Add a L{FileDescriptor} for monitoring of data available to read. 
    215         """ 
    216         self._add(reader, self._reads, self._writes, INFLAGS, OUTFLAGS) 
    217  
    218  
    219     def addWriter(self, writer): 
    220         """ 
    221         Add a L{FileDescriptor} for monitoring ability to write data. 
    222         """ 
    223         self._add(writer, self._writes, self._reads, OUTFLAGS, INFLAGS) 
    224  
    225  
    226     def getReaders(self): 
    227         """ 
    228         Retrieve the list of current L{FileDescriptor} monitored for reading. 
    229         """ 
    230         return list(self._reads) 
    231  
    232  
    233     def getWriters(self): 
    234         """ 
    235         Retrieve the list of current L{FileDescriptor} monitored for writing. 
    236         """ 
    237         return list(self._writes) 
    238  
    239  
    240     def removeAll(self): 
    241         """ 
    242         Remove monitoring for all registered L{FileDescriptor}s. 
    243         """ 
    244         return self._removeAll(self._reads, self._writes) 
    245  
    246  
    247     def _remove(self, source, primary, other, flags): 
    248         """ 
    249         Remove monitoring the given L{FileDescriptor} for either reading or 
    250         writing. If it's still monitored for the other operation, we 
    251         re-register the L{FileDescriptor} for only that operation. 
    252         """ 
    253         if source not in primary: 
    254             return 
    255         gobject.source_remove(self._sources[source]) 
    256         primary.remove(source) 
    257         if source in other: 
    258             self._sources[source] = self.input_add( 
    259                 source, flags, self._ioEventCallback) 
    260         else: 
    261             self._sources.pop(source) 
    262  
    263  
    264     def removeReader(self, reader): 
    265         """ 
    266         Stop monitoring the given L{FileDescriptor} for reading. 
    267         """ 
    268         self._remove(reader, self._reads, self._writes, OUTFLAGS) 
    269  
    270  
    271     def removeWriter(self, writer): 
    272         """ 
    273         Stop monitoring the given L{FileDescriptor} for writing. 
    274         """ 
    275         self._remove(writer, self._writes, self._reads, INFLAGS) 
    276  
    277  
    278     def iterate(self, delay=0): 
    279         """ 
    280         One iteration of the event loop, for trial's use. 
    281  
    282         This is not used for actual reactor runs. 
    283         """ 
    284         self.runUntilCurrent() 
    285         while self.__pending(): 
    286            self.__iteration(0) 
    287  
    288  
    289     def crash(self): 
    290         """ 
    291         Crash the reactor. 
    292         """ 
    293         posixbase.PosixReactorBase.crash(self) 
    294         self.__crash() 
    295  
    296  
    297     def stop(self): 
    298         """ 
    299         Stop the reactor. 
    300         """ 
    301         posixbase.PosixReactorBase.stop(self) 
    302         # The base implementation only sets a flag, to ensure shutting down is 
    303         # not reentrant. Unfortunately, this flag is not meaningful to the 
    304         # gobject event loop. We therefore call wakeUp() to ensure the event 
    305         # loop will call back into Twisted once this iteration is done. This 
    306         # will result in self.runUntilCurrent() being called, where the stop 
    307         # flag will trigger the actual shutdown process, eventually calling 
    308         # crash() which will do the actual gobject event loop shutdown. 
    309         self.wakeUp() 
    310  
    311  
    312     def run(self, installSignalHandlers=1): 
    313         """ 
    314         Run the reactor. 
    315         """ 
    316         self.callWhenRunning(self._reschedule) 
    317         self.startRunning(installSignalHandlers=installSignalHandlers) 
    318         if self._started: 
    319             self.__run() 
    320  
    321  
    322     def callLater(self, *args, **kwargs): 
    323         """ 
    324         Schedule a C{DelayedCall}. 
    325         """ 
    326         result = posixbase.PosixReactorBase.callLater(self, *args, **kwargs) 
    327         # Make sure we'll get woken up at correct time to handle this new 
    328         # scheduled call: 
    329         self._reschedule() 
    330         return result 
    331  
    332  
    333     def _reschedule(self): 
    334         """ 
    335         Schedule a glib timeout for C{_simulate}. 
    336         """ 
    337         if self._simtag is not None: 
    338             gobject.source_remove(self._simtag) 
    339             self._simtag = None 
    340         timeout = self.timeout() 
    341         if timeout is not None: 
    342             self._simtag = gobject.timeout_add(int(timeout * 1000), 
    343                                                self._simulate) 
    344  
    345  
    346     def _simulate(self): 
    347         """ 
    348         Run timers, and then reschedule glib timeout for next scheduled event. 
    349         """ 
    350         self.runUntilCurrent() 
    351         self._reschedule() 
    352  
    353  
    354  
    355 class PortableGtkReactor(_Gtk2SignalMixin, selectreactor.SelectReactor): 
     140class PortableGtkReactor(_glibbase.PortableGlibReactorBase): 
    356141    """ 
    357142    Reactor that works on Windows. 
    358143 
    359144    Sockets aren't supported by GTK+'s input_add on Win32. 
    360145    """ 
    361     _simtag = None 
     146    def __init__(self, useGtk=False): 
     147        _glibbase.PortableGlibReactorBase.__init__(self) 
    362148 
    363     def crash(self): 
    364         selectreactor.SelectReactor.crash(self) 
    365         import gtk 
    366         # mainquit is deprecated in newer versions 
    367         if gtk.main_level(): 
    368             if hasattr(gtk, 'main_quit'): 
    369                 gtk.main_quit() 
    370             else: 
    371                 gtk.mainquit() 
    372  
    373  
    374     def run(self, installSignalHandlers=1): 
    375         import gtk 
    376         self.startRunning(installSignalHandlers=installSignalHandlers) 
    377         gobject.timeout_add(0, self.simulate) 
    378         # mainloop is deprecated in newer versions 
    379         if self._started: 
    380             if hasattr(gtk, 'main'): 
    381                 gtk.main() 
    382             else: 
    383                 gtk.mainloop() 
    384  
    385  
    386     def simulate(self): 
    387         """ 
    388         Run simulation loops and reschedule callbacks. 
    389         """ 
    390         if self._simtag is not None: 
    391             gobject.source_remove(self._simtag) 
    392         self.iterate() 
    393         timeout = min(self.timeout(), 0.01) 
    394         if timeout is None: 
    395             timeout = 0.01 
    396         # grumble 
    397         self._simtag = gobject.timeout_add(int(timeout * 1010), self.simulate) 
     149        self._source_remove = gobject.source_remove 
     150        self._idle_add = gobject.idle_add 
     151        self._timeout_add = gobject.timeout_add 
    398152 
     153        if useGtk: 
     154            import gtk 
     155 
     156            def mainquit(): 
     157                if gtk.main_level(): 
     158                    gtk.main_quit() 
     159 
     160            self._crash = mainquit 
     161            self._run = gtk.main 
     162        else: 
     163            self.loop = gobject.MainLoop() 
     164            self._crash = lambda: gobject.idle_add(self.loop.quit) 
     165            self._run = self.loop.run 
    399166 
    400167 
    401168def install(useGtk=True): 
    def install(useGtk=True): 
    411178    return reactor 
    412179 
    413180 
    414  
    415181def portableInstall(useGtk=True): 
    416182    """ 
    417183    Configure the twisted mainloop to be run inside the gtk mainloop. 
    def portableInstall(useGtk=True): 
    422188    return reactor 
    423189 
    424190 
    425  
    426191if runtime.platform.getType() != 'posix': 
    427192    install = portableInstall 
    428193 
    429194 
    430  
    431195__all__ = ['install'] 
  • twisted/internet/gtk3reactor.py

    === added file 'twisted/internet/gtk3reactor.py'
     
     1 
     2""" 
     3This module provides support for Twisted to interact with the gtk3 mainloop. 
     4This is like gi, but slightly slower and requires a working $DISPLAY. 
     5 
     6In order to use this support, simply do the following:: 
     7 
     8    |  from twisted.internet import gtk3reactor 
     9    |  gtk3reactor.install() 
     10 
     11Then use twisted.internet APIs as usual.  The other methods here are not 
     12intended to be called directly. 
     13 
     14When installing the reactor, you can choose whether to use the glib 
     15event loop or the GTK+ event loop which is based on it but adds GUI 
     16integration. 
     17 
     18Maintainer: Itamar Shtull-Trauring 
     19""" 
     20 
     21from twisted.internet import gireactor 
     22from twisted.python import runtime 
     23 
     24 
     25class Gtk3Reactor(gireactor.GIReactor): 
     26    """ 
     27    The reactor using the gtk3 mainloop. 
     28    """ 
     29 
     30    def __init__(self, useGtk=True): 
     31        """ 
     32        Override init to set the C{useGtk} flag. 
     33        """ 
     34        gireactor.GIReactor.__init__(self, useGtk=useGtk) 
     35 
     36 
     37class PortableGtk3Reactor(gireactor.PortableGIReactor): 
     38    """ 
     39    Portable GTK+ 3.x reactor. 
     40    """ 
     41    def __init__(self, useGtk=True): 
     42        """ 
     43        Override init to set the C{useGtk} flag. 
     44        """ 
     45        gireactor.PortableGIReactor.__init__(self, useGtk=useGtk) 
     46 
     47 
     48def install(useGtk=True): 
     49    """ 
     50    Configure the twisted mainloop to be run inside the glib mainloop. 
     51 
     52    @param useGtk: should GTK+ rather than glib event loop be 
     53        used (this will be slightly slower but does support GUI). 
     54    """ 
     55    if runtime.platform.getType() == 'posix': 
     56        reactor = Gtk3Reactor(useGtk=useGtk) 
     57    else: 
     58        reactor = PortableGtk3Reactor(useGtk=useGtk) 
     59 
     60    from twisted.internet.main import installReactor 
     61    installReactor(reactor) 
     62    return reactor 
     63 
     64 
     65__all__ = ['install'] 
  • twisted/internet/test/reactormixins.py

    === modified file 'twisted/internet/test/reactormixins.py'
    class ReactorBuilder: 
    5656        # it's not _really_ worth it to support on other platforms, 
    5757        # since no one really wants to use it on other platforms. 
    5858        _reactors.extend([ 
     59                "twisted.internet.gireactor.PortableGIReactor", 
     60                "twisted.internet.gtk3reactor.PortableGtk3Reactor", 
    5961                "twisted.internet.gtk2reactor.PortableGtkReactor", 
    6062                "twisted.internet.win32eventreactor.Win32Reactor", 
    6163                "twisted.internet.iocpreactor.reactor.IOCPReactor"]) 
    6264    else: 
    6365        _reactors.extend([ 
     66                "twisted.internet.gireactor.GIReactor", 
     67                "twisted.internet.gtk3reactor.Gtk3Reactor", 
    6468                "twisted.internet.glib2reactor.Glib2Reactor", 
    6569                "twisted.internet.gtk2reactor.Gtk2Reactor", 
    6670                "twisted.internet.kqreactor.KQueueReactor"]) 
  • twisted/plugins/twisted_reactors.py

    === modified file 'twisted/plugins/twisted_reactors.py'
    select = Reactor( 
    1111    'select', 'twisted.internet.selectreactor', 'select(2)-based reactor.') 
    1212wx = Reactor( 
    1313    'wx', 'twisted.internet.wxreactor', 'wxPython integration reactor.') 
     14gi = Reactor( 
     15    'gi', 'twisted.internet.gireactor', 'GObject Introspection integration reactor.') 
     16gtk3 = Reactor( 
     17    'gtk3', 'twisted.internet.gtk3reactor', 'Gtk3 integration reactor.') 
    1418gtk = Reactor( 
    1519    'gtk', 'twisted.internet.gtkreactor', 'Gtk1 integration reactor.') 
    1620gtk2 = Reactor(