https://github.com/python/cpython/commit/dbac03b411549c0f40fbb66127ae6f445027fcc8
commit: dbac03b411549c0f40fbb66127ae6f445027fcc8
branch: main
author: Vincent Michel <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-08-12T09:01:42Z
summary:

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 <[email protected]>

files:
A Misc/NEWS.d/next/Library/2021-09-09-10-15-54.gh-issue-75245.tLeTkn.rst
M Lib/socket.py
M Lib/test/test_socket.py

diff --git a/Lib/socket.py b/Lib/socket.py
index 2a4f875f76b069..21a852155abb3b 100644
--- a/Lib/socket.py
+++ b/Lib/socket.py
@@ -327,8 +327,18 @@ def makefile(self, mode="r", buffering=None, *,
             rawmode += "w"
         raw = SocketIO(self, rawmode)
         self._io_refs += 1
+        line_buffering = False
         if buffering is None:
             buffering = -1
+        if buffering == 1:
+            if binary:
+                import warnings
+                warnings.warn("line buffering (buffering=1) isn't supported "
+                              "in binary mode, the default buffer size will "
+                              "be used", RuntimeWarning, 2)
+            else:
+                line_buffering = True
+            buffering = -1
         if buffering < 0:
             buffering = io.DEFAULT_BUFFER_SIZE
         if buffering == 0:
@@ -345,7 +355,8 @@ def makefile(self, mode="r", buffering=None, *,
         if binary:
             return buffer
         encoding = io.text_encoding(encoding)
-        text = io.TextIOWrapper(buffer, encoding, errors, newline)
+        text = io.TextIOWrapper(
+            buffer, encoding, errors, newline, line_buffering)
         text.mode = mode
         return text
 
diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py
index 7bb50f7b8aa47e..e4b3d848923f8c 100644
--- a/Lib/test/test_socket.py
+++ b/Lib/test/test_socket.py
@@ -1969,6 +1969,25 @@ def test_makefile_mode(self):
                     with sock.makefile(mode, encoding=encoding) as fp:
                         self.assertEqual(fp.mode, mode)
 
+    def test_makefile_line_buffering(self):
+        with socket.socket() as sock:
+            for mode in 'r', 'w':
+                with self.subTest(mode=mode):
+                    with sock.makefile(mode, buffering=1,
+                                       encoding="utf-8") as fp:
+                        self.assertTrue(fp.line_buffering)
+
+    def test_makefile_line_buffering_binary(self):
+        # Line buffering is not supported in binary mode, as in open().
+        with socket.socket() as sock:
+            for mode in 'rb', 'wb':
+                with self.subTest(mode=mode):
+                    with self.assertWarnsRegex(
+                            RuntimeWarning,
+                            "line buffering .* isn't supported in binary "
+                            "mode"):
+                        sock.makefile(mode, buffering=1).close()
+
     def test_makefile_invalid_mode(self):
         for mode in 'rt', 'x', '+', 'a':
             with self.subTest(mode=mode):
@@ -5761,10 +5780,20 @@ def testReadline(self):
         # Performing file readline test
         line = self.read_file.readline()
         self.assertEqual(line, self.read_msg)
+        # Readline mode
+        if self.bufsize == 1 and self.read_mode == "r":
+            self.assertTrue(self.read_file.line_buffering)
 
     def _testReadline(self):
         self.write_file.write(self.write_msg)
-        self.write_file.flush()
+        # Readline mode: no need to flush
+        if self.bufsize == 1 and self.write_mode == "w":
+            self.assertTrue(self.write_file.line_buffering)
+        else:
+            self.write_file.flush()
+        # Prevent garbage collection from flushing
+        # until the server has finished
+        self.assertTrue(self.serv_finished.wait(5.0))
 
     def testCloseAfterMakefile(self):
         # The file returned by makefile should keep the socket open.
@@ -5922,11 +5951,6 @@ def _testWriteNonBlocking(self):
             self.serv_skipped = "failed to saturate the socket buffer"
 
 
-class LineBufferedFileObjectClassTestCase(FileObjectClassTestCase):
-
-    bufsize = 1 # Default-buffered for reading; line-buffered for writing
-
-
 class SmallBufferedFileObjectClassTestCase(FileObjectClassTestCase):
 
     bufsize = 2 # Exercise the buffering code
@@ -5962,6 +5986,16 @@ class 
UnicodeReadWriteFileObjectClassTestCase(FileObjectClassTestCase):
     newline = ''
 
 
+class UnicodeLineBufferedFileObjectClassTestCase(FileObjectClassTestCase):
+
+    bufsize = 1  # Default-buffered for reading; line-buffered for writing
+    read_mode = 'r'
+    read_msg = MSG.decode('utf-8')
+    write_mode = 'w'
+    write_msg = MSG.decode('utf-8')
+    newline = ''
+
+
 class NetworkConnectionTest(object):
     """Prove network connection."""
 
diff --git 
a/Misc/NEWS.d/next/Library/2021-09-09-10-15-54.gh-issue-75245.tLeTkn.rst 
b/Misc/NEWS.d/next/Library/2021-09-09-10-15-54.gh-issue-75245.tLeTkn.rst
new file mode 100644
index 00000000000000..e690f4e2c14e3d
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2021-09-09-10-15-54.gh-issue-75245.tLeTkn.rst
@@ -0,0 +1,5 @@
+:meth:`socket.socket.makefile` now supports line buffering (``buffering=1``)
+in text mode, as :func:`open` does and as it worked in Python 2.  Previously
+it silently used block buffering.  In binary mode it now emits
+a :exc:`RuntimeWarning` and uses the default buffer size, also as :func:`open`
+does.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to