Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1db9e7b

Browse files
committed
Issue #22054: Add os.get_blocking() and os.set_blocking() functions to get and
set the blocking mode of a file descriptor (False if the O_NONBLOCK flag is set, True otherwise). These functions are not available on Windows.
1 parent 6aa4269 commit 1db9e7b

13 files changed

Lines changed: 198 additions & 58 deletions

File tree

Doc/library/os.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,17 @@ as internal buffering of data.
807807
Availability: Unix.
808808

809809

810+
.. function:: get_blocking(fd)
811+
812+
Get the blocking mode of the file descriptor: ``False`` if the
813+
:data:`O_NONBLOCK` flag is set, ``True`` if the flag is cleared.
814+
815+
See also :func:`set_blocking` and :meth:`socket.socket.setblocking`.
816+
817+
Availability: Unix.
818+
819+
.. versionadded:: 3.5
820+
810821
.. function:: isatty(fd)
811822

812823
Return ``True`` if the file descriptor *fd* is open and connected to a
@@ -1107,6 +1118,18 @@ or `the MSDN <http://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Window
11071118
.. versionadded:: 3.3
11081119

11091120

1121+
.. function:: set_blocking(fd, blocking)
1122+
1123+
Set the blocking mode of the specified file descriptor. Set the
1124+
:data:`O_NONBLOCK` flag if blocking is ``False``, clear the flag otherwise.
1125+
1126+
See also :func:`get_blocking` and :meth:`socket.socket.setblocking`.
1127+
1128+
Availability: Unix.
1129+
1130+
.. versionadded:: 3.5
1131+
1132+
11101133
.. data:: SF_NODISKIO
11111134
SF_MNOWAIT
11121135
SF_SYNC

Include/fileutils.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,14 @@ PyAPI_FUNC(int) _Py_set_inheritable(int fd, int inheritable,
7070
int *atomic_flag_works);
7171

7272
PyAPI_FUNC(int) _Py_dup(int fd);
73-
#endif
73+
74+
#ifndef MS_WINDOWS
75+
PyAPI_FUNC(int) _Py_get_blocking(int fd);
76+
77+
PyAPI_FUNC(int) _Py_set_blocking(int fd, int blocking);
78+
#endif /* !MS_WINDOWS */
79+
80+
#endif /* Py_LIMITED_API */
7481

7582
#ifdef __cplusplus
7683
}

Lib/asyncio/unix_events.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"""Selector event loop for Unix with signal handling."""
22

33
import errno
4-
import fcntl
54
import os
65
import signal
76
import socket
@@ -259,12 +258,6 @@ def create_unix_server(self, protocol_factory, path=None, *,
259258
return server
260259

261260

262-
def _set_nonblocking(fd):
263-
flags = fcntl.fcntl(fd, fcntl.F_GETFL)
264-
flags = flags | os.O_NONBLOCK
265-
fcntl.fcntl(fd, fcntl.F_SETFL, flags)
266-
267-
268261
class _UnixReadPipeTransport(transports.ReadTransport):
269262

270263
max_size = 256 * 1024 # max bytes we read in one event loop iteration
@@ -280,7 +273,7 @@ def __init__(self, loop, pipe, protocol, waiter=None, extra=None):
280273
stat.S_ISSOCK(mode) or
281274
stat.S_ISCHR(mode)):
282275
raise ValueError("Pipe transport is for pipes/sockets only.")
283-
_set_nonblocking(self._fileno)
276+
os.set_blocking(self._fileno, False)
284277
self._protocol = protocol
285278
self._closing = False
286279
self._loop.add_reader(self._fileno, self._read_ready)
@@ -373,7 +366,7 @@ def __init__(self, loop, pipe, protocol, waiter=None, extra=None):
373366
stat.S_ISCHR(mode)):
374367
raise ValueError("Pipe transport is only for "
375368
"pipes, sockets and character devices")
376-
_set_nonblocking(self._fileno)
369+
os.set_blocking(self._fileno, False)
377370
self._protocol = protocol
378371
self._buffer = []
379372
self._conn_lost = 0

Lib/asyncore.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -590,8 +590,6 @@ def close_all(map=None, ignore_all=False):
590590
# Regardless, this is useful for pipes, and stdin/stdout...
591591

592592
if os.name == 'posix':
593-
import fcntl
594-
595593
class file_wrapper:
596594
# Here we override just enough to make a file
597595
# look like a socket for the purposes of asyncore.
@@ -642,9 +640,7 @@ def __init__(self, fd, map=None):
642640
pass
643641
self.set_file(fd)
644642
# set it to non-blocking mode
645-
flags = fcntl.fcntl(fd, fcntl.F_GETFL, 0)
646-
flags = flags | os.O_NONBLOCK
647-
fcntl.fcntl(fd, fcntl.F_SETFL, flags)
643+
os.set_blocking(fd, False)
648644

649645
def set_file(self, fd):
650646
self.socket = file_wrapper(fd)

Lib/test/test_asyncio/test_unix_events.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -306,9 +306,9 @@ def setUp(self):
306306
self.pipe = mock.Mock(spec_set=io.RawIOBase)
307307
self.pipe.fileno.return_value = 5
308308

309-
fcntl_patcher = mock.patch('fcntl.fcntl')
310-
fcntl_patcher.start()
311-
self.addCleanup(fcntl_patcher.stop)
309+
blocking_patcher = mock.patch('os.set_blocking')
310+
blocking_patcher.start()
311+
self.addCleanup(blocking_patcher.stop)
312312

313313
fstat_patcher = mock.patch('os.fstat')
314314
m_fstat = fstat_patcher.start()
@@ -469,9 +469,9 @@ def setUp(self):
469469
self.pipe = mock.Mock(spec_set=io.RawIOBase)
470470
self.pipe.fileno.return_value = 5
471471

472-
fcntl_patcher = mock.patch('fcntl.fcntl')
473-
fcntl_patcher.start()
474-
self.addCleanup(fcntl_patcher.stop)
472+
blocking_patcher = mock.patch('os.set_blocking')
473+
blocking_patcher.start()
474+
self.addCleanup(blocking_patcher.stop)
475475

476476
fstat_patcher = mock.patch('os.fstat')
477477
m_fstat = fstat_patcher.start()

Lib/test/test_io.py

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,6 @@
4444
import threading
4545
except ImportError:
4646
threading = None
47-
try:
48-
import fcntl
49-
except ImportError:
50-
fcntl = None
5147

5248
def _default_chunk_size():
5349
"""Get the default TextIOWrapper chunk size"""
@@ -3230,26 +3226,20 @@ def test_pickling(self):
32303226
with self.open(support.TESTFN, **kwargs) as f:
32313227
self.assertRaises(TypeError, pickle.dumps, f, protocol)
32323228

3233-
@unittest.skipUnless(fcntl, 'fcntl required for this test')
32343229
def test_nonblock_pipe_write_bigbuf(self):
32353230
self._test_nonblock_pipe_write(16*1024)
32363231

3237-
@unittest.skipUnless(fcntl, 'fcntl required for this test')
32383232
def test_nonblock_pipe_write_smallbuf(self):
32393233
self._test_nonblock_pipe_write(1024)
32403234

3241-
def _set_non_blocking(self, fd):
3242-
flags = fcntl.fcntl(fd, fcntl.F_GETFL)
3243-
self.assertNotEqual(flags, -1)
3244-
res = fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)
3245-
self.assertEqual(res, 0)
3246-
3235+
@unittest.skipUnless(hasattr(os, 'set_blocking'),
3236+
'os.set_blocking() required for this test')
32473237
def _test_nonblock_pipe_write(self, bufsize):
32483238
sent = []
32493239
received = []
32503240
r, w = os.pipe()
3251-
self._set_non_blocking(r)
3252-
self._set_non_blocking(w)
3241+
os.set_blocking(r, False)
3242+
os.set_blocking(w, False)
32533243

32543244
# To exercise all code paths in the C implementation we need
32553245
# to play with buffer sizes. For instance, if we choose a

Lib/test/test_os.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1376,6 +1376,16 @@ def test_write(self):
13761376
def test_writev(self):
13771377
self.check(os.writev, [b'abc'])
13781378

1379+
def test_inheritable(self):
1380+
self.check(os.get_inheritable)
1381+
self.check(os.set_inheritable, True)
1382+
1383+
@unittest.skipUnless(hasattr(os, 'get_blocking'),
1384+
'needs os.get_blocking() and os.set_blocking()')
1385+
def test_blocking(self):
1386+
self.check(os.get_blocking)
1387+
self.check(os.set_blocking, True)
1388+
13791389

13801390
class LinkTests(unittest.TestCase):
13811391
def setUp(self):
@@ -2591,6 +2601,21 @@ def test_openpty(self):
25912601
self.assertEqual(os.get_inheritable(slave_fd), False)
25922602

25932603

2604+
@unittest.skipUnless(hasattr(os, 'get_blocking'),
2605+
'needs os.get_blocking() and os.set_blocking()')
2606+
class BlockingTests(unittest.TestCase):
2607+
def test_blocking(self):
2608+
fd = os.open(__file__, os.O_RDONLY)
2609+
self.addCleanup(os.close, fd)
2610+
self.assertEqual(os.get_blocking(fd), True)
2611+
2612+
os.set_blocking(fd, False)
2613+
self.assertEqual(os.get_blocking(fd), False)
2614+
2615+
os.set_blocking(fd, True)
2616+
self.assertEqual(os.get_blocking(fd), True)
2617+
2618+
25942619
@support.reap_threads
25952620
def test_main():
25962621
support.run_unittest(
@@ -2626,6 +2651,7 @@ def test_main():
26262651
CPUCountTests,
26272652
FDInheritanceTests,
26282653
Win32JunctionTests,
2654+
BlockingTests,
26292655
)
26302656

26312657
if __name__ == "__main__":

Lib/test/test_posix.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import sys
1010
import time
1111
import os
12-
import fcntl
1312
import platform
1413
import pwd
1514
import shutil
@@ -355,7 +354,7 @@ def test_dup2(self):
355354
def test_oscloexec(self):
356355
fd = os.open(support.TESTFN, os.O_RDONLY|os.O_CLOEXEC)
357356
self.addCleanup(os.close, fd)
358-
self.assertTrue(fcntl.fcntl(fd, fcntl.F_GETFD) & fcntl.FD_CLOEXEC)
357+
self.assertFalse(os.get_inheritable(fd))
359358

360359
@unittest.skipUnless(hasattr(posix, 'O_EXLOCK'),
361360
'test needs posix.O_EXLOCK')
@@ -605,8 +604,8 @@ def test_pipe2(self):
605604
self.addCleanup(os.close, w)
606605
self.assertFalse(os.get_inheritable(r))
607606
self.assertFalse(os.get_inheritable(w))
608-
self.assertTrue(fcntl.fcntl(r, fcntl.F_GETFL) & os.O_NONBLOCK)
609-
self.assertTrue(fcntl.fcntl(w, fcntl.F_GETFL) & os.O_NONBLOCK)
607+
self.assertFalse(os.get_blocking(r))
608+
self.assertFalse(os.get_blocking(w))
610609
# try reading from an empty pipe: this should fail, not block
611610
self.assertRaises(OSError, os.read, r, 1)
612611
# try a write big enough to fill-up the pipe: this should either

Lib/test/test_pty.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from test.support import verbose, run_unittest, import_module, reap_children
22

3-
#Skip these tests if either fcntl or termios is not available
4-
fcntl = import_module('fcntl')
3+
# Skip these tests if termios is not available
54
import_module('termios')
65

76
import errno
@@ -84,16 +83,18 @@ def test_basic(self):
8483
# in master_open(), we need to read the EOF.
8584

8685
# Ensure the fd is non-blocking in case there's nothing to read.
87-
orig_flags = fcntl.fcntl(master_fd, fcntl.F_GETFL)
88-
fcntl.fcntl(master_fd, fcntl.F_SETFL, orig_flags | os.O_NONBLOCK)
86+
blocking = os.get_blocking(master_fd)
8987
try:
90-
s1 = os.read(master_fd, 1024)
91-
self.assertEqual(b'', s1)
92-
except OSError as e:
93-
if e.errno != errno.EAGAIN:
94-
raise
95-
# Restore the original flags.
96-
fcntl.fcntl(master_fd, fcntl.F_SETFL, orig_flags)
88+
os.set_blocking(master_fd, False)
89+
try:
90+
s1 = os.read(master_fd, 1024)
91+
self.assertEqual(b'', s1)
92+
except OSError as e:
93+
if e.errno != errno.EAGAIN:
94+
raise
95+
finally:
96+
# Restore the original flags.
97+
os.set_blocking(master_fd, blocking)
9798

9899
debug("Writing to slave_fd")
99100
os.write(slave_fd, TEST_STRING_1)

Lib/test/test_signal.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,6 @@ def check_wakeup(self, test_body, *signals, ordered=True):
276276
# use a subprocess to have only one thread
277277
code = """if 1:
278278
import _testcapi
279-
import fcntl
280279
import os
281280
import signal
282281
import struct
@@ -299,10 +298,7 @@ def check_signum(signals):
299298
300299
signal.signal(signal.SIGALRM, handler)
301300
read, write = os.pipe()
302-
for fd in (read, write):
303-
flags = fcntl.fcntl(fd, fcntl.F_GETFL, 0)
304-
flags = flags | os.O_NONBLOCK
305-
fcntl.fcntl(fd, fcntl.F_SETFL, flags)
301+
os.set_blocking(write, False)
306302
signal.set_wakeup_fd(write)
307303
308304
test()
@@ -322,7 +318,6 @@ def test_wakeup_write_error(self):
322318
code = """if 1:
323319
import _testcapi
324320
import errno
325-
import fcntl
326321
import os
327322
import signal
328323
import sys
@@ -333,8 +328,7 @@ def handler(signum, frame):
333328
334329
signal.signal(signal.SIGALRM, handler)
335330
r, w = os.pipe()
336-
flags = fcntl.fcntl(r, fcntl.F_GETFL, 0)
337-
fcntl.fcntl(r, fcntl.F_SETFL, flags | os.O_NONBLOCK)
331+
os.set_blocking(r, False)
338332
339333
# Set wakeup_fd a read-only file descriptor to trigger the error
340334
signal.set_wakeup_fd(r)

0 commit comments

Comments
 (0)