D5751: tests: change how sockets are closed

indygreg (Gregory Szorc) phabricator at mercurial-scm.org
Wed Jan 30 20:56:29 UTC 2019


indygreg created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  Python 3 uses a different type to represent a socket file object
  than Python 2. We need to conditionalize how the socket is closed
  accordingly.
  
  While we're here, we switch to use socket.shutdown() to close the
  socket. This is because socket.close() may not actually close
  the socket until it is GCd. socket.shutdown() forces an immediate
  shutdown.
  
  I suspect Python 3 changed semantic behavior here, as I can't get
  test-http-bad-server.t to work with socket.close(). socket.shutdown()
  does appear to work, however.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D5751

AFFECTED FILES
  tests/badserverext.py

CHANGE DETAILS

diff --git a/tests/badserverext.py b/tests/badserverext.py
--- a/tests/badserverext.py
+++ b/tests/badserverext.py
@@ -32,6 +32,7 @@
 from __future__ import absolute_import
 
 import socket
+import sys
 
 from mercurial import(
     registrar,
@@ -115,7 +116,7 @@
         object.__setattr__(self, '_closeaftersendbytes', closeaftersendbytes)
 
     def __getattribute__(self, name):
-        if name in ('read', 'readline', 'write', '_writelog'):
+        if name in ('_close', 'read', 'readline', 'write', '_writelog'):
             return object.__getattribute__(self, name)
 
         return getattr(object.__getattribute__(self, '_orig'), name)
@@ -133,6 +134,19 @@
         object.__getattribute__(self, '_logfp').write(b'\n')
         object.__getattribute__(self, '_logfp').flush()
 
+    def _close(self):
+        # Python 3 uses an io.BufferedIO instance. Python 2 uses some file
+        # object wrapper.
+        if sys.version_info[0] >= 3:
+            orig = object.__getattribute__(self, '_orig')
+
+            if hasattr(orig, 'raw'):
+                orig.raw._sock.shutdown(socket.SHUT_RDWR)
+            else:
+                self.close()
+        else:
+            self._sock.shutdown(socket.SHUT_RDWR)
+
     def read(self, size=-1):
         remaining = object.__getattribute__(self, '_closeafterrecvbytes')
 
@@ -161,7 +175,8 @@
 
         if remaining <= 0:
             self._writelog(b'read limit reached, closing socket')
-            self._sock.close()
+            self._close()
+
             # This is the easiest way to abort the current request.
             raise Exception('connection closed after receiving N bytes')
 
@@ -194,7 +209,8 @@
 
         if remaining <= 0:
             self._writelog(b'read limit reached; closing socket')
-            self._sock.close()
+            self._close()
+
             # This is the easiest way to abort the current request.
             raise Exception('connection closed after receiving N bytes')
 
@@ -225,7 +241,8 @@
 
         if remaining <= 0:
             self._writelog(b'write limit reached; closing socket')
-            self._sock.close()
+            self._close()
+
             raise Exception('connection closed after sending N bytes')
 
         return result



To: indygreg, #hg-reviewers
Cc: mercurial-devel


More information about the Mercurial-devel mailing list