-
- Notifications
You must be signed in to change notification settings - Fork 33.4k
bpo-32373: Add socket.getblocking() method. #4926
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e411fc1 c737e29 3e65a90 8487f58 3a1898d 240af33 4fac6d3 File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| | @@ -98,6 +98,12 @@ def _have_socket_vsock(): | |
| ret = get_cid() is not None | ||
| return ret | ||
| | ||
| | ||
| def _is_fd_in_blocking_mode(sock): | ||
| return not bool( | ||
| fcntl.fcntl(sock, fcntl.F_GETFL, os.O_NONBLOCK) & os.O_NONBLOCK) | ||
| | ||
| | ||
| HAVE_SOCKET_CAN = _have_socket_can() | ||
| | ||
| HAVE_SOCKET_CAN_ISOTP = _have_socket_can_isotp() | ||
| | @@ -4123,8 +4129,44 @@ def testSetBlocking(self): | |
| # Testing whether set blocking works | ||
| self.serv.setblocking(True) | ||
| self.assertIsNone(self.serv.gettimeout()) | ||
| self.assertTrue(self.serv.getblocking()) | ||
| if fcntl: | ||
| self.assertTrue(_is_fd_in_blocking_mode(self.serv)) | ||
| | ||
| self.serv.setblocking(False) | ||
| self.assertEqual(self.serv.gettimeout(), 0.0) | ||
| self.assertFalse(self.serv.getblocking()) | ||
| if fcntl: | ||
| self.assertFalse(_is_fd_in_blocking_mode(self.serv)) | ||
| ||
| | ||
| self.serv.settimeout(None) | ||
| self.assertTrue(self.serv.getblocking()) | ||
| if fcntl: | ||
| self.assertTrue(_is_fd_in_blocking_mode(self.serv)) | ||
| | ||
| self.serv.settimeout(0) | ||
| self.assertFalse(self.serv.getblocking()) | ||
| self.assertEqual(self.serv.gettimeout(), 0) | ||
| if fcntl: | ||
| self.assertFalse(_is_fd_in_blocking_mode(self.serv)) | ||
| | ||
| self.serv.settimeout(10) | ||
| self.assertTrue(self.serv.getblocking()) | ||
| self.assertEqual(self.serv.gettimeout(), 10) | ||
| if fcntl: | ||
| # When a Python socket has a non-zero timeout, it's | ||
| # switched internally to a non-blocking mode. | ||
| # Later, sock.sendall(), sock.recv(), and other socket | ||
| # operations use a `select()` call and handle EWOULDBLOCK/EGAIN | ||
| # on all socket operations. That's how timeouts are | ||
| # enforced. | ||
| self.assertFalse(_is_fd_in_blocking_mode(self.serv)) | ||
| | ||
| self.serv.settimeout(0) | ||
| self.assertFalse(self.serv.getblocking()) | ||
| if fcntl: | ||
| self.assertFalse(_is_fd_in_blocking_mode(self.serv)) | ||
| | ||
| start = time.time() | ||
| try: | ||
| self.serv.accept() | ||
| | @@ -4157,6 +4199,8 @@ def testInitNonBlocking(self): | |
| self.serv.close() | ||
| self.serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM | | ||
| socket.SOCK_NONBLOCK) | ||
| self.assertFalse(self.serv.getblocking()) | ||
| self.assertEqual(self.serv.gettimeout(), 0) | ||
| self.port = support.bind_port(self.serv) | ||
| self.serv.listen() | ||
| # actual testing | ||
| | @@ -5234,11 +5278,24 @@ def checkNonblock(self, s, nonblock=True, timeout=0.0): | |
| self.assertEqual(s.gettimeout(), timeout) | ||
| self.assertTrue( | ||
| fcntl.fcntl(s, fcntl.F_GETFL, os.O_NONBLOCK) & os.O_NONBLOCK) | ||
| if timeout == 0: | ||
| # timeout == 0: means that getblocking() must be False. | ||
| self.assertFalse(s.getblocking()) | ||
| else: | ||
| # If timeout > 0, the socket will be in a "blocking" mode | ||
| # from the standpoint of the Python API. For Python socket | ||
| # object, "blocking" means that operations like 'sock.recv()' | ||
| # will block. Internally, file descriptors for | ||
| ||
| # "blocking" Python sockets *with timeouts* are in a | ||
| # *non-blocking* mode, and 'sock.recv()' uses 'select()' | ||
| # and handles EWOULDBLOCK/EAGAIN to enforce the timeout. | ||
| self.assertTrue(s.getblocking()) | ||
| else: | ||
| self.assertEqual(s.type, socket.SOCK_STREAM) | ||
| self.assertEqual(s.gettimeout(), None) | ||
| self.assertFalse( | ||
| fcntl.fcntl(s, fcntl.F_GETFL, os.O_NONBLOCK) & os.O_NONBLOCK) | ||
| self.assertTrue(s.getblocking()) | ||
| | ||
| @support.requires_linux_version(2, 6, 28) | ||
| def test_SOCK_NONBLOCK(self): | ||
| | @@ -5248,15 +5305,15 @@ def test_SOCK_NONBLOCK(self): | |
| socket.SOCK_STREAM | socket.SOCK_NONBLOCK) as s: | ||
| self.checkNonblock(s) | ||
| s.setblocking(1) | ||
| self.checkNonblock(s, False) | ||
| self.checkNonblock(s, nonblock=False) | ||
| s.setblocking(0) | ||
| self.checkNonblock(s) | ||
| s.settimeout(None) | ||
| self.checkNonblock(s, False) | ||
| self.checkNonblock(s, nonblock=False) | ||
| s.settimeout(2.0) | ||
| self.checkNonblock(s, timeout=2.0) | ||
| s.setblocking(1) | ||
| self.checkNonblock(s, False) | ||
| self.checkNonblock(s, nonblock=False) | ||
| # defaulttimeout | ||
| t = socket.getdefaulttimeout() | ||
| socket.setdefaulttimeout(0.0) | ||
| | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Add socket.getblocking() method. | ||
| ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| | @@ -136,6 +136,7 @@ sendall(data[, flags]) -- send all data\n\ | |
| send(data[, flags]) -- send data, may not send all of it\n\ | ||
| sendto(data[, flags], addr) -- send data to a given address\n\ | ||
| setblocking(0 | 1) -- set or clear the blocking I/O flag\n\ | ||
| getblocking() -- return True if socket is blocking, False if non-blocking\n\ | ||
| setsockopt(level, optname, value[, optlen]) -- set socket options\n\ | ||
| settimeout(None | float) -- set or clear the timeout\n\ | ||
| shutdown(how) -- shut down traffic in one or both directions\n\ | ||
| | @@ -2531,6 +2532,27 @@ Set the socket to blocking (flag is true) or non-blocking (false).\n\ | |
| setblocking(True) is equivalent to settimeout(None);\n\ | ||
| setblocking(False) is equivalent to settimeout(0.0)."); | ||
| | ||
| /* s.getblocking() method. | ||
| Returns True if socket is in blocking mode, | ||
| False if it is in non-blocking mode. | ||
| */ | ||
| static PyObject * | ||
| sock_getblocking(PySocketSockObject *s) | ||
| { | ||
| if (s->sock_timeout) { | ||
| Py_RETURN_TRUE; | ||
| } | ||
| else { | ||
| Py_RETURN_FALSE; | ||
| } | ||
| } | ||
| | ||
| PyDoc_STRVAR(getblocking_doc, | ||
| "getblocking()\n\ | ||
| \n\ | ||
| Returns True if socket is in blocking mode, or False if it\n\ | ||
| ||
| is in non-blocking mode."); | ||
| Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You may add "This is equivalent to checking | ||
| | ||
| static int | ||
| socket_parse_timeout(_PyTime_t *timeout, PyObject *timeout_obj) | ||
| { | ||
| | @@ -2587,7 +2609,30 @@ sock_settimeout(PySocketSockObject *s, PyObject *arg) | |
| return NULL; | ||
| | ||
| s->sock_timeout = timeout; | ||
| if (internal_setblocking(s, timeout < 0) == -1) { | ||
| | ||
| int block = timeout < 0; | ||
| /* Blocking mode for a Python socket object means that operations | ||
| ||
| like :meth:`recv` or :meth:`sendall` will block the execution of | ||
| the current thread until they are complete or aborted with a | ||
| `socket.timeout` or `socket.error` errors. When timeout is `None`, | ||
| the underlying FD is in a blocking mode. When timeout is a positive | ||
| number, the FD is in a non-blocking mode, and socket ops are | ||
| implemented with a `select()` call. | ||
| | ||
| When timeout is 0.0, the FD is in a non-blocking mode. | ||
| | ||
| This table summarizes all states in which the socket object and | ||
| its underlying FD can be: | ||
| | ||
| ==================== ===================== ============== | ||
| `gettimeout()` `getblocking()` FD | ||
| ==================== ===================== ============== | ||
| ``None`` ``True`` blocking | ||
| ``0.0`` ``False`` non-blocking | ||
| ``> 0`` ``True`` non-blocking | ||
| ||
| */ | ||
| | ||
| if (internal_setblocking(s, block) == -1) { | ||
| return NULL; | ||
| } | ||
| Py_RETURN_NONE; | ||
| | @@ -4607,6 +4652,8 @@ static PyMethodDef sock_methods[] = { | |
| sendto_doc}, | ||
| {"setblocking", (PyCFunction)sock_setblocking, METH_O, | ||
| setblocking_doc}, | ||
| {"getblocking", (PyCFunction)sock_getblocking, METH_NOARGS, | ||
| getblocking_doc}, | ||
| {"settimeout", (PyCFunction)sock_settimeout, METH_O, | ||
| settimeout_doc}, | ||
| {"gettimeout", (PyCFunction)sock_gettimeout, METH_NOARGS, | ||
| | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Victor: "getblocking() should be a simple as: return True if the timeout is equal to zero, False otherwise."
Yury: "That's exactly what is in the PR."
Hum, this doc doesn't mention the timeout but refers to a "blocking mode" which is not well defined for timeout > 0.
The socket module mentions 3 modes: blocking, non-blocking and "timeout mode":
https://docs.python.org/dev/library/socket.html#notes-on-socket-timeouts
Replace "if socket is in blocking mode" with "if the timeout is equal to zero", or replace "is in blocking mode" with "is in blocking or timeout mode"?
By the way, there is already a note: "At the operating system level, sockets in timeout mode are internally set in non-blocking mode."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the docstring here should say "This is equivalent to
gettimeout() == 0", just like the docstring forsetblockingexplains that it's equivalent to a certainsettimeoutcall.