Skip to content

Commit f11b460

Browse files
authored
bpo-32373: Add socket.getblocking() method. (#4926)
1 parent 631fd38 commit f11b460

File tree

4 files changed

+119
-4
lines changed

4 files changed

+119
-4
lines changed

Doc/library/socket.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,6 +1087,16 @@ to sockets.
10871087
to decode C structures encoded as byte strings).
10881088

10891089

1090+
.. method:: socket.getblocking()
1091+
1092+
Return ``True`` if socket is in blocking mode, ``False`` if in
1093+
non-blocking.
1094+
1095+
This is equivalent to checking ``socket.gettimeout() == 0``.
1096+
1097+
.. versionadded:: 3.7
1098+
1099+
10901100
.. method:: socket.gettimeout()
10911101

10921102
Return the timeout in seconds (float) associated with socket operations,

Lib/test/test_socket.py

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,12 @@ def _have_socket_vsock():
9898
ret = get_cid() is not None
9999
return ret
100100

101+
102+
def _is_fd_in_blocking_mode(sock):
103+
return not bool(
104+
fcntl.fcntl(sock, fcntl.F_GETFL, os.O_NONBLOCK) & os.O_NONBLOCK)
105+
106+
101107
HAVE_SOCKET_CAN = _have_socket_can()
102108

103109
HAVE_SOCKET_CAN_ISOTP = _have_socket_can_isotp()
@@ -4079,8 +4085,44 @@ def testSetBlocking(self):
40794085
# Testing whether set blocking works
40804086
self.serv.setblocking(True)
40814087
self.assertIsNone(self.serv.gettimeout())
4088+
self.assertTrue(self.serv.getblocking())
4089+
if fcntl:
4090+
self.assertTrue(_is_fd_in_blocking_mode(self.serv))
4091+
40824092
self.serv.setblocking(False)
40834093
self.assertEqual(self.serv.gettimeout(), 0.0)
4094+
self.assertFalse(self.serv.getblocking())
4095+
if fcntl:
4096+
self.assertFalse(_is_fd_in_blocking_mode(self.serv))
4097+
4098+
self.serv.settimeout(None)
4099+
self.assertTrue(self.serv.getblocking())
4100+
if fcntl:
4101+
self.assertTrue(_is_fd_in_blocking_mode(self.serv))
4102+
4103+
self.serv.settimeout(0)
4104+
self.assertFalse(self.serv.getblocking())
4105+
self.assertEqual(self.serv.gettimeout(), 0)
4106+
if fcntl:
4107+
self.assertFalse(_is_fd_in_blocking_mode(self.serv))
4108+
4109+
self.serv.settimeout(10)
4110+
self.assertTrue(self.serv.getblocking())
4111+
self.assertEqual(self.serv.gettimeout(), 10)
4112+
if fcntl:
4113+
# When a Python socket has a non-zero timeout, it's
4114+
# switched internally to a non-blocking mode.
4115+
# Later, sock.sendall(), sock.recv(), and other socket
4116+
# operations use a `select()` call and handle EWOULDBLOCK/EGAIN
4117+
# on all socket operations. That's how timeouts are
4118+
# enforced.
4119+
self.assertFalse(_is_fd_in_blocking_mode(self.serv))
4120+
4121+
self.serv.settimeout(0)
4122+
self.assertFalse(self.serv.getblocking())
4123+
if fcntl:
4124+
self.assertFalse(_is_fd_in_blocking_mode(self.serv))
4125+
40844126
start = time.time()
40854127
try:
40864128
self.serv.accept()
@@ -4113,6 +4155,8 @@ def testInitNonBlocking(self):
41134155
self.serv.close()
41144156
self.serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM |
41154157
socket.SOCK_NONBLOCK)
4158+
self.assertFalse(self.serv.getblocking())
4159+
self.assertEqual(self.serv.gettimeout(), 0)
41164160
self.port = support.bind_port(self.serv)
41174161
self.serv.listen()
41184162
# actual testing
@@ -5190,11 +5234,24 @@ def checkNonblock(self, s, nonblock=True, timeout=0.0):
51905234
self.assertEqual(s.gettimeout(), timeout)
51915235
self.assertTrue(
51925236
fcntl.fcntl(s, fcntl.F_GETFL, os.O_NONBLOCK) & os.O_NONBLOCK)
5237+
if timeout == 0:
5238+
# timeout == 0: means that getblocking() must be False.
5239+
self.assertFalse(s.getblocking())
5240+
else:
5241+
# If timeout > 0, the socket will be in a "blocking" mode
5242+
# from the standpoint of the Python API. For Python socket
5243+
# object, "blocking" means that operations like 'sock.recv()'
5244+
# will block. Internally, file descriptors for
5245+
# "blocking" Python sockets *with timeouts* are in a
5246+
# *non-blocking* mode, and 'sock.recv()' uses 'select()'
5247+
# and handles EWOULDBLOCK/EAGAIN to enforce the timeout.
5248+
self.assertTrue(s.getblocking())
51935249
else:
51945250
self.assertEqual(s.type, socket.SOCK_STREAM)
51955251
self.assertEqual(s.gettimeout(), None)
51965252
self.assertFalse(
51975253
fcntl.fcntl(s, fcntl.F_GETFL, os.O_NONBLOCK) & os.O_NONBLOCK)
5254+
self.assertTrue(s.getblocking())
51985255

