Index: Objects/fileobject.c =================================================================== --- Objects/fileobject.c (révision 84011) +++ Objects/fileobject.c (copie de travail) @@ -1735,8 +1735,10 @@ file_write(PyFileObject *f, PyObject *args) { Py_buffer pbuf; - char *s; + const char *s; Py_ssize_t n, n2; + PyObject *encoded = NULL; + if (f->f_fp == NULL) return err_closed(); if (!f->writable) @@ -1746,14 +1748,41 @@ return NULL; s = pbuf.buf; n = pbuf.len; - } else - if (!PyArg_ParseTuple(args, "t#", &s, &n)) - return NULL; + } + else { + const char *encoding, *errors; + PyObject *text; + if (!PyArg_ParseTuple(args, "O", &text)) + return NULL; + + if (PyString_Check(text)) { + s = PyString_AS_STRING(text); + n = PyString_GET_SIZE(text); + } else if (PyUnicode_Check(text)) { + if (f->f_encoding != Py_None) + encoding = PyString_AS_STRING(f->f_encoding); + else + encoding = PyUnicode_GetDefaultEncoding(); + if (f->f_errors != Py_None) + errors = PyString_AS_STRING(f->f_errors); + else + errors = "strict"; + encoded = PyUnicode_AsEncodedString(text, encoding, errors); + if (encoded == NULL) + return NULL; + s = PyString_AS_STRING(encoded); + n = PyString_GET_SIZE(encoded); + } else { + if (PyObject_AsCharBuffer(text, &s, &n)) + return NULL; + } + } f->f_softspace = 0; FILE_BEGIN_ALLOW_THREADS(f) errno = 0; n2 = fwrite(s, 1, n, f->f_fp); FILE_END_ALLOW_THREADS(f) + Py_XDECREF(encoded); if (f->f_binary) PyBuffer_Release(&pbuf); if (n2 != n) { Index: Misc/NEWS =================================================================== --- Misc/NEWS (révision 84011) +++ Misc/NEWS (copie de travail) @@ -23,6 +23,9 @@ - format(complex(-0.0, 2.0), '-') omitted the real part from the output, - format(complex(0.0, 2.0), '-') included a sign and parentheses. +- Issue #4947: The write() method of sys.stdout and sys.stderr uses the + encoding and errors attributes instead of using utf-8 in strict mode. + - Issue #7616: Fix copying of overlapping memoryview slices with the Intel compiler. Index: Lib/test/test_file2k.py =================================================================== --- Lib/test/test_file2k.py (révision 84011) +++ Lib/test/test_file2k.py (copie de travail) @@ -619,7 +619,39 @@ finally: sys.stdout = save_stdout + def test_unicode(self): + import subprocess + def get_message(encoding, *code): + code = '\n'.join(code) + env = os.environ.copy() + env['PYTHONIOENCODING'] = encoding + process = subprocess.Popen([sys.executable, "-c", code], + stdout=subprocess.PIPE, env=env) + stdout, stderr = process.communicate() + self.assertEqual(process.returncode, 0) + return stdout + + def check_message(text, encoding, expected): + stdout = get_message(encoding, + "import sys", + "sys.stdout.write(%r)" % text, + "sys.stdout.flush()") + self.assertEqual(stdout, expected) + + check_message(u'\u20ac\n', "iso-8859-15", "\xa4\n") + check_message(u'\u20ac\n', "utf-16-le", '\xac\x20\n\x00') + check_message(u'15\u20ac\n', "iso-8859-1:ignore", "15\n") + check_message(u'15\u20ac\n', "iso-8859-1:backslashreplace", + "15\\u20ac\n") + + for objtype in ('buffer', 'bytearray'): + stdout = get_message('ascii', + 'import sys', + r'sys.stdout.write(%s("\xe9\n"))' % objtype) + self.assertEqual(stdout, "\xe9\n") + + def test_main(): # Historically, these tests have been sloppy about removing TESTFN. # So get rid of it no matter what.