Repository: twisted/twisted

Issue:
PortableGIReactor is obsolete
In history, glib didn't support sockets on Windows so Twisted implemented PortableGIReactor which does socket handling with selectreactor and uses glib for glib stuff.

In 2016 or so, glib fixed this oversight so now you can use sockets as channels with glib on Windows.

Thus, PortableGIReactor is no longer needed and the regular GIReactor is fine.

Make PortableGIReactor an alias for GIReactor instead of its own separate weird thing.



Discussion:

Resolving patch:
diff --git a/src/twisted/internet/_glibbase.py b/src/twisted/internet/_glibbase.py
--- a/src/twisted/internet/_glibbase.py
+++ b/src/twisted/internet/_glibbase.py
@@ -17,7 +17,7 @@
 
 from zope.interface import implementer
 
-from twisted.internet import posixbase, selectreactor
+from twisted.internet import posixbase
 from twisted.internet.abstract import FileDescriptor
 from twisted.internet.interfaces import IReactorFDSet, IReadDescriptor, IWriteDescriptor
 from twisted.python import log
@@ -114,7 +114,22 @@ def __init__(self, glib_module: Any, gtk_module: Any, useGtk: bool = False) -> N
         self._writes: Set[IWriteDescriptor] = set()
         self._sources: Dict[FileDescriptor, int] = {}
         self._glib = glib_module
-        posixbase.PosixReactorBase.__init__(self)
+
+        self._POLL_DISCONNECTED = (
+            glib_module.IOCondition.HUP
+            | glib_module.IOCondition.ERR
+            | glib_module.IOCondition.NVAL
+        )
+        self._POLL_IN = glib_module.IOCondition.IN
+        self._POLL_OUT = glib_module.IOCondition.OUT
+
+        # glib's iochannel sources won't tell us about any events that we haven't
+        # asked for, even if those events aren't sensible inputs to the poll()
+        # call.
+        self.INFLAGS = self._POLL_IN | self._POLL_DISCONNECTED
+        self.OUTFLAGS = self._POLL_OUT | self._POLL_DISCONNECTED
+
+        super().__init__()
 
         self._source_remove = self._glib.source_remove
         self._timeout_add = self._glib.timeout_add
@@ -309,49 +324,3 @@ def _simulate(self):
         """
         self.runUntilCurrent()
         self._reschedule()
-
-
-class PortableGlibReactorBase(selectreactor.SelectReactor):
-    """
-    Base class for GObject event loop reactors that works on Windows.
-
-    Sockets aren't supported by GObject's input_add on Win32.
-    """
-
-    def __init__(self, glib_module: Any, gtk_module: Any, useGtk: bool = False) -> None:
-        self._simtag = None
-        self._glib = glib_module
-        selectreactor.SelectReactor.__init__(self)
-
-        self._source_remove = self._glib.source_remove
-        self._timeout_add = self._glib.timeout_add
-
-        self.loop = self._glib.MainLoop()
-        self._crash = _loopQuitter(self._glib.idle_add, self.loop.quit)
-        self._run = self.loop.run
-
-    def crash(self):
-        selectreactor.SelectReactor.crash(self)
-        self._crash()
-
-    def run(self, installSignalHandlers=True):
-        self.startRunning(installSignalHandlers=installSignalHandlers)
-        self._timeout_add(0, self.simulate)
-        if self._started:
-            self._run()
-
-    def simulate(self):
-        """
-        Run simulation loops and reschedule callbacks.
-        """
-        if self._simtag is not None:
-            self._source_remove(self._simtag)
-        self.iterate()
-        timeout = self.timeout()
-        if timeout is None or timeout > 0.01:
-            timeout = 0.01
-        self._simtag = self._timeout_add(
-            int(timeout * 1000),
-            self.simulate,
-            priority=self._glib.PRIORITY_DEFAULT_IDLE,
-        )
diff --git a/src/twisted/internet/gireactor.py b/src/twisted/internet/gireactor.py
--- a/src/twisted/internet/gireactor.py
+++ b/src/twisted/internet/gireactor.py
@@ -41,18 +41,6 @@ class GIReactor(_glibbase.GlibReactorBase):
         with C{registerGApplication}.
     """
 
