Patchwork [3,of,6] socketserver: make it compatible with python 2

login
register
mail settings
Submitter Jun Wu
Date May 9, 2016, 12:06 a.m.
Message ID <0e68b512f4d9dad9c064.1462752382@x1c>
Download mbox | patch
Permalink /patch/14978/
State Changes Requested
Headers show

Comments

Jun Wu - May 9, 2016, 12:06 a.m.
# HG changeset patch
# User Jun Wu <quark@fb.com>
# Date 1462502252 -3600
#      Fri May 06 03:37:32 2016 +0100
# Node ID 0e68b512f4d9dad9c064342b0deebd45238f0cdf
# Parent  d94e32fbb8c35c2b536a74780f54d563a48e19c5
socketserver: make it compatible with python 2

The unmodified socketserver.py from Python 3 has issues with Python 2, namely
"import selectors" and a "print" difference.

This patch adds shim code for Python 2.6, 2.7 to makes it just work.
Augie Fackler - May 11, 2016, 2:01 a.m.
On Mon, May 09, 2016 at 01:06:22AM +0100, Jun Wu wrote:
> # HG changeset patch
> # User Jun Wu <quark@fb.com>
> # Date 1462502252 -3600
> #      Fri May 06 03:37:32 2016 +0100
> # Node ID 0e68b512f4d9dad9c064342b0deebd45238f0cdf
> # Parent  d94e32fbb8c35c2b536a74780f54d563a48e19c5
> socketserver: make it compatible with python 2
>
> The unmodified socketserver.py from Python 3 has issues with Python 2, namely
> "import selectors" and a "print" difference.

The print difference could be handled with a from __future__ import,
couldn't it? (I think it's print_function, but maybe I'm missing something.)