51995256
@support.requires_linux_version(2, 6, 28)
52005257
def test_SOCK_NONBLOCK(self):
@@ -5204,15 +5261,15 @@ def test_SOCK_NONBLOCK(self):
52045261
socket.SOCK_STREAM | socket.SOCK_NONBLOCK) as s:
52055262
self.checkNonblock(s)
52065263
s.setblocking(1)
5207-
self.checkNonblock(s, False)
5264+
self.checkNonblock(s, nonblock=False)
52085265
s.setblocking(0)
52095266
self.checkNonblock(s)
52105267
s.settimeout(None)
5211-
self.checkNonblock(s, False)
5268+
self.checkNonblock(s, nonblock=False)
52125269
s.settimeout(2.0)
52135270
self.checkNonblock(s, timeout=2.0)
52145271
s.setblocking(1)
5215-
self.checkNonblock(s, False)
5272+
self.checkNonblock(s, nonblock=False)
52165273
# defaulttimeout
52175274
t = socket.getdefaulttimeout()
52185275
socket.setdefaulttimeout(0.0)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add socket.getblocking() method.

Modules/socketmodule.c

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ sendall(data[, flags]) -- send all data\n\
136136
send(data[, flags]) -- send data, may not send all of it\n\
137137
sendto(data[, flags], addr) -- send data to a given address\n\
138138
setblocking(0 | 1) -- set or clear the blocking I/O flag\n\
139+
getblocking() -- return True if socket is blocking, False if non-blocking\n\
139140
setsockopt(level, optname, value[, optlen]) -- set socket options\n\
140141
settimeout(None | float) -- set or clear the timeout\n\
141142
shutdown(how) -- shut down traffic in one or both directions\n\
@@ -2525,6 +2526,27 @@ Set the socket to blocking (flag is true) or non-blocking (false).\n\
25252526
setblocking(True) is equivalent to settimeout(None);\n\
25262527
setblocking(False) is equivalent to settimeout(0.0).");
25272528

2529+
/* s.getblocking() method.
2530+
Returns True if socket is in blocking mode,
2531+
False if it is in non-blocking mode.
2532+
*/
2533+
static PyObject *
2534+
sock_getblocking(PySocketSockObject *s)
2535+
{
2536+
if (s->sock_timeout) {
2537+
Py_RETURN_TRUE;
2538+
}
2539+
else {
2540+
Py_RETURN_FALSE;
2541+
}
2542+
}
2543+
2544+
PyDoc_STRVAR(getblocking_doc,
2545+
"getblocking()\n\
2546+
\n\
2547+
Returns True if socket is in blocking mode, or False if it\n\
2548+
is in non-blocking mode.");
2549+
25282550
static int
25292551
socket_parse_timeout(_PyTime_t *timeout, PyObject *timeout_obj)
25302552
{
@@ -2581,7 +2603,30 @@ sock_settimeout(PySocketSockObject *s, PyObject *arg)
25812603
return NULL;
25822604

25832605
s->sock_timeout = timeout;
2584-
if (internal_setblocking(s, timeout < 0) == -1) {
2606+
2607+
int block = timeout < 0;
2608+
/* Blocking mode for a Python socket object means that operations
2609+
like :meth:`recv` or :meth:`sendall` will block the execution of
2610+
the current thread until they are complete or aborted with a
2611+
`socket.timeout` or `socket.error` errors. When timeout is `None`,
2612+
the underlying FD is in a blocking mode. When timeout is a positive
2613+
number, the FD is in a non-blocking mode, and socket ops are
2614+
implemented with a `select()` call.
2615+
2616+
When timeout is 0.0, the FD is in a non-blocking mode.
2617+
2618+
This table summarizes all states in which the socket object and
2619+
its underlying FD can be:
2620+
2621+
==================== ===================== ==============
2622+
`gettimeout()` `getblocking()` FD
2623+
==================== ===================== ==============
2624+
``None`` ``True`` blocking
2625+
``0.0`` ``False`` non-blocking
2626+
``> 0`` ``True`` non-blocking
2627+
*/
2628+
2629+
if (internal_setblocking(s, block) == -1) {
25852630
return NULL;
25862631
}
25872632
Py_RETURN_NONE;
@@ -4601,6 +4646,8 @@ static PyMethodDef sock_methods[] = {
46014646
sendto_doc},
46024647
{"setblocking", (PyCFunction)sock_setblocking, METH_O,
46034648
setblocking_doc},
4649+
{"getblocking", (PyCFunction)sock_getblocking, METH_NOARGS,
4650+
getblocking_doc},
46044651
{"settimeout", (PyCFunction)sock_settimeout, METH_O,
46054652
settimeout_doc},
46064653
{"gettimeout", (PyCFunction)sock_gettimeout, METH_NOARGS,

0 commit comments

Comments
 (0)