[Python-checkins] r68451 - in python/branches/py3k: Include/pydebug.h Lib/test/test_cmd_line.py Misc/NEWS Modules/main.c Python/pythonrun.c

antoine.pitrou python-checkins at python.org
Fri Jan 9 19:53:14 CET 2009


Author: antoine.pitrou
Date: Fri Jan  9 19:53:14 2009
New Revision: 68451

Log:
Issue #4705: Fix the -u ("unbuffered binary stdout and stderr") command-line
flag to work properly. Furthermore, when specifying -u, the text stdout
and stderr streams have line-by-line buffering enabled (the default being
to buffer arbitrary chunks of data). Patch by Victor Stinner, test by me.



Modified:
   python/branches/py3k/Include/pydebug.h
   python/branches/py3k/Lib/test/test_cmd_line.py
   python/branches/py3k/Misc/NEWS
   python/branches/py3k/Modules/main.c
   python/branches/py3k/Python/pythonrun.c

Modified: python/branches/py3k/Include/pydebug.h
==============================================================================
--- python/branches/py3k/Include/pydebug.h	(original)
+++ python/branches/py3k/Include/pydebug.h	Fri Jan  9 19:53:14 2009
@@ -18,6 +18,7 @@
 PyAPI_DATA(int) Py_DivisionWarningFlag;
 PyAPI_DATA(int) Py_DontWriteBytecodeFlag;
 PyAPI_DATA(int) Py_NoUserSiteDirectory;
+PyAPI_DATA(int) Py_UnbufferedStdioFlag;
 
 /* this is a wrapper around getenv() that pays attention to
    Py_IgnoreEnvironmentFlag.  It should be used for getting variables like

Modified: python/branches/py3k/Lib/test/test_cmd_line.py
==============================================================================
--- python/branches/py3k/Lib/test/test_cmd_line.py	(original)
+++ python/branches/py3k/Lib/test/test_cmd_line.py	Fri Jan  9 19:53:14 2009
@@ -142,6 +142,23 @@
                 self.exit_code('-c', command),
                 0)
 
+    def test_unbuffered_output(self):
+        # Test expected operation of the '-u' switch
+        for stream in ('stdout', 'stderr'):
+            # Binary is unbuffered
+            code = ("import os, sys; sys.%s.buffer.write(b'x'); os._exit(0)"
+                % stream)
+            data, rc = self.start_python_and_exit_code('-u', '-c', code)
+            self.assertEqual(rc, 0)
+            self.assertEqual(data, b'x', "binary %s not unbuffered" % stream)
+            # Text is line-buffered
+            code = ("import os, sys; sys.%s.write('x\\n'); os._exit(0)"
+                % stream)
+            data, rc = self.start_python_and_exit_code('-u', '-c', code)
+            self.assertEqual(rc, 0)
+            self.assertEqual(data.strip(), b'x',
+                "text %s not line-buffered" % stream)
+
 
 def test_main():
     test.support.run_unittest(CmdLineTest)

Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS	(original)
+++ python/branches/py3k/Misc/NEWS	Fri Jan  9 19:53:14 2009
@@ -12,6 +12,11 @@
 Core and Builtins
 -----------------
 
+- Issue #4705: Fix the -u ("unbuffered binary stdout and stderr") command-line
+  flag to work properly. Furthermore, when specifying -u, the text stdout
+  and stderr streams have line-by-line buffering enabled (the default being
+  to buffer arbitrary chunks of data).
+
 - The internal table, _PyLong_DigitValue, is now an array of unsigned chars
   instead of ints (reducing its size from 4 to 8 times thereby reducing
   Python's overall memory).

Modified: python/branches/py3k/Modules/main.c
==============================================================================
--- python/branches/py3k/Modules/main.c	(original)
+++ python/branches/py3k/Modules/main.c	Fri Jan  9 19:53:14 2009
@@ -292,7 +292,6 @@
 	wchar_t *module = NULL;
 	FILE *fp = stdin;
 	char *p;
-	int unbuffered = 0;
 	int skipfirstline = 0;
 	int stdin_is_interactive = 0;
 	int help = 0;
@@ -374,7 +373,7 @@
 			break;
 
 		case 'u':
-			unbuffered++;
+			Py_UnbufferedStdioFlag = 1;
 			saw_unbuffered_flag = 1;
 			break;
 
@@ -423,7 +422,7 @@
 		Py_InspectFlag = 1;
 	if (!saw_unbuffered_flag &&
 	    (p = Py_GETENV("PYTHONUNBUFFERED")) && *p != '\0')
-		unbuffered = 1;
+		Py_UnbufferedStdioFlag = 1;
 
 	if (!Py_NoUserSiteDirectory &&
 	    (p = Py_GETENV("PYTHONNOUSERSITE")) && *p != '\0')
@@ -444,7 +443,7 @@
 
 	stdin_is_interactive = Py_FdIsInteractive(stdin, (char *)0);
 
-	if (unbuffered) {
+	if (Py_UnbufferedStdioFlag) {
 #if defined(MS_WINDOWS) || defined(__CYGWIN__)
 		_setmode(fileno(stdin), O_BINARY);
 		_setmode(fileno(stdout), O_BINARY);

Modified: python/branches/py3k/Python/pythonrun.c
==============================================================================
--- python/branches/py3k/Python/pythonrun.c	(original)
+++ python/branches/py3k/Python/pythonrun.c	Fri Jan  9 19:53:14 2009
@@ -88,6 +88,7 @@
 int Py_FrozenFlag; /* Needed by getpath.c */
 int Py_IgnoreEnvironmentFlag; /* e.g. PYTHONPATH, PYTHONHOME */
 int Py_NoUserSiteDirectory = 0; /* for -s and site.py */
