Skip to content

Commit dbac03b

Browse files
gh-75245: Support line buffering in socket.makefile() (GH-12370)
buffering=1 now enables line buffering in text mode, as in open() and as it worked in Python 2. In binary mode it emits a RuntimeWarning and uses the default buffer size, also as in open(). Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
1 parent e37cf49 commit dbac03b

3 files changed

Lines changed: 57 additions & 7 deletions

File tree

Lib/socket.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,8 +327,18 @@ def makefile(self, mode="r", buffering=None, *,
327327
rawmode += "w"
328328
raw = SocketIO(self, rawmode)
329329
self._io_refs += 1
330+
line_buffering = False
330331
if buffering is None:
331332
buffering = -1
333+
if buffering == 1:
334+
if binary:
335+
import warnings
336+
warnings.warn("line buffering (buffering=1) isn't supported "
337+
"in binary mode, the default buffer size will "
338+
"be used", RuntimeWarning, 2)
339+
else:
340+
line_buffering = True
341+
buffering = -1
332342
if buffering < 0:
333343
buffering = io.DEFAULT_BUFFER_SIZE
334344
if buffering == 0:
@@ -345,7 +355,8 @@ def makefile(self, mode="r", buffering=None, *,
345355
if binary:
346356
return buffer
347357
encoding = io.text_encoding(encoding)
348-
text = io.TextIOWrapper(buffer, encoding, errors, newline)
358+
text = io.TextIOWrapper(
359+
buffer, encoding, errors, newline, line_buffering)
349360
text.mode = mode
350361
return text
351362

Lib/test/test_socket.py

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1969,6 +1969,25 @@ def test_makefile_mode(self):
19691969
with sock.makefile(mode, encoding=encoding) as fp:
19701970
self.assertEqual(fp.mode, mode)
19711971

1972+
def test_makefile_line_buffering(self):
1973+
with socket.socket() as sock:
1974+
for mode in 'r', 'w':
1975+
with self.subTest(mode=mode):
1976+
with sock.makefile(mode, buffering=1,
1977+
encoding="utf-8") as fp:
1978+
self.assertTrue(fp.line_buffering)
1979+
1980+
def test_makefile_line_buffering_binary(self):
1981+
# Line buffering is not supported in binary mode, as in open().
1982+
with socket.socket() as sock:
1983+
for mode in 'rb', 'wb':
1984+
with self.subTest(mode=mode):
1985+
with self.assertWarnsRegex(
1986+
RuntimeWarning,
1987+
"line buffering .* isn't supported in binary "
1988+
"mode"):
1989+
sock.makefile(mode, buffering=1).close()
1990+
19721991
def test_makefile_invalid_mode(self):
19731992
for mode in 'rt', 'x', '+', 'a':
19741993
with self.subTest(mode=mode):
@@ -5761,10 +5780,20 @@ def testReadline(self):
57615780
# Performing file readline test
57625781
line = self.read_file.readline()
57635782
self.assertEqual(line, self.read_msg)
5783+
# Readline mode
5784+
if self.bufsize == 1 and self.read_mode == "r":
5785+
self.assertTrue(self.read_file.line_buffering)
57645786

57655787
def _testReadline(self):
57665788
self.write_file.write(self.write_msg)
5767-
self.write_file.flush()
5789+
# Readline mode: no need to flush
5790+
if self.bufsize == 1 and self.write_mode == "w":
5791+
self.assertTrue(self.write_file.line_buffering)
5792+
else:
5793+
self.write_file.flush()
5794+
# Prevent garbage collection from flushing
5795+
# until the server has finished
5796+
self.assertTrue(self.serv_finished.wait(5.0))
57685797

57695798
def testCloseAfterMakefile(self):
57705799
# The file returned by makefile should keep the socket open.
@@ -5922,11 +5951,6 @@ def _testWriteNonBlocking(self):
59225951
self.serv_skipped = "failed to saturate the socket buffer"
59235952

59245953

5925-
class LineBufferedFileObjectClassTestCase(FileObjectClassTestCase):
5926-
5927-
bufsize = 1 # Default-buffered for reading; line-buffered for writing
5928-
5929-
59305954
class SmallBufferedFileObjectClassTestCase(FileObjectClassTestCase):
59315955

59325956
bufsize = 2 # Exercise the buffering code
@@ -5962,6 +5986,16 @@ class UnicodeReadWriteFileObjectClassTestCase(FileObjectClassTestCase):
59625986
newline = ''
59635987

59645988

5989+
class UnicodeLineBufferedFileObjectClassTestCase(FileObjectClassTestCase):
5990+
5991+
bufsize = 1 # Default-buffered for reading; line-buffered for writing
5992+
read_mode = 'r'
5993+
read_msg = MSG.decode('utf-8')
5994+
write_mode = 'w'
5995+
write_msg = MSG.decode('utf-8')
5996+
newline = ''
5997+
5998+
59655999
class NetworkConnectionTest(object):
59666000
"""Prove network connection."""
59676001

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
:meth:`socket.socket.makefile` now supports line buffering (``buffering=1``)
2+
in text mode, as :func:`open` does and as it worked in Python 2. Previously
3+
it silently used block buffering. In binary mode it now emits
4+
a :exc:`RuntimeWarning` and uses the default buffer size, also as :func:`open`
5+
does.

0 commit comments

Comments
 (0)