Release all forwarded functions in .close. Fixes #1513223.
diff --git a/Lib/socket.py b/Lib/socket.py
index fa0e663..4e83f09 100644
--- a/Lib/socket.py
+++ b/Lib/socket.py
@@ -130,35 +130,37 @@
if sys.platform == "riscos":
_socketmethods = _socketmethods + ('sleeptaskw',)
+# All the method names that must be delegated to either the real socket
+# object or the _closedsocket object.
+_delegate_methods = ("recv", "recvfrom", "recv_into", "recvfrom_into",
+ "send", "sendto")
+
class _closedsocket(object):
__slots__ = []
def _dummy(*args):
raise error(EBADF, 'Bad file descriptor')
- send = recv = sendto = recvfrom = __getattr__ = _dummy
+ # All _delegate_methods must also be initialized here.
+ send = recv = recv_into = sendto = recvfrom = recvfrom_into = _dummy
+ __getattr__ = _dummy
class _socketobject(object):
__doc__ = _realsocket.__doc__
- __slots__ = ["_sock",
- "recv", "recv_into", "recvfrom_into",
- "send", "sendto", "recvfrom",
- "__weakref__"]
+ __slots__ = ["_sock", "__weakref__"] + list(_delegate_methods)
def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, _sock=None):
if _sock is None:
_sock = _realsocket(family, type, proto)
self._sock = _sock
- self.send = self._sock.send
- self.recv = self._sock.recv
- self.recv_into = self._sock.recv_into
- self.sendto = self._sock.sendto
- self.recvfrom = self._sock.recvfrom
- self.recvfrom_into = self._sock.recvfrom_into
+ for method in _delegate_methods:
+ setattr(self, method, getattr(_sock, method))
def close(self):
self._sock = _closedsocket()
- self.send = self.recv = self.sendto = self.recvfrom = self._sock._dummy
+ dummy = self._sock._dummy
+ for method in _delegate_methods:
+ setattr(self, method, dummy)
close.__doc__ = _realsocket.close.__doc__
def accept(self):
diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py
index e773eab..b7d3916 100644
--- a/Lib/test/test_socket.py
+++ b/Lib/test/test_socket.py
@@ -582,6 +582,21 @@
def _testRecvFrom(self):
self.cli.sendto(MSG, 0, (HOST, PORT))
+class TCPCloserTest(ThreadedTCPSocketTest):
+
+ def testClose(self):
+ conn, addr = self.serv.accept()
+ conn.close()
+
+ sd = self.cli
+ read, write, err = select.select([sd], [], [], 1.0)
+ self.assertEqual(read, [sd])
+ self.assertEqual(sd.recv(1), '')
+
+ def _testClose(self):
+ self.cli.connect((HOST, PORT))
+ time.sleep(1.0)
+
class BasicSocketPairTest(SocketPairTest):
def __init__(self, methodName='runTest'):
@@ -890,8 +905,8 @@
self.serv_conn.send(buf)
def test_main():
- tests = [GeneralModuleTests, BasicTCPTest, TCPTimeoutTest, TestExceptions,
- BufferIOTest]
+ tests = [GeneralModuleTests, BasicTCPTest, TCPCloserTest, TCPTimeoutTest,
+ TestExceptions, BufferIOTest]
if sys.platform != 'mac':
tests.extend([ BasicUDPTest, UDPTimeoutTest ])
diff --git a/Misc/NEWS b/Misc/NEWS
index a8fe1b2..0393423 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -19,6 +19,9 @@
Library
-------
+- Bug #1513223: .close() of a _socketobj now releases the underlying
+ socket again, which then gets closed as it becomes unreferenced.
+
- The '_ctypes' extension module now works when Python is configured
with the --without-threads option.