From d0e31b980f18101738d0ec518cb991a5fb73fe93 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sat, 27 Jan 2018 09:54:13 +0100 Subject: bpo-32454: socket closefd (#5048) Add close(fd) function to the socket module Signed-off-by: Christian Heimes --- Doc/library/socket.rst | 8 ++++++ Lib/test/test_socket.py | 16 +++++++++++ .../2017-12-30-10-38-05.bpo-32454.wsZnl-.rst | 1 + Modules/socketmodule.c | 31 ++++++++++++++++++++-- 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2017-12-30-10-38-05.bpo-32454.wsZnl-.rst diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index db032ca..7edd4ba 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -578,6 +578,14 @@ Other functions The :mod:`socket` module also offers various network-related services: +.. function:: close(fd) + + Close a socket file descriptor. This is like :func:`os.close`, but for + sockets. On some platforms (most noticeable Windows) :func:`os.close` + does not work for socket file descriptors. + + .. versionadded:: 3.7 + .. function:: getaddrinfo(host, port, family=0, type=0, proto=0, flags=0) Translate the *host*/*port* argument into a sequence of 5-tuples that contain diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 22d142c..275384c 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1519,6 +1519,22 @@ class GeneralModuleTests(unittest.TestCase): self.assertRaises(ValueError, fp.writable) self.assertRaises(ValueError, fp.seekable) + def test_socket_close(self): + sock = socket.socket() + try: + sock.bind((HOST, 0)) + socket.close(sock.fileno()) + with self.assertRaises(OSError): + sock.listen(1) + finally: + with self.assertRaises(OSError): + # sock.close() fails with EBADF + sock.close() + with self.assertRaises(TypeError): + socket.close(None) + with self.assertRaises(OSError): + socket.close(-1) + def test_makefile_mode(self): for mode in 'r', 'rb', 'rw', 'w', 'wb': with self.subTest(mode=mode): diff --git a/Misc/NEWS.d/next/Library/2017-12-30-10-38-05.bpo-32454.wsZnl-.rst b/Misc/NEWS.d/next/Library/2017-12-30-10-38-05.bpo-32454.wsZnl-.rst new file mode 100644 index 0000000..b8eaa85 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2017-12-30-10-38-05.bpo-32454.wsZnl-.rst @@ -0,0 +1 @@ +Add close(fd) function to the socket module. diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index d75a51a..5fe2431 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -2836,7 +2836,7 @@ sock_close(PySocketSockObject *s) Py_RETURN_NONE; } -PyDoc_STRVAR(close_doc, +PyDoc_STRVAR(sock_close_doc, "close()\n\ \n\ Close the socket. It cannot be used after this call."); @@ -4558,7 +4558,7 @@ static PyMethodDef sock_methods[] = { {"bind", (PyCFunction)sock_bind, METH_O, bind_doc}, {"close", (PyCFunction)sock_close, METH_NOARGS, - close_doc}, + sock_close_doc}, {"connect", (PyCFunction)sock_connect, METH_O, connect_doc}, {"connect_ex", (PyCFunction)sock_connect_ex, METH_O, @@ -5456,6 +5456,31 @@ PyDoc_STRVAR(getprotobyname_doc, \n\ Return the protocol number for the named protocol. (Rarely used.)"); +static PyObject * +socket_close(PyObject *self, PyObject *fdobj) +{ + SOCKET_T fd; + int res; + + fd = PyLong_AsSocket_t(fdobj); + if (fd == (SOCKET_T)(-1) && PyErr_Occurred()) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = SOCKETCLOSE(fd); + Py_END_ALLOW_THREADS + /* bpo-30319: The peer can already have closed the connection. + Python ignores ECONNRESET on close(). */ + if (res < 0 && !CHECK_ERRNO(ECONNRESET)) { + return set_error(); + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR(close_doc, +"close(integer) -> None\n\ +\n\ +Close an integer socket file descriptor. This is like os.close(), but for\n\ +sockets; on some platforms os.close() won't work for socket file descriptors."); #ifndef NO_DUP /* dup() function for socket fds */ @@ -6397,6 +6422,8 @@ static PyMethodDef socket_methods[] = { METH_VARARGS, getservbyport_doc}, {"getprotobyname", socket_getprotobyname, METH_VARARGS, getprotobyname_doc}, + {"close", socket_close, + METH_O, close_doc}, #ifndef NO_DUP {"dup", socket_dup, METH_O, dup_doc}, -- cgit v0.12