>
> This patch adds shim code for Python 2.6, 2.7 to makes it just work.
>
> diff --git a/mercurial/socketserver.py b/mercurial/socketserver.py
> --- a/mercurial/socketserver.py
> +++ b/mercurial/socketserver.py
> @@ -140,7 +140,11 @@
>      import dummy_threading as threading
>
>  import time as timemod
> -time = timemod.monotonic
> +if hasattr(timemod, 'monotonic'):
> +    time = timemod.monotonic
> +else:
> +    # Python 2 does not have monotonic
> +    time = timemod.time
>
>  __all__ = ["BaseServer", "TCPServer", "UDPServer", "ForkingUDPServer",
>             "ForkingTCPServer", "ThreadingUDPServer", "ThreadingTCPServer",
> @@ -161,8 +165,44 @@
>  try:
>      import selectors
>  except ImportError:
> -    # Python 2 does not have selectors
> -    pass
> +    # Python 2 does not have selectors. This is a minimal "selectors"
> +    # module just satisfying the needs of the "socketserver" module.
> +    import select
> +    class selectors(object):
> +        EVENT_READ = (1 << 0)
> +
> +        class SelectSelector(object):
> +            def __init__(self):
> +                self._fileobj = None
> +
> +            def register(self, fileobj, events, data=None):
> +                assert events == selectors.EVENT_READ
> +                assert data is None
> +                self._fileobj = fileobj
> +
> +            def select(self, timeout=None):
> +                if self._fileobj is None:
> +                    return False
> +                # copied from Python 2.7 SocketServer._eintr_retry
> +                while True:
> +                    try:
> +                        r, w, e = select.select([self._fileobj], [], [],
> +                                                timeout)
> +                        # the return value is incompatible with Python 3
> +                        # since we don't want to introduce heavy stuff like
> +                        # SelectorKey.
> +                        # it's good enough for all the use cases in this
> +                        # socketserver module.
> +                        return r
> +                    except (OSError, select.error) as e:
> +                        if e.args[0] != errno.EINTR:
> +                            raise
> +
> +            def __enter__(self):
> +                return self
> +
> +            def __exit__(self, *args):
> +                pass
>
>  # poll/select have the advantage of not requiring any extra file descriptor,
>  # contrarily to epoll/kqueue (also, they require a single syscall).
> @@ -388,8 +428,8 @@
>
>          """
>          print('-'*40)
> -        print('Exception happened during processing of request from', end=' ')
> -        print(client_address)
> +        print('Exception happened during processing of request from %s'
> +              % client_address)
>          import traceback
>          traceback.print_exc() # XXX But this goes to stderr!
>          print('-'*40)
> @@ -573,7 +613,10 @@
>              except ChildProcessError:
>                  # we don't have any children, we're done
>                  self.active_children.clear()
> -            except OSError:
> +            except OSError as e:
> +                # "ChildProcessError" for Python 2
> +                if e.errno == errno.ECHILD:
> +                    self.active_children.clear()
>                  break
>
>          # Now reap all defunct children.
> @@ -586,8 +629,10 @@
>              except ChildProcessError:
>                  # someone else reaped it
>                  self.active_children.discard(pid)
> -            except OSError:
> -                pass
> +            except OSError as e:
> +                # "ChildProcessError" for Python 2
> +                if e.errno == errno.ECHILD:
> +                    self.active_children.discard(pid)
>
>      def handle_timeout(self):
>          """Wait for zombies after self.timeout seconds of inactivity.
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@mercurial-scm.org
> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Jun Wu - May 11, 2016, 3:11 a.m.
On 05/11/2016 03:01 AM, Augie Fackler wrote:
> The print difference could be handled with a from __future__ import,
> couldn't it? (I think it's print_function, but maybe I'm missing something.)

It's print(..., end=x). You are right, print_function works.

Thanks for looking at these. I will send a V2 addressing all issues.

Patch

diff --git a/mercurial/socketserver.py b/mercurial/socketserver.py
--- a/mercurial/socketserver.py
+++ b/mercurial/socketserver.py
@@ -140,7 +140,11 @@ 
     import dummy_threading as threading
 
 import time as timemod
-time = timemod.monotonic
+if hasattr(timemod, 'monotonic'):
+    time = timemod.monotonic
+else:
+    # Python 2 does not have monotonic
+    time = timemod.time
 
 __all__ = ["BaseServer", "TCPServer", "UDPServer", "ForkingUDPServer",
            "ForkingTCPServer", "ThreadingUDPServer", "ThreadingTCPServer",
@@ -161,8 +165,44 @@ 
 try:
     import selectors
 except ImportError:
-    # Python 2 does not have selectors
-    pass
+    # Python 2 does not have selectors. This is a minimal "selectors"
+    # module just satisfying the needs of the "socketserver" module.
+    import select
+    class selectors(object):
+        EVENT_READ = (1 << 0)
+
+        class SelectSelector(object):
+            def __init__(self):
+                self._fileobj = None
+
+            def register(self, fileobj, events, data=None):
+                assert events == selectors.EVENT_READ
+                assert data is None
+                self._fileobj = fileobj
+
+            def select(self, timeout=None):
+                if self._fileobj is None:
+                    return False
+                # copied from Python 2.7 SocketServer._eintr_retry
+                while True:
+                    try:
+                        r, w, e = select.select([self._fileobj], [], [],
+                                                timeout)
+                        # the return value is incompatible with Python 3
+                        # since we don't want to introduce heavy stuff like
+                        # SelectorKey.
+                        # it's good enough for all the use cases in this
+                        # socketserver module.
+                        return r
+                    except (OSError, select.error) as e:
+                        if e.args[0] != errno.EINTR:
+                            raise
+
+            def __enter__(self):
+                return self
+
+            def __exit__(self, *args):
+                pass
 
 # poll/select have the advantage of not requiring any extra file descriptor,
 # contrarily to epoll/kqueue (also, they require a single syscall).
@@ -388,8 +428,8 @@ 
 
         """
         print('-'*40)
-        print('Exception happened during processing of request from', end=' ')
-        print(client_address)
+        print('Exception happened during processing of request from %s'
+              % client_address)
         import traceback
         traceback.print_exc() # XXX But this goes to stderr!
         print('-'*40)
@@ -573,7 +613,10 @@ 
             except ChildProcessError:
                 # we don't have any children, we're done
                 self.active_children.clear()
-            except OSError:
+            except OSError as e:
+                # "ChildProcessError" for Python 2
+                if e.errno == errno.ECHILD:
+                    self.active_children.clear()
                 break
 
         # Now reap all defunct children.
@@ -586,8 +629,10 @@ 
             except ChildProcessError:
                 # someone else reaped it
                 self.active_children.discard(pid)
-            except OSError:
-                pass
+            except OSError as e:
+                # "ChildProcessError" for Python 2
+                if e.errno == errno.ECHILD:
+                    self.active_children.discard(pid)
 
     def handle_timeout(self):
         """Wait for zombies after self.timeout seconds of inactivity.