# HG changeset patch
# User Antoine Pitrou
# Date 1230417170 -3600
diff -r c917cb926b0e -r bfa6ad04fbaa Include/pydebug.h
--- a/Include/pydebug.h
+++ b/Include/pydebug.h
@@ -18,6 +18,7 @@ PyAPI_DATA(int) Py_IgnoreEnvironmentFlag
PyAPI_DATA(int) Py_DivisionWarningFlag;
PyAPI_DATA(int) Py_DontWriteBytecodeFlag;
PyAPI_DATA(int) Py_NoUserSiteDirectory;
+PyAPI_DATA(int) Py_UnbufferedStdoutFlag;
/* this is a wrapper around getenv() that pays attention to
Py_IgnoreEnvironmentFlag. It should be used for getting variables like
diff -r c917cb926b0e -r bfa6ad04fbaa Lib/test/test_cmd_line.py
--- a/Lib/test/test_cmd_line.py
+++ b/Lib/test/test_cmd_line.py
@@ -142,6 +142,23 @@ class CmdLineTest(unittest.TestCase):
self.exit_code('-c', command),
0)
+ def test_unbuffered(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)
diff -r c917cb926b0e -r bfa6ad04fbaa Modules/main.c
--- a/Modules/main.c
+++ b/Modules/main.c
@@ -292,7 +292,6 @@ Py_Main(int argc, wchar_t **argv)
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 @@ Py_Main(int argc, wchar_t **argv)
break;
case 'u':
- unbuffered++;
+ Py_UnbufferedStdoutFlag++;
saw_unbuffered_flag = 1;
break;
@@ -423,7 +422,7 @@ Py_Main(int argc, wchar_t **argv)
Py_InspectFlag = 1;
if (!saw_unbuffered_flag &&
(p = Py_GETENV("PYTHONUNBUFFERED")) && *p != '\0')
- unbuffered = 1;
+ Py_UnbufferedStdoutFlag = 1;
if (!Py_NoUserSiteDirectory &&
(p = Py_GETENV("PYTHONNOUSERSITE")) && *p != '\0')
@@ -444,7 +443,7 @@ Py_Main(int argc, wchar_t **argv)
stdin_is_interactive = Py_FdIsInteractive(stdin, (char *)0);
- if (unbuffered) {
+ if (Py_UnbufferedStdoutFlag) {
#if defined(MS_WINDOWS) || defined(__CYGWIN__)
_setmode(fileno(stdin), O_BINARY);
_setmode(fileno(stdout), O_BINARY);
diff -r c917cb926b0e -r bfa6ad04fbaa Objects/fileobject.c
--- a/Objects/fileobject.c
+++ b/Objects/fileobject.c
@@ -29,17 +29,48 @@ PyObject *
PyFile_FromFd(int fd, char *name, char *mode, int buffering, char *encoding,
char *errors, char *newline, int closefd)
{
- PyObject *io, *stream, *nameobj = NULL;
+ PyObject *io = NULL, *stream = NULL, *nameobj = NULL, *modeobj = NULL;
io = PyImport_ImportModule("io");
if (io == NULL)
return NULL;
- stream = PyObject_CallMethod(io, "open", "isisssi", fd, mode,
- buffering, encoding, errors,
- newline, closefd);
+ if (!strchr(mode, 'b') && buffering == 0) {
+ /* As a convenience, when buffering == 0 on a text file, we
+ open the underlying binary stream in unbuffered mode and
+ wrap it with a text stream in line-buffered mode. */
+ PyObject *buf, *binmode;
+
+ modeobj = PyUnicode_FromString(mode);
+ if (modeobj == NULL)
+ goto onerror;
+ binmode = PyUnicode_FromFormat("%Ub", modeobj);
+ if (binmode == NULL)
+ goto onerror;
+
+ buf = PyObject_CallMethod(io, "open", "iOiOOOi",
+ fd, binmode, buffering,
+ Py_None, Py_None, Py_None, closefd);
+ Py_DECREF(binmode);
+ if (buf == NULL)
+ goto onerror;
+ stream = PyObject_CallMethod(io, "TextIOWrapper", "OsssO",
+ buf, encoding, errors,
+ newline, Py_True);
+ Py_DECREF(buf);
+ if (stream == NULL)
+ goto onerror;
+ if (PyObject_SetAttrString(stream, "mode", modeobj) < 0)
+ goto onerror;
+ Py_DECREF(modeobj);
+ }
+ else {
+ stream = PyObject_CallMethod(io, "open", "isisssi", fd, mode,
+ buffering, encoding, errors,
+ newline, closefd);
+ }
Py_DECREF(io);
if (stream == NULL)
- return NULL;
+ goto onerror;
if (name != NULL) {
nameobj = PyUnicode_FromString(name);
if (nameobj == NULL)
@@ -51,6 +82,13 @@ PyFile_FromFd(int fd, char *name, char *
}
}
return stream;
+
+onerror:
+ Py_XDECREF(io);
+ Py_XDECREF(stream);
+ Py_XDECREF(nameobj);
+ Py_XDECREF(modeobj);
+ return NULL;
}
PyObject *
diff -r c917cb926b0e -r bfa6ad04fbaa Python/pythonrun.c
--- a/Python/pythonrun.c
+++ b/Python/pythonrun.c
@@ -84,6 +84,7 @@ int Py_UseClassExceptionsFlag = 1; /* Ne
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_UnbufferedStdoutFlag = 0; /* Unbuffered binary std{out,err} */
/* PyModule_GetWarningsModule is no longer necessary as of 2.6
since _warnings is builtin. This API should not be used. */
@@ -810,8 +811,9 @@ initstdio(void)
#endif
}
else {
- if (!(std = PyFile_FromFd(fd, "", "w", -1, encoding,
- errors, "\n", 0))) {
+ if (!(std = PyFile_FromFd(fd, "", "w",
+ Py_UnbufferedStdoutFlag ? 0 : -1,
+ encoding, errors, "\n", 0))) {
goto error;
}
} /* if (fd < 0) */
@@ -831,8 +833,10 @@ initstdio(void)
#endif
}
else {
- if (!(std = PyFile_FromFd(fd, "", "w", -1, encoding,
- "backslashreplace", "\n", 0))) {
+ if (!(std = PyFile_FromFd(fd, "", "w",
+ Py_UnbufferedStdoutFlag ? 0 : -1,
+ encoding, "backslashreplace",
+ "\n", 0))) {
goto error;
}
} /* if (fd < 0) */