]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.14] Make Android streams respect the unbuffered (`-u`) option (GH-138806) (#139108)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Mon, 29 Sep 2025 08:16:25 +0000 (10:16 +0200)
committerGitHub <noreply@github.com>
Mon, 29 Sep 2025 08:16:25 +0000 (11:16 +0300)
Co-authored-by: Malcolm Smith <smith@chaquo.com>
Lib/_android_support.py
Lib/test/test_android.py

index ae506f6a4b57b899600b97ac79a4f04a0f7297e2..a439d03a144dd22cdfa8a17e8b250fb51d3deb12 100644 (file)
@@ -29,15 +29,19 @@ def init_streams(android_log_write, stdout_prio, stderr_prio):
 
     global logcat
     logcat = Logcat(android_log_write)
-
-    sys.stdout = TextLogStream(
-        stdout_prio, "python.stdout", sys.stdout.fileno())
-    sys.stderr = TextLogStream(
-        stderr_prio, "python.stderr", sys.stderr.fileno())
+    sys.stdout = TextLogStream(stdout_prio, "python.stdout", sys.stdout)
+    sys.stderr = TextLogStream(stderr_prio, "python.stderr", sys.stderr)
 
 
 class TextLogStream(io.TextIOWrapper):
-    def __init__(self, prio, tag, fileno=None, **kwargs):
+    def __init__(self, prio, tag, original=None, **kwargs):
+        # Respect the -u option.
+        if original:
+            kwargs.setdefault("write_through", original.write_through)
+            fileno = original.fileno()
+        else:
+            fileno = None
+
         # The default is surrogateescape for stdout and backslashreplace for
         # stderr, but in the context of an Android log, readability is more
         # important than reversibility.
index de83ce081c2790f4d143ab02eb132e2c9dd20b98..c6c4a15a7ee34d9b546ff49d37a83716abbed6ba 100644 (file)
@@ -91,34 +91,38 @@ class TestAndroidOutput(unittest.TestCase):
         self.logcat_thread = None
 
     @contextmanager
-    def unbuffered(self, stream):
-        stream.reconfigure(write_through=True)
+    def reconfigure(self, stream, **settings):
+        original_settings = {key: getattr(stream, key, None) for key in settings.keys()}
+        stream.reconfigure(**settings)
         try:
             yield
         finally:
-            stream.reconfigure(write_through=False)
+            stream.reconfigure(**original_settings)
 
-    # In --verbose3 mode, sys.stdout and sys.stderr are captured, so we can't
-    # test them directly. Detect this mode and use some temporary streams with
-    # the same properties.
     def stream_context(self, stream_name, level):
-        # https://developer.android.com/ndk/reference/group/logging
-        prio = {"I": 4, "W": 5}[level]
-
         stack = ExitStack()
         stack.enter_context(self.subTest(stream_name))
+
+        # In --verbose3 mode, sys.stdout and sys.stderr are captured, so we can't
+        # test them directly. Detect this mode and use some temporary streams with
+        # the same properties.
         stream = getattr(sys, stream_name)
         native_stream = getattr(sys, f"__{stream_name}__")
         if isinstance(stream, io.StringIO):
+            # https://developer.android.com/ndk/reference/group/logging
+            prio = {"I": 4, "W": 5}[level]
             stack.enter_context(
                 patch(
                     f"sys.{stream_name}",
-                    TextLogStream(
-                        prio, f"python.{stream_name}", native_stream.fileno(),
-                        errors="backslashreplace"
+                    stream := TextLogStream(
+                        prio, f"python.{stream_name}", native_stream,
                     ),
                 )
             )
+
+        # The tests assume the stream is initially buffered.
+        stack.enter_context(self.reconfigure(stream, write_through=False))
+
         return stack
 
     def test_str(self):
@@ -145,7 +149,7 @@ class TestAndroidOutput(unittest.TestCase):
                     self.assert_logs(level, tag, lines)
 
                 # Single-line messages,
-                with self.unbuffered(stream):
+                with self.reconfigure(stream, write_through=True):
                     write("", [])
 
                     write("a")
@@ -192,7 +196,7 @@ class TestAndroidOutput(unittest.TestCase):
 
                 # However, buffering can be turned off completely if you want a
                 # flush after every write.
-                with self.unbuffered(stream):
+                with self.reconfigure(stream, write_through=True):
                     write("\nx", ["", "x"])
                     write("\na\n", ["", "a"])
                     write("\n", [""])