+int Py_UnbufferedStdioFlag = 0; /* Unbuffered binary std{in,out,err} */
 
 /* PyModule_GetWarningsModule is no longer necessary as of 2.6
 since _warnings is builtin.  This API should not be used. */
@@ -728,6 +729,75 @@
 	}
 }
 
+static PyObject*
+create_stdio(PyObject* io,
+	int fd, int write_mode, char* name,
+	char* encoding, char* errors)
+{
+	PyObject *buf = NULL, *stream = NULL, *text = NULL, *raw = NULL;
+	const char* mode;
+	const PyObject *line_buffering;
+	int buffering;
+
+	if (Py_UnbufferedStdioFlag)
+		buffering = 0;
+	else
+		buffering = -1;
+	if (write_mode)
+		mode = "wb";
+	else
+		mode = "rb";
+	buf = PyObject_CallMethod(io, "open", "isiOOOi",
+				  fd, mode, buffering,
+				  Py_None, Py_None, Py_None, 0);
+	if (buf == NULL)
+		goto error;
+
+	if (!Py_UnbufferedStdioFlag) {
+		raw = PyObject_GetAttrString(buf, "raw");
+		if (raw == NULL)
+			goto error;
+	}
+	else {
+		raw = buf;
+		Py_INCREF(raw);
+	}
+
+	text = PyUnicode_FromString(name);
+	if (text == NULL || PyObject_SetAttrString(raw, "_name", text) < 0)
+		goto error;
+	Py_CLEAR(raw);
+	Py_CLEAR(text);
+
+	if (Py_UnbufferedStdioFlag)
+		line_buffering = Py_True;
+	else
+		line_buffering = Py_False;
+	stream = PyObject_CallMethod(io, "TextIOWrapper", "OsssO",
+				     buf, encoding, errors,
+				     "\n", line_buffering);
+	Py_CLEAR(buf);
+	if (stream == NULL)
+		goto error;
+
+	if (write_mode)
+		mode = "w";
+	else
+		mode = "r";
+	text = PyUnicode_FromString(mode);
+	if (!text || PyObject_SetAttrString(stream, "mode", text) < 0)
+		goto error;
+	Py_CLEAR(text);
+	return stream;
+
+error:
+	Py_XDECREF(buf);
+	Py_XDECREF(stream);
+	Py_XDECREF(text);
+	Py_XDECREF(raw);
+	return NULL;
+}
+
 /* Initialize sys.stdin, stdout, stderr and builtins.open */
 static int
 initstdio(void)
@@ -794,10 +864,9 @@
 #endif
 	}
 	else {
-		if (!(std = PyFile_FromFd(fd, "<stdin>", "r", -1, encoding, 
-					  errors, "\n", 0))) {
+		std = create_stdio(iomod, fd, 0, "<stdin>", encoding, errors);
+		if (std == NULL)
 			goto error;
-		}
 	} /* if (fd < 0) */
 	PySys_SetObject("__stdin__", std);
 	PySys_SetObject("stdin", std);
@@ -814,10 +883,9 @@
 #endif
 	}
 	else {
-		if (!(std = PyFile_FromFd(fd, "<stdout>", "w", -1, encoding, 
-					  errors, "\n", 0))) {
+		std = create_stdio(iomod, fd, 1, "<stdout>", encoding, errors);
+		if (std == NULL)
 			goto error;
-		}
 	} /* if (fd < 0) */
 	PySys_SetObject("__stdout__", std);
 	PySys_SetObject("stdout", std);
@@ -835,10 +903,9 @@
 #endif
 	}
 	else {
-		if (!(std = PyFile_FromFd(fd, "<stderr>", "w", -1, encoding,
-					  "backslashreplace", "\n", 0))) {
+		std = create_stdio(iomod, fd, 1, "<stderr>", encoding, "backslashreplace");
+		if (std == NULL)
 			goto error;
-		}
 	} /* if (fd < 0) */
 
 	/* Same as hack above, pre-import stderr's codec to avoid recursion


More information about the Python-checkins mailing list