# HG changeset patch
# Parent 05120447f2c627e7ad0d996a9ea16a2df3382f2e
Issue #1602: Windows console doesn't input or print Unicode
diff --git a/Include/pydebug.h b/Include/pydebug.h
--- a/Include/pydebug.h
+++ b/Include/pydebug.h
@@ -24,6 +24,10 @@
PyAPI_DATA(int) Py_HashRandomizationFlag;
PyAPI_DATA(int) Py_IsolatedFlag;
+#ifdef MS_WINDOWS
+PyAPI_DATA(int) Py_LegacyWindowsStdioFlag;
+#endif
+
/* this is a wrapper around getenv() that pays attention to
Py_IgnoreEnvironmentFlag. It should be used for getting variables like
PYTHONPATH and PYTHONHOME from the environment */
diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c
--- a/Modules/_io/_iomodule.c
+++ b/Modules/_io/_iomodule.c
@@ -20,6 +20,9 @@
#include
#endif /* HAVE_SYS_STAT_H */
+#ifdef MS_WINDOWS
+#include
+#endif
/* Various interned strings */
@@ -52,7 +55,6 @@
PyObject *_PyIO_empty_bytes;
PyObject *_PyIO_zero;
-
PyDoc_STRVAR(module_doc,
"The io module provides the Python interfaces to stream handling. The\n"
"builtin open function is defined in this module.\n"
@@ -362,8 +364,18 @@
}
/* Create the Raw file stream */
- raw = PyObject_CallFunction((PyObject *)&PyFileIO_Type,
- "OsiO", path_or_fd, rawmode, closefd, opener);
+ {
+ PyObject *RawIO_class = (PyObject *)&PyFileIO_Type;
+#ifdef MS_WINDOWS
+ if (!Py_LegacyWindowsStdioFlag && _PyIO_get_console_type(path_or_fd) != '\0') {
+ RawIO_class = (PyObject *)&PyWindowsConsoleIO_Type;
+ encoding = "utf-16-le";
+ }
+#endif
+ raw = PyObject_CallFunction(RawIO_class,
+ "OsiO", path_or_fd, rawmode, closefd, opener);
+ }
+
if (raw == NULL)
goto error;
result = raw;
@@ -425,7 +437,7 @@
{
PyObject *Buffered_class;
- if (updating)
+ if (updating)
Buffered_class = (PyObject *)&PyBufferedRandom_Type;
else if (creating || writing || appending)
Buffered_class = (PyObject *)&PyBufferedWriter_Type;
@@ -591,7 +603,6 @@
return mod;
}
-
static int
iomodule_traverse(PyObject *mod, visitproc visit, void *arg) {
_PyIO_State *state = IO_MOD_STATE(mod);
@@ -708,6 +719,12 @@
PyStringIO_Type.tp_base = &PyTextIOBase_Type;
ADD_TYPE(&PyStringIO_Type, "StringIO");
+#ifdef MS_WINDOWS
+ /* WindowsConsoleIO */
+ PyWindowsConsoleIO_Type.tp_base = &PyRawIOBase_Type;
+ ADD_TYPE(&PyWindowsConsoleIO_Type, "WindowsConsoleIO");
+#endif
+
/* BufferedReader */
PyBufferedReader_Type.tp_base = &PyBufferedIOBase_Type;
ADD_TYPE(&PyBufferedReader_Type, "BufferedReader");
diff --git a/Modules/_io/_iomodule.h b/Modules/_io/_iomodule.h
--- a/Modules/_io/_iomodule.h
+++ b/Modules/_io/_iomodule.h
@@ -19,6 +19,12 @@
extern PyTypeObject PyTextIOWrapper_Type;
extern PyTypeObject PyIncrementalNewlineDecoder_Type;
+#ifndef Py_LIMITED_API
+#ifdef MS_WINDOWS
+extern PyTypeObject PyWindowsConsoleIO_Type;
+#define PyWindowsConsoleIO_Check(op) (PyObject_TypeCheck((op), &PyWindowsConsoleIO_Type))
+#endif /* MS_WINDOWS */
+#endif /* Py_LIMITED_API */
extern int _PyIO_ConvertSsize_t(PyObject *, void *);
@@ -145,6 +151,10 @@
extern _PyIO_State *_PyIO_get_module_state(void);
extern PyObject *_PyIO_get_locale_module(_PyIO_State *);
+#ifdef MS_WINDOWS
+extern char _PyIO_get_console_type(PyObject *);
+#endif
+
extern PyObject *_PyIO_str_close;
extern PyObject *_PyIO_str_closed;
extern PyObject *_PyIO_str_decode;
diff --git a/Modules/_io/clinic/winconsoleio.c.h b/Modules/_io/clinic/winconsoleio.c.h
new file mode 100644
--- /dev/null
+++ b/Modules/_io/clinic/winconsoleio.c.h
@@ -0,0 +1,417 @@
+/*[clinic input]
+preserve
+[clinic start generated code]*/
+
+#if defined(MS_WINDOWS)
+
+PyDoc_STRVAR(_io_WindowsConsoleIO_close__doc__,
+"close($self, /)\n"
+"--\n"
+"\n"
+"Close the handle.\n"
+"\n"
+"A closed handle cannot be used for further I/O operations. close() may be\n"
+"called more than once without error.");
+
+#define _IO_WINDOWSCONSOLEIO_CLOSE_METHODDEF \
+ {"close", (PyCFunction)_io_WindowsConsoleIO_close, METH_NOARGS, _io_WindowsConsoleIO_close__doc__},
+
+static PyObject *
+_io_WindowsConsoleIO_close_impl(winconsoleio *self);
+
+static PyObject *
+_io_WindowsConsoleIO_close(winconsoleio *self, PyObject *Py_UNUSED(ignored))
+{
+ return _io_WindowsConsoleIO_close_impl(self);
+}
+
+#endif /* defined(MS_WINDOWS) */
+
+#if defined(MS_WINDOWS)
+
+PyDoc_STRVAR(_io_WindowsConsoleIO___init____doc__,
+"WindowsConsoleIO(file, mode=\'r\', closefd=True, opener=None)\n"
+"--\n"
+"\n"
+"Open a file.\n"
+"\n"
+"The mode can be \'r\' (default), or \'w\' reading or writing. All other mode\n"
+"characters will be ignored. The *opener* parameter is always ignored.");
+
+static int
+_io_WindowsConsoleIO___init___impl(winconsoleio *self, PyObject *nameobj,
+ const char *mode, int closefd,
+ PyObject *opener);
+
+static int
+_io_WindowsConsoleIO___init__(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ int return_value = -1;
+ static char *_keywords[] = {"file", "mode", "closefd", "opener", NULL};
+ PyObject *nameobj;
+ const char *mode = "r";
+ int closefd = 1;
+ PyObject *opener = Py_None;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|siO:WindowsConsoleIO", _keywords,
+ &nameobj, &mode, &closefd, &opener)) {
+ goto exit;
+ }
+ return_value = _io_WindowsConsoleIO___init___impl((winconsoleio *)self, nameobj, mode, closefd, opener);
+
+exit:
+ return return_value;
+}
+
+#endif /* defined(MS_WINDOWS) */
+
+#if defined(MS_WINDOWS)
+
+PyDoc_STRVAR(_io_WindowsConsoleIO_fileno__doc__,
+"fileno($self, /)\n"
+"--\n"
+"\n"
+"Return the underlying file descriptor (an integer).");
+
+#define _IO_WINDOWSCONSOLEIO_FILENO_METHODDEF \
+ {"fileno", (PyCFunction)_io_WindowsConsoleIO_fileno, METH_NOARGS, _io_WindowsConsoleIO_fileno__doc__},
+
+static PyObject *
+_io_WindowsConsoleIO_fileno_impl(winconsoleio *self);
+
+static PyObject *
+_io_WindowsConsoleIO_fileno(winconsoleio *self, PyObject *Py_UNUSED(ignored))
+{
+ return _io_WindowsConsoleIO_fileno_impl(self);
+}
+
+#endif /* defined(MS_WINDOWS) */
+
+#if defined(MS_WINDOWS)
+
+PyDoc_STRVAR(_io_WindowsConsoleIO_readable__doc__,
+"readable($self, /)\n"
+"--\n"
+"\n"
+"True if console is an input buffer.");
+
+#define _IO_WINDOWSCONSOLEIO_READABLE_METHODDEF \
+ {"readable", (PyCFunction)_io_WindowsConsoleIO_readable, METH_NOARGS, _io_WindowsConsoleIO_readable__doc__},
+
+static PyObject *
+_io_WindowsConsoleIO_readable_impl(winconsoleio *self);
+
+static PyObject *
+_io_WindowsConsoleIO_readable(winconsoleio *self, PyObject *Py_UNUSED(ignored))
+{
+ return _io_WindowsConsoleIO_readable_impl(self);
+}
+
+#endif /* defined(MS_WINDOWS) */
+
+#if defined(MS_WINDOWS)
+
+PyDoc_STRVAR(_io_WindowsConsoleIO_writable__doc__,
+"writable($self, /)\n"
+"--\n"
+"\n"
+"True if console is an output buffer.");
+
+#define _IO_WINDOWSCONSOLEIO_WRITABLE_METHODDEF \
+ {"writable", (PyCFunction)_io_WindowsConsoleIO_writable, METH_NOARGS, _io_WindowsConsoleIO_writable__doc__},
+
+static PyObject *
+_io_WindowsConsoleIO_writable_impl(winconsoleio *self);
+
+static PyObject *
+_io_WindowsConsoleIO_writable(winconsoleio *self, PyObject *Py_UNUSED(ignored))
+{
+ return _io_WindowsConsoleIO_writable_impl(self);
+}
+
+#endif /* defined(MS_WINDOWS) */
+
+#if defined(MS_WINDOWS)
+
+PyDoc_STRVAR(_io_WindowsConsoleIO_seekable__doc__,
+"seekable($self, /)\n"
+"--\n"
+"\n"
+"This is always false for consoles.");
+
+#define _IO_WINDOWSCONSOLEIO_SEEKABLE_METHODDEF \
+ {"seekable", (PyCFunction)_io_WindowsConsoleIO_seekable, METH_NOARGS, _io_WindowsConsoleIO_seekable__doc__},
+
+static PyObject *
+_io_WindowsConsoleIO_seekable_impl(winconsoleio *self);
+
+static PyObject *
+_io_WindowsConsoleIO_seekable(winconsoleio *self, PyObject *Py_UNUSED(ignored))
+{
+ return _io_WindowsConsoleIO_seekable_impl(self);
+}
+
+#endif /* defined(MS_WINDOWS) */
+
+#if defined(MS_WINDOWS)
+
+PyDoc_STRVAR(_io_WindowsConsoleIO_readinto__doc__,
+"readinto($self, buffer, /)\n"
+"--\n"
+"\n"
+"Same as RawIOBase.readinto().");
+
+#define _IO_WINDOWSCONSOLEIO_READINTO_METHODDEF \
+ {"readinto", (PyCFunction)_io_WindowsConsoleIO_readinto, METH_O, _io_WindowsConsoleIO_readinto__doc__},
+
+static PyObject *
+_io_WindowsConsoleIO_readinto_impl(winconsoleio *self, Py_buffer *buffer);
+
+static PyObject *
+_io_WindowsConsoleIO_readinto(winconsoleio *self, PyObject *arg)
+{
+ PyObject *return_value = NULL;
+ Py_buffer buffer = {NULL, NULL};
+
+ if (!PyArg_Parse(arg, "w*:readinto", &buffer)) {
+ goto exit;
+ }
+ return_value = _io_WindowsConsoleIO_readinto_impl(self, &buffer);
+
+exit:
+ /* Cleanup for buffer */
+ if (buffer.obj) {
+ PyBuffer_Release(&buffer);
+ }
+
+ return return_value;
+}
+
+#endif /* defined(MS_WINDOWS) */
+
+#if defined(MS_WINDOWS)
+
+PyDoc_STRVAR(_io_WindowsConsoleIO_readall__doc__,
+"readall($self, /)\n"
+"--\n"
+"\n"
+"Read all data from the file, returned as bytes.\n"
+"\n"
+"In non-blocking mode, returns as much as is immediately available,\n"
+"or None if no data is available. Return an empty bytes object at EOF.");
+
+#define _IO_WINDOWSCONSOLEIO_READALL_METHODDEF \
+ {"readall", (PyCFunction)_io_WindowsConsoleIO_readall, METH_NOARGS, _io_WindowsConsoleIO_readall__doc__},
+
+static PyObject *
+_io_WindowsConsoleIO_readall_impl(winconsoleio *self);
+
+static PyObject *
+_io_WindowsConsoleIO_readall(winconsoleio *self, PyObject *Py_UNUSED(ignored))
+{
+ return _io_WindowsConsoleIO_readall_impl(self);
+}
+
+#endif /* defined(MS_WINDOWS) */
+
+#if defined(MS_WINDOWS)
+
+PyDoc_STRVAR(_io_WindowsConsoleIO_read__doc__,
+"read($self, size=-1, /)\n"
+"--\n"
+"\n"
+"Read at most size bytes, returned as bytes.\n"
+"\n"
+"Only makes one system call, so less data may be returned than requested.\n"
+"In non-blocking mode, returns None if no data is available.\n"
+"Return an empty bytes object at EOF.");
+
+#define _IO_WINDOWSCONSOLEIO_READ_METHODDEF \
+ {"read", (PyCFunction)_io_WindowsConsoleIO_read, METH_VARARGS, _io_WindowsConsoleIO_read__doc__},
+
+static PyObject *
+_io_WindowsConsoleIO_read_impl(winconsoleio *self, Py_ssize_t size);
+
+static PyObject *
+_io_WindowsConsoleIO_read(winconsoleio *self, PyObject *args)
+{
+ PyObject *return_value = NULL;
+ Py_ssize_t size = -1;
+
+ if (!PyArg_ParseTuple(args, "|O&:read",
+ _PyIO_ConvertSsize_t, &size)) {
+ goto exit;
+ }
+ return_value = _io_WindowsConsoleIO_read_impl(self, size);
+
+exit:
+ return return_value;
+}
+
+#endif /* defined(MS_WINDOWS) */
+
+#if defined(MS_WINDOWS)
+
+PyDoc_STRVAR(_io_WindowsConsoleIO_write__doc__,
+"write($self, b, /)\n"
+"--\n"
+"\n"
+"Write buffer b to file, return number of bytes written.\n"
+"\n"
+"Only makes one system call, so not all of the data may be written.\n"
+"The number of bytes actually written is returned. In non-blocking mode,\n"
+"returns None if the write would block.");
+
+#define _IO_WINDOWSCONSOLEIO_WRITE_METHODDEF \
+ {"write", (PyCFunction)_io_WindowsConsoleIO_write, METH_O, _io_WindowsConsoleIO_write__doc__},
+
+static PyObject *
+_io_WindowsConsoleIO_write_impl(winconsoleio *self, Py_buffer *b);
+
+static PyObject *
+_io_WindowsConsoleIO_write(winconsoleio *self, PyObject *arg)
+{
+ PyObject *return_value = NULL;
+ Py_buffer b = {NULL, NULL};
+
+ if (!PyArg_Parse(arg, "y*:write", &b)) {
+ goto exit;
+ }
+ return_value = _io_WindowsConsoleIO_write_impl(self, &b);
+
+exit:
+ /* Cleanup for b */
+ if (b.obj) {
+ PyBuffer_Release(&b);
+ }
+
+ return return_value;
+}
+
+#endif /* defined(MS_WINDOWS) */
+
+#if defined(MS_WINDOWS)
+
+PyDoc_STRVAR(_io_WindowsConsoleIO_seek__doc__,
+"seek($self, pos, whence=0, /)\n"
+"--\n"
+"\n"
+"Raises IOError as console buffers are not seekable.");
+
+#define _IO_WINDOWSCONSOLEIO_SEEK_METHODDEF \
+ {"seek", (PyCFunction)_io_WindowsConsoleIO_seek, METH_VARARGS, _io_WindowsConsoleIO_seek__doc__},
+
+static PyObject *
+_io_WindowsConsoleIO_seek_impl(winconsoleio *self, PyObject *pos, int whence);
+
+static PyObject *
+_io_WindowsConsoleIO_seek(winconsoleio *self, PyObject *args)
+{
+ PyObject *return_value = NULL;
+ PyObject *pos;
+ int whence = 0;
+
+ if (!PyArg_ParseTuple(args, "O|i:seek",
+ &pos, &whence)) {
+ goto exit;
+ }
+ return_value = _io_WindowsConsoleIO_seek_impl(self, pos, whence);
+
+exit:
+ return return_value;
+}
+
+#endif /* defined(MS_WINDOWS) */
+
+#if defined(MS_WINDOWS)
+
+PyDoc_STRVAR(_io_WindowsConsoleIO_tell__doc__,
+"tell($self, /)\n"
+"--\n"
+"\n"
+"Raises OSError as console buffers are not seekable.");
+
+#define _IO_WINDOWSCONSOLEIO_TELL_METHODDEF \
+ {"tell", (PyCFunction)_io_WindowsConsoleIO_tell, METH_NOARGS, _io_WindowsConsoleIO_tell__doc__},
+
+static PyObject *
+_io_WindowsConsoleIO_tell_impl(winconsoleio *self);
+
+static PyObject *
+_io_WindowsConsoleIO_tell(winconsoleio *self, PyObject *Py_UNUSED(ignored))
+{
+ return _io_WindowsConsoleIO_tell_impl(self);
+}
+
+#endif /* defined(MS_WINDOWS) */
+
+#if defined(MS_WINDOWS)
+
+PyDoc_STRVAR(_io_WindowsConsoleIO_isatty__doc__,
+"isatty($self, /)\n"
+"--\n"
+"\n"
+"Always True.");
+
+#define _IO_WINDOWSCONSOLEIO_ISATTY_METHODDEF \
+ {"isatty", (PyCFunction)_io_WindowsConsoleIO_isatty, METH_NOARGS, _io_WindowsConsoleIO_isatty__doc__},
+
+static PyObject *
+_io_WindowsConsoleIO_isatty_impl(winconsoleio *self);
+
+static PyObject *
+_io_WindowsConsoleIO_isatty(winconsoleio *self, PyObject *Py_UNUSED(ignored))
+{
+ return _io_WindowsConsoleIO_isatty_impl(self);
+}
+
+#endif /* defined(MS_WINDOWS) */
+
+#ifndef _IO_WINDOWSCONSOLEIO_CLOSE_METHODDEF
+ #define _IO_WINDOWSCONSOLEIO_CLOSE_METHODDEF
+#endif /* !defined(_IO_WINDOWSCONSOLEIO_CLOSE_METHODDEF) */
+
+#ifndef _IO_WINDOWSCONSOLEIO_FILENO_METHODDEF
+ #define _IO_WINDOWSCONSOLEIO_FILENO_METHODDEF
+#endif /* !defined(_IO_WINDOWSCONSOLEIO_FILENO_METHODDEF) */
+
+#ifndef _IO_WINDOWSCONSOLEIO_READABLE_METHODDEF
+ #define _IO_WINDOWSCONSOLEIO_READABLE_METHODDEF
+#endif /* !defined(_IO_WINDOWSCONSOLEIO_READABLE_METHODDEF) */
+
+#ifndef _IO_WINDOWSCONSOLEIO_WRITABLE_METHODDEF
+ #define _IO_WINDOWSCONSOLEIO_WRITABLE_METHODDEF
+#endif /* !defined(_IO_WINDOWSCONSOLEIO_WRITABLE_METHODDEF) */
+
+#ifndef _IO_WINDOWSCONSOLEIO_SEEKABLE_METHODDEF
+ #define _IO_WINDOWSCONSOLEIO_SEEKABLE_METHODDEF
+#endif /* !defined(_IO_WINDOWSCONSOLEIO_SEEKABLE_METHODDEF) */
+
+#ifndef _IO_WINDOWSCONSOLEIO_READINTO_METHODDEF
+ #define _IO_WINDOWSCONSOLEIO_READINTO_METHODDEF
+#endif /* !defined(_IO_WINDOWSCONSOLEIO_READINTO_METHODDEF) */
+
+#ifndef _IO_WINDOWSCONSOLEIO_READALL_METHODDEF
+ #define _IO_WINDOWSCONSOLEIO_READALL_METHODDEF
+#endif /* !defined(_IO_WINDOWSCONSOLEIO_READALL_METHODDEF) */
+
+#ifndef _IO_WINDOWSCONSOLEIO_READ_METHODDEF
+ #define _IO_WINDOWSCONSOLEIO_READ_METHODDEF
+#endif /* !defined(_IO_WINDOWSCONSOLEIO_READ_METHODDEF) */
+
+#ifndef _IO_WINDOWSCONSOLEIO_WRITE_METHODDEF
+ #define _IO_WINDOWSCONSOLEIO_WRITE_METHODDEF
+#endif /* !defined(_IO_WINDOWSCONSOLEIO_WRITE_METHODDEF) */
+
+#ifndef _IO_WINDOWSCONSOLEIO_SEEK_METHODDEF
+ #define _IO_WINDOWSCONSOLEIO_SEEK_METHODDEF
+#endif /* !defined(_IO_WINDOWSCONSOLEIO_SEEK_METHODDEF) */
+
+#ifndef _IO_WINDOWSCONSOLEIO_TELL_METHODDEF
+ #define _IO_WINDOWSCONSOLEIO_TELL_METHODDEF
+#endif /* !defined(_IO_WINDOWSCONSOLEIO_TELL_METHODDEF) */
+
+#ifndef _IO_WINDOWSCONSOLEIO_ISATTY_METHODDEF
+ #define _IO_WINDOWSCONSOLEIO_ISATTY_METHODDEF
+#endif /* !defined(_IO_WINDOWSCONSOLEIO_ISATTY_METHODDEF) */
+/*[clinic end generated code: output=6330ceedd7d2e529 input=a9049054013a1b77]*/
diff --git a/Modules/_io/winconsoleio.c b/Modules/_io/winconsoleio.c
new file mode 100644
--- /dev/null
+++ b/Modules/_io/winconsoleio.c
@@ -0,0 +1,814 @@
+/*
+ An implementation of Windows console I/O
+
+ Classes defined here: WinConsoleReader, WinConsoleWriter
+
+ Written by Steve Dower
+*/
+
+#define PY_SSIZE_T_CLEAN
+#include "Python.h"
+
+#ifdef MS_WINDOWS
+
+#include "structmember.h"
+#ifdef HAVE_SYS_TYPES_H
+#include
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include
+#endif
+#include /* For offsetof */
+
+#define WIN32_LEAN_AND_MEAN
+#include
+
+#include "_iomodule.h"
+
+#if BUFSIZ < (8*1024)
+#define SMALLCHUNK (8*1024)
+#elif (BUFSIZ >= (2 << 25))
+#error "unreasonable BUFSIZ > 64MB defined"
+#else
+#define SMALLCHUNK BUFSIZ
+#endif
+
+char _get_console_type(HANDLE handle) {
+ DWORD mode, peek_count;
+
+ if (handle == INVALID_HANDLE_VALUE)
+ return '\0';
+
+ if (!GetConsoleMode(handle, &mode))
+ return '\0';
+
+ /* Peek at the handle to see whether it is an input or output handle */
+ if (PeekConsoleInputW(handle, NULL, 0, &peek_count))
+ return 'r';
+ return 'w';
+}
+
+char _PyIO_get_console_type(PyObject *path_or_fd) {
+ int fd;
+
+ fd = PyLong_AsLong(path_or_fd);
+ PyErr_Clear();
+ if (fd >= 0) {
+ return _get_console_type((HANDLE)_get_osfhandle(fd));
+ }
+
+ /* TODO: Support opening console using name */
+ return '\0';
+}
+
+/*[clinic input]
+module _io
+class _io.WindowsConsoleIO "winconsoleio *" "&PyWindowsConsoleIO_Type"
+[clinic start generated code]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=431722d0fd743878]*/
+
+/*[python input]
+class io_ssize_t_converter(CConverter):
+ type = 'Py_ssize_t'
+ converter = '_PyIO_ConvertSsize_t'
+[python start generated code]*/
+/*[python end generated code: output=da39a3ee5e6b4b0d input=d0a811d3cbfd1b33]*/
+
+typedef struct {
+ PyObject_HEAD
+ HANDLE handle;
+ unsigned int created : 1;
+ unsigned int readable : 1;
+ unsigned int writable : 1;
+ unsigned int closehandle : 1;
+ char finalizing;
+ unsigned int blksize;
+ PyObject *weakreflist;
+ PyObject *dict;
+} winconsoleio;
+
+PyTypeObject PyWindowsConsoleIO_Type;
+
+_Py_IDENTIFIER(name);
+
+int
+_PyWindowsConsoleIO_closed(PyObject *self)
+{
+ return ((winconsoleio *)self)->handle == INVALID_HANDLE_VALUE;
+}
+
+
+/* Returns 0 on success, -1 with exception set on failure. */
+static int
+internal_close(winconsoleio *self)
+{
+ if (self->handle != INVALID_HANDLE_VALUE) {
+ if (self->closehandle) {
+ CloseHandle(self->handle);
+ }
+ self->handle = INVALID_HANDLE_VALUE;
+ }
+ return 0;
+}
+
+/*[clinic input]
+_io.WindowsConsoleIO.close
+
+Close the handle.
+
+A closed handle cannot be used for further I/O operations. close() may be
+called more than once without error.
+[clinic start generated code]*/
+
+static PyObject *
+_io_WindowsConsoleIO_close_impl(winconsoleio *self)
+/*[clinic end generated code: output=222098bea11857e5 input=5cdeb454f05e5b9c]*/
+{
+ PyObject *res;
+ PyObject *exc, *val, *tb;
+ int rc;
+ _Py_IDENTIFIER(close);
+ res = _PyObject_CallMethodId((PyObject*)&PyRawIOBase_Type,
+ &PyId_close, "O", self);
+ if (!self->closehandle) {
+ self->handle = INVALID_HANDLE_VALUE;
+ return res;
+ }
+ if (res == NULL)
+ PyErr_Fetch(&exc, &val, &tb);
+ rc = internal_close(self);
+ if (res == NULL)
+ _PyErr_ChainExceptions(exc, val, tb);
+ if (rc < 0)
+ Py_CLEAR(res);
+ return res;
+}
+
+static PyObject *
+winconsoleio_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ winconsoleio *self;
+
+ assert(type != NULL && type->tp_alloc != NULL);
+
+ self = (winconsoleio *) type->tp_alloc(type, 0);
+ if (self != NULL) {
+ self->handle = INVALID_HANDLE_VALUE;
+ self->created = 0;
+ self->readable = 0;
+ self->writable = 0;
+ self->closehandle = 0;
+ self->blksize = 0;
+ self->weakreflist = NULL;
+ }
+
+ return (PyObject *) self;
+}
+
+/*[clinic input]
+_io.WindowsConsoleIO.__init__
+ file as nameobj: object
+ mode: str = "r"
+ closefd: int(c_default="1") = True
+ opener: object = None
+
+Open a file.
+
+The mode can be 'r' (default), or 'w' reading or writing. All other mode
+characters will be ignored. The *opener* parameter is always ignored.
+[clinic start generated code]*/
+
+static int
+_io_WindowsConsoleIO___init___impl(winconsoleio *self, PyObject *nameobj,
+ const char *mode, int closefd,
+ PyObject *opener)
+/*[clinic end generated code: output=d75a8c3247929b4e input=d446236e71cc17ee]*/
+{
+ const char *name = NULL;
+ PyObject *stringobj = NULL;
+ const char *s;
+ Py_UNICODE *widename = NULL;
+ int ret = 0;
+ int rwa = 0;
+ int fd = -1;
+ int fd_is_own = 0;
+
+ assert(PyWindowsConsoleIO_Check(self));
+ if (self->handle >= 0) {
+ if (self->closehandle) {
+ /* Have to close the existing file first. */
+ if (internal_close(self) < 0)
+ return -1;
+ }
+ else
+ self->handle = INVALID_HANDLE_VALUE;
+ }
+
+ if (PyFloat_Check(nameobj)) {
+ PyErr_SetString(PyExc_TypeError,
+ "integer argument expected, got float");
+ return -1;
+ }
+
+ fd = _PyLong_AsInt(nameobj);
+ if (fd < 0) {
+ if (!PyErr_Occurred()) {
+ PyErr_SetString(PyExc_ValueError,
+ "negative file descriptor");
+ return -1;
+ }
+ PyErr_Clear();
+ }
+
+ if (PyUnicode_Check(nameobj)) {
+ Py_ssize_t length;
+ widename = PyUnicode_AsUnicodeAndSize(nameobj, &length);
+ if (widename == NULL)
+ return -1;
+ if (wcslen(widename) != length) {
+ PyErr_SetString(PyExc_ValueError, "embedded null character");
+ return -1;
+ }
+ } else if (fd < 0) {
+ if (!PyUnicode_FSConverter(nameobj, &stringobj)) {
+ return -1;
+ }
+ name = PyBytes_AS_STRING(stringobj);
+ }
+
+ s = mode;
+ while (*s) {
+ switch (*s++) {
+ case '+':
+ case 'a':
+ case 'b':
+ case 'x':
+ break;
+ case 'r':
+ if (rwa)
+ goto bad_mode;
+ rwa = 1;
+ self->readable = 1;
+ break;
+ case 'w':
+ if (rwa)
+ goto bad_mode;
+ rwa = 1;
+ self->writable = 1;
+ break;
+ default:
+ PyErr_Format(PyExc_ValueError,
+ "invalid mode: %.200s", mode);
+ goto error;
+ }
+ }
+
+ if (!rwa)
+ goto bad_mode;
+
+ if (fd >= 0) {
+ self->handle = (HANDLE)_get_osfhandle(fd);
+ self->closehandle = 0;
+ }
+ else {
+ DWORD access = GENERIC_READ;
+
+ self->closehandle = 1;
+ if (!closefd) {
+ PyErr_SetString(PyExc_ValueError,
+ "Cannot use closefd=False with file name");
+ goto error;
+ }
+
+ if (self->writable)
+ access |= GENERIC_WRITE;
+
+ Py_BEGIN_ALLOW_THREADS
+ if (widename != NULL)
+ self->handle = CreateFileW(widename, access,
+ FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+ else
+ self->handle = CreateFileA(name, access,
+ FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+ Py_END_ALLOW_THREADS
+
+ if (self->handle == INVALID_HANDLE_VALUE) {
+ PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, GetLastError(), nameobj);
+ goto error;
+ }
+ }
+
+ if (self->writable && _get_console_type(self->handle) != 'w') {
+ PyErr_SetString(PyExc_ValueError,
+ "Cannot open console input buffer for writing");
+ goto error;
+ }
+ if (self->readable && _get_console_type(self->handle) != 'r') {
+ PyErr_SetString(PyExc_ValueError,
+ "Cannot open console output buffer for reading");
+ goto error;
+ }
+
+ self->blksize = DEFAULT_BUFFER_SIZE;
+
+ if (_PyObject_SetAttrId((PyObject *)self, &PyId_name, nameobj) < 0)
+ goto error;
+
+ goto done;
+
+bad_mode:
+ PyErr_SetString(PyExc_ValueError,
+ "Must have exactly one of read or write mode");
+error:
+ ret = -1;
+ internal_close(self);
+
+done:
+ Py_CLEAR(stringobj);
+ return ret;
+}
+
+static int
+winconsoleio_traverse(winconsoleio *self, visitproc visit, void *arg)
+{
+ Py_VISIT(self->dict);
+ return 0;
+}
+
+static int
+winconsoleio_clear(winconsoleio *self)
+{
+ Py_CLEAR(self->dict);
+ return 0;
+}
+
+static void
+winconsoleio_dealloc(winconsoleio *self)
+{
+ self->finalizing = 1;
+ if (_PyIOBase_finalize((PyObject *) self) < 0)
+ return;
+ _PyObject_GC_UNTRACK(self);
+ if (self->weakreflist != NULL)
+ PyObject_ClearWeakRefs((PyObject *) self);
+ Py_CLEAR(self->dict);
+ Py_TYPE(self)->tp_free((PyObject *)self);
+}
+
+static PyObject *
+err_closed(void)
+{
+ PyErr_SetString(PyExc_ValueError, "I/O operation on closed file");
+ return NULL;
+}
+
+static PyObject *
+err_mode(const char *action)
+{
+ _PyIO_State *state = IO_STATE();
+ if (state != NULL)
+ PyErr_Format(state->unsupported_operation,
+ "Console buffer does not support %s", action);
+ return NULL;
+}
+
+/*[clinic input]
+_io.WindowsConsoleIO.fileno
+
+Return the underlying file descriptor (an integer).
+[clinic start generated code]*/
+
+static PyObject *
+_io_WindowsConsoleIO_fileno_impl(winconsoleio *self)
+/*[clinic end generated code: output=8ce2675b8b2e70aa input=9d4498c1de6f6a18]*/
+{
+ err_mode("fileno");
+ return NULL;
+}
+
+/*[clinic input]
+_io.WindowsConsoleIO.readable
+
+True if console is an input buffer.
+[clinic start generated code]*/
+
+static PyObject *
+_io_WindowsConsoleIO_readable_impl(winconsoleio *self)
+/*[clinic end generated code: output=8cc7d865bea4671b input=9cffdf2ab99ca138]*/
+{
+ if (self->handle == INVALID_HANDLE_VALUE)
+ return err_closed();
+ return PyBool_FromLong((long) self->readable);
+}
+
+/*[clinic input]
+_io.WindowsConsoleIO.writable
+
+True if console is an output buffer.
+[clinic start generated code]*/
+
+static PyObject *
+_io_WindowsConsoleIO_writable_impl(winconsoleio *self)
+/*[clinic end generated code: output=480ef155e305ed74 input=940422ab3d91c8d2]*/
+{
+ if (self->handle == INVALID_HANDLE_VALUE)
+ return err_closed();
+ return PyBool_FromLong((long) self->writable);
+}
+
+/*[clinic input]
+_io.WindowsConsoleIO.seekable
+
+This is always false for consoles.
+[clinic start generated code]*/
+
+static PyObject *
+_io_WindowsConsoleIO_seekable_impl(winconsoleio *self)
+/*[clinic end generated code: output=3299ed704b401a1c input=8a8bb4a7727e9b67]*/
+{
+ Py_RETURN_FALSE;
+}
+
+/*[clinic input]
+_io.WindowsConsoleIO.readinto
+ buffer: Py_buffer(accept={rwbuffer})
+ /
+
+Same as RawIOBase.readinto().
+[clinic start generated code]*/
+
+static PyObject *
+_io_WindowsConsoleIO_readinto_impl(winconsoleio *self, Py_buffer *buffer)
+/*[clinic end generated code: output=ccf1ee4b17df834d input=8c5b9abb7c2dba64]*/
+{
+ DWORD n;
+
+ if (self->handle == INVALID_HANDLE_VALUE)
+ return err_closed();
+ if (!self->readable)
+ return err_mode("reading");
+
+ if (!ReadConsoleW(self->handle, buffer->buf, buffer->len / sizeof(wchar_t), &n, NULL)) {
+ /* copy error because PyBuffer_Release() can indirectly modify it */
+ return PyErr_SetFromWindowsErr(0);
+ }
+
+ /* TODO: Retry if not enough read? */
+
+ return PyLong_FromSsize_t(n * sizeof(wchar_t));
+}
+
+static size_t
+new_buffersize(winconsoleio *self, size_t currentsize)
+{
+ size_t addend;
+
+ /* Expand the buffer by an amount proportional to the current size,
+ giving us amortized linear-time behavior. For bigger sizes, use a
+ less-than-double growth factor to avoid excessive allocation. */
+ assert(currentsize <= PY_SSIZE_T_MAX);
+ if (currentsize > 65536)
+ addend = currentsize >> 3;
+ else
+ addend = 256 + currentsize;
+ if (addend < SMALLCHUNK)
+ /* Avoid tiny read() calls. */
+ addend = SMALLCHUNK;
+ return addend + currentsize;
+}
+
+/*[clinic input]
+_io.WindowsConsoleIO.readall
+
+Read all data from the file, returned as bytes.
+
+In non-blocking mode, returns as much as is immediately available,
+or None if no data is available. Return an empty bytes object at EOF.
+[clinic start generated code]*/
+
+static PyObject *
+_io_WindowsConsoleIO_readall_impl(winconsoleio *self)
+/*[clinic end generated code: output=524a2ffc0b46dc4e input=538b16912b563010]*/
+{
+ PyObject *result;
+ Py_ssize_t bytes_read = 0;
+ DWORD n;
+ size_t bufsize;
+
+ if (self->handle == INVALID_HANDLE_VALUE)
+ return err_closed();
+
+ bufsize = SMALLCHUNK;
+
+ result = PyBytes_FromStringAndSize(NULL, bufsize);
+ if (result == NULL)
+ return NULL;
+
+ while (1) {
+ if (bytes_read >= (Py_ssize_t)bufsize) {
+ bufsize = new_buffersize(self, bytes_read);
+ if (bufsize > PY_SSIZE_T_MAX || bufsize <= 0) {
+ PyErr_SetString(PyExc_OverflowError,
+ "unbounded read returned more bytes "
+ "than a Python bytes object can hold");
+ Py_DECREF(result);
+ return NULL;
+ }
+
+ if (PyBytes_GET_SIZE(result) < (Py_ssize_t)bufsize) {
+ if (_PyBytes_Resize(&result, bufsize) < 0)
+ return NULL;
+ }
+ }
+
+ if (!ReadConsoleW(self->handle, PyBytes_AS_STRING(result) + bytes_read,
+ (bufsize - bytes_read) / sizeof(wchar_t), &n, NULL)) {
+ int err = GetLastError();
+ Py_DECREF(result);
+ return PyErr_SetFromWindowsErr(err);
+ }
+
+ if (n == 0)
+ break;
+ bytes_read += n * sizeof(wchar_t);
+ }
+
+ if (PyBytes_GET_SIZE(result) > bytes_read) {
+ if (_PyBytes_Resize(&result, bytes_read) < 0)
+ return NULL;
+ }
+ return result;
+}
+
+/*[clinic input]
+_io.WindowsConsoleIO.read
+ size: io_ssize_t = -1
+ /
+
+Read at most size bytes, returned as bytes.
+
+Only makes one system call, so less data may be returned than requested.
+In non-blocking mode, returns None if no data is available.
+Return an empty bytes object at EOF.
+[clinic start generated code]*/
+
+static PyObject *
+_io_WindowsConsoleIO_read_impl(winconsoleio *self, Py_ssize_t size)
+/*[clinic end generated code: output=8300f301cef9e00c input=599776f55233f916]*/
+{
+ DWORD n;
+ PyObject *bytes;
+
+ if (self->handle == INVALID_HANDLE_VALUE)
+ return err_closed();
+ if (!self->readable)
+ return err_mode("reading");
+
+ if (size < 0)
+ return _io_WindowsConsoleIO_readall_impl(self);
+ if (size > ULONG_MAX)
+ size = ULONG_MAX;
+
+ bytes = PyBytes_FromStringAndSize(NULL, size);
+ if (bytes == NULL)
+ return NULL;
+ if (!ReadConsoleW(self->handle, PyBytes_AS_STRING(bytes),
+ size / sizeof(wchar_t), &n, NULL)) {
+ int err = GetLastError();
+ Py_DECREF(bytes);
+ return PyErr_SetFromWindowsErr(err);
+ }
+
+ if (n * sizeof(wchar_t) != size) {
+ if (_PyBytes_Resize(&bytes, n * sizeof(wchar_t)) < 0) {
+ Py_CLEAR(bytes);
+ return NULL;
+ }
+ }
+
+ return (PyObject *) bytes;
+}
+
+/*[clinic input]
+_io.WindowsConsoleIO.write
+ b: Py_buffer
+ /
+
+Write buffer b to file, return number of bytes written.
+
+Only makes one system call, so not all of the data may be written.
+The number of bytes actually written is returned. In non-blocking mode,
+returns None if the write would block.
+[clinic start generated code]*/
+
+static PyObject *
+_io_WindowsConsoleIO_write_impl(winconsoleio *self, Py_buffer *b)
+/*[clinic end generated code: output=931b5512f7615d6f input=47508023d8e45472]*/
+{
+ DWORD n;
+
+ if (self->handle == INVALID_HANDLE_VALUE)
+ return err_closed();
+ if (!self->writable)
+ return err_mode("writing");
+
+ if (!WriteConsoleW(self->handle, b->buf, b->len / sizeof(wchar_t),
+ &n, NULL)) {
+ return PyErr_SetFromWindowsErr(0);
+ }
+
+ return PyLong_FromSsize_t(n * sizeof(wchar_t));
+}
+
+/*[clinic input]
+_io.WindowsConsoleIO.seek
+ pos: object
+ whence: int = 0
+ /
+
+Raises IOError as console buffers are not seekable.
+[clinic start generated code]*/
+
+static PyObject *
+_io_WindowsConsoleIO_seek_impl(winconsoleio *self, PyObject *pos, int whence)
+/*[clinic end generated code: output=c20f4a858474be69 input=6718d8c6a563ca23]*/
+{
+ if (self->handle == INVALID_HANDLE_VALUE)
+ return err_closed();
+
+ return err_mode("seeking");
+}
+
+/*[clinic input]
+_io.WindowsConsoleIO.tell
+
+Raises OSError as console buffers are not seekable.
+[clinic start generated code]*/
+
+static PyObject *
+_io_WindowsConsoleIO_tell_impl(winconsoleio *self)
+/*[clinic end generated code: output=e45fd45edf85bacf input=3921089ae32406b6]*/
+{
+ if (self->handle == INVALID_HANDLE_VALUE)
+ return err_closed();
+
+ return err_mode("seeking");
+}
+
+static const char *
+mode_string(winconsoleio *self)
+{
+ if (self->readable)
+ return "rb";
+ else if (self->writable)
+ return "wb";
+ return NULL;
+}
+
+static PyObject *
+winconsoleio_repr(winconsoleio *self)
+{
+ if (self->handle == INVALID_HANDLE_VALUE)
+ return PyUnicode_FromFormat("<_io.WindowsConsoleIO [closed]>");
+
+ if (self->readable)
+ return PyUnicode_FromFormat("<_io.WindowsConsoleIO mode='rb' closefd=%s>",
+ self->closehandle ? "True" : "False");
+ if (self->writable)
+ return PyUnicode_FromFormat("<_io.WindowsConsoleIO mode='wb' closefd=%s>",
+ self->closehandle ? "True" : "False");
+
+ PyErr_SetString(PyExc_SystemError, "WindowsConsoleIO has invalid mode");
+ return NULL;
+}
+
+/*[clinic input]
+_io.WindowsConsoleIO.isatty
+
+Always True.
+[clinic start generated code]*/
+
+static PyObject *
+_io_WindowsConsoleIO_isatty_impl(winconsoleio *self)
+/*[clinic end generated code: output=ee9b776418c2b0dd input=97521b5fd13237ca]*/
+{
+ if (self->handle == INVALID_HANDLE_VALUE)
+ return err_closed();
+
+ Py_RETURN_TRUE;
+}
+
+static PyObject *
+winconsoleio_getstate(winconsoleio *self)
+{
+ PyErr_Format(PyExc_TypeError,
+ "cannot serialize '%s' object", Py_TYPE(self)->tp_name);
+ return NULL;
+}
+
+#include "clinic/winconsoleio.c.h"
+
+static PyMethodDef winconsoleio_methods[] = {
+ _IO_WINDOWSCONSOLEIO_READ_METHODDEF
+ _IO_WINDOWSCONSOLEIO_READALL_METHODDEF
+ _IO_WINDOWSCONSOLEIO_READINTO_METHODDEF
+ _IO_WINDOWSCONSOLEIO_WRITE_METHODDEF
+ _IO_WINDOWSCONSOLEIO_SEEK_METHODDEF
+ _IO_WINDOWSCONSOLEIO_TELL_METHODDEF
+ _IO_WINDOWSCONSOLEIO_CLOSE_METHODDEF
+ _IO_WINDOWSCONSOLEIO_SEEKABLE_METHODDEF
+ _IO_WINDOWSCONSOLEIO_READABLE_METHODDEF
+ _IO_WINDOWSCONSOLEIO_WRITABLE_METHODDEF
+ _IO_WINDOWSCONSOLEIO_FILENO_METHODDEF
+ _IO_WINDOWSCONSOLEIO_ISATTY_METHODDEF
+ {"__getstate__", (PyCFunction)winconsoleio_getstate, METH_NOARGS, NULL},
+ {NULL, NULL} /* sentinel */
+};
+
+/* 'closed' and 'mode' are attributes for backwards compatibility reasons. */
+
+static PyObject *
+get_closed(winconsoleio *self, void *closure)
+{
+ return PyBool_FromLong((long)(self->handle == INVALID_HANDLE_VALUE));
+}
+
+static PyObject *
+get_closefd(winconsoleio *self, void *closure)
+{
+ return PyBool_FromLong((long)(self->closehandle));
+}
+
+static PyObject *
+get_mode(winconsoleio *self, void *closure)
+{
+ return PyUnicode_FromString(self->readable ? "rb" : "wb");
+}
+
+static PyGetSetDef winconsoleio_getsetlist[] = {
+ {"closed", (getter)get_closed, NULL, "True if the file is closed"},
+ {"closefd", (getter)get_closefd, NULL,
+ "True if the file descriptor will be closed by close()."},
+ {"mode", (getter)get_mode, NULL, "String giving the file mode"},
+ {NULL},
+};
+
+static PyMemberDef winconsoleio_members[] = {
+ {"_blksize", T_UINT, offsetof(winconsoleio, blksize), 0},
+ {"_finalizing", T_BOOL, offsetof(winconsoleio, finalizing), 0},
+ {NULL}
+};
+
+PyTypeObject PyWindowsConsoleIO_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "_io.WindowsConsoleIO",
+ sizeof(winconsoleio),
+ 0,
+ (destructor)winconsoleio_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_reserved */
+ (reprfunc)winconsoleio_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ PyObject_GenericGetAttr, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
+ | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /* tp_flags */
+ _io_WindowsConsoleIO___init____doc__, /* tp_doc */
+ (traverseproc)winconsoleio_traverse, /* tp_traverse */
+ (inquiry)winconsoleio_clear, /* tp_clear */
+ 0, /* tp_richcompare */
+ offsetof(winconsoleio, weakreflist), /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ winconsoleio_methods, /* tp_methods */
+ winconsoleio_members, /* tp_members */
+ winconsoleio_getsetlist, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ offsetof(winconsoleio, dict), /* tp_dictoffset */
+ _io_WindowsConsoleIO___init__, /* tp_init */
+ PyType_GenericAlloc, /* tp_alloc */
+ winconsoleio_new, /* tp_new */
+ PyObject_GC_Del, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+ 0, /* tp_finalize */
+};
+
+#endif /* MS_WINDOWS */
diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj
--- a/PCbuild/pythoncore.vcxproj
+++ b/PCbuild/pythoncore.vcxproj
@@ -266,6 +266,7 @@
+
diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters
--- a/PCbuild/pythoncore.vcxproj.filters
+++ b/PCbuild/pythoncore.vcxproj.filters
@@ -593,6 +593,9 @@
Modules\_io
+
+ Modules\_io
+
Modules\_io
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -31,6 +31,9 @@
#ifdef MS_WINDOWS
#undef BYTE
#include "windows.h"
+
+extern PyTypeObject PyWindowsConsoleIO_Type;
+#define PyWindowsConsoleIO_Check(op) (PyObject_TypeCheck((op), &PyWindowsConsoleIO_Type))
#endif
_Py_IDENTIFIER(flush);
@@ -90,6 +93,9 @@
int Py_UnbufferedStdioFlag = 0; /* Unbuffered binary std{in,out,err} */
int Py_HashRandomizationFlag = 0; /* for -R and PYTHONHASHSEED */
int Py_IsolatedFlag = 0; /* for -I, isolate from user's env */
+#ifdef MS_WINDOWS
+int Py_LegacyWindowsStdioFlag = 0; /* Uses FileIO instead of WindowsConsoleIO */
+#endif
PyThreadState *_Py_Finalizing = NULL;
@@ -321,6 +327,10 @@
check its value further. */
if ((p = Py_GETENV("PYTHONHASHSEED")) && *p != '\0')
Py_HashRandomizationFlag = add_flag(Py_HashRandomizationFlag, p);
+#ifdef MS_WINDOWS
+ if ((p = Py_GETENV("PYTHONLEGACYWINDOWSSTDIO")) && *p != '\0')
+ Py_LegacyWindowsStdioFlag = add_flag(Py_LegacyWindowsStdioFlag, p);
+#endif
_PyRandom_Init();
@@ -1033,6 +1043,7 @@
_Py_IDENTIFIER(isatty);
_Py_IDENTIFIER(TextIOWrapper);
_Py_IDENTIFIER(mode);
+ _Py_IDENTIFIER(raw);
if (!is_valid_fd(fd))
Py_RETURN_NONE;
@@ -1058,7 +1069,6 @@
goto error;
if (buffering) {
- _Py_IDENTIFIER(raw);
raw = _PyObject_GetAttrId(buf, &PyId_raw);
if (raw == NULL)
goto error;
@@ -1068,6 +1078,12 @@
Py_INCREF(raw);
}
+#ifdef MS_WINDOWS
+ /* Windows console IO is always UTF-16-LE encoded */
+ if (PyWindowsConsoleIO_Check(raw))
+ encoding = "utf-16-le";
+#endif
+
text = PyUnicode_FromString(name);
if (text == NULL || _PyObject_SetAttrId(raw, &PyId_name, text) < 0)
goto error;