# 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) */

AltStyle によって変換されたページ (->オリジナル) /