-    _POLL_DISCONNECTED = (
-        GLib.IOCondition.HUP | GLib.IOCondition.ERR | GLib.IOCondition.NVAL
-    )
-    _POLL_IN = GLib.IOCondition.IN
-    _POLL_OUT = GLib.IOCondition.OUT
-
-    # glib's iochannel sources won't tell us about any events that we haven't
-    # asked for, even if those events aren't sensible inputs to the poll()
-    # call.
-    INFLAGS = _POLL_IN | _POLL_DISCONNECTED
-    OUTFLAGS = _POLL_OUT | _POLL_DISCONNECTED
-
     # By default no Application is registered:
     _gapplication = None
 
@@ -91,13 +79,13 @@ def run():
         self._crash = app.quit
 
 
-class PortableGIReactor(_glibbase.PortableGlibReactorBase):
+class PortableGIReactor(_glibbase.GlibReactorBase):
     """
     Portable GObject Introspection event loop reactor.
     """
 
     def __init__(self, useGtk=False):
-        _glibbase.PortableGlibReactorBase.__init__(self, GLib, None, useGtk=useGtk)
+        super().__init__(GLib, None, useGtk=useGtk)
 
     def registerGApplication(self, app):
         """
@@ -106,6 +94,11 @@ def registerGApplication(self, app):
         """
         raise NotImplementedError("GApplication is not currently supported on Windows.")
 
+    def simulate(self) -> None:
+        """
+        For compatibility only. Do nothing.
+        """
+
 
 def install(useGtk: bool = False) -> Union[GIReactor, PortableGIReactor]:
     """
@@ -114,6 +107,7 @@ def install(useGtk: bool = False) -> Union[GIReactor, PortableGIReactor]:
     @param useGtk: A hint that the Gtk GUI will or will not be used.  Currently
         does not modify any behavior.
     """
+    reactor: Union[GIReactor, PortableGIReactor]
     if runtime.platform.getType() == "posix":
         reactor = GIReactor(useGtk=useGtk)
     else:
diff --git a/src/twisted/internet/gtk2reactor.py b/src/twisted/internet/gtk2reactor.py
--- a/src/twisted/internet/gtk2reactor.py
+++ b/src/twisted/internet/gtk2reactor.py
@@ -71,16 +71,6 @@ class Gtk2Reactor(_glibbase.GlibReactorBase):
     PyGTK+ 2 event loop reactor.
     """
 
-    _POLL_DISCONNECTED = gobject.IO_HUP | gobject.IO_ERR | gobject.IO_NVAL
-    _POLL_IN = gobject.IO_IN
-    _POLL_OUT = gobject.IO_OUT
-
-    # glib's iochannel sources won't tell us about any events that we haven't
-    # asked for, even if those events aren't sensible inputs to the poll()
-    # call.
-    INFLAGS = _POLL_IN | _POLL_DISCONNECTED
-    OUTFLAGS = _POLL_OUT | _POLL_DISCONNECTED
-
     def __init__(self, useGtk=True):
         _gtk = None
         if useGtk is True:
@@ -89,19 +79,10 @@ def __init__(self, useGtk=True):
         _glibbase.GlibReactorBase.__init__(self, gobject, _gtk, useGtk=useGtk)
 
 
-class PortableGtkReactor(_glibbase.PortableGlibReactorBase):
-    """
-    Reactor that works on Windows.
-
-    Sockets aren't supported by GTK+'s input_add on Win32.
-    """
-
-    def __init__(self, useGtk=True):
-        _gtk = None
-        if useGtk is True:
-            import gtk as _gtk
-
-        _glibbase.PortableGlibReactorBase.__init__(self, gobject, _gtk, useGtk=useGtk)
+# We don't bother deprecating the PortableGtkReactor.
+# The original code was removed and replaced with the
+# backward compatible generic GTK reactor.
+PortableGtkReactor = Gtk2Reactor
 
 
 def install(useGtk=True):


Test patch: