blob: 460f2d3fa071a892d4bbaed2d0214fa59957e604 [file] [log] [blame]
Steve Dower39294992016-08-30 21:22:36 -07001/*
2 An implementation of Windows console I/O
3
4 Classes defined here: _WindowsConsoleIO
5
6 Written by Steve Dower
7*/
8
9#define PY_SSIZE_T_CLEAN
10#include "Python.h"
Victor Stinnerbcda8f12018-11-21 22:27:47 +010011#include "pycore_object.h"
Steve Dower39294992016-08-30 21:22:36 -070012
13#ifdef MS_WINDOWS
14
Victor Stinner4a21e572020-04-15 02:35:41 +020015#include "structmember.h" // PyMemberDef
Steve Dower39294992016-08-30 21:22:36 -070016#ifdef HAVE_SYS_TYPES_H
17#include <sys/types.h>
18#endif
19#ifdef HAVE_SYS_STAT_H
20#include <sys/stat.h>
21#endif
22#include <stddef.h> /* For offsetof */
23
24#define WIN32_LEAN_AND_MEAN
25#include <windows.h>
Steve Dower27f26ad2016-09-17 13:51:23 -070026#include <fcntl.h>
Steve Dower39294992016-08-30 21:22:36 -070027
28#include "_iomodule.h"
29
30/* BUFSIZ determines how many characters can be typed at the console
31 before it starts blocking. */
32#if BUFSIZ < (16*1024)
33#define SMALLCHUNK (2*1024)
34#elif (BUFSIZ >= (2 << 25))
Victor Stinner8c663fd2017-11-08 14:44:44 -080035#error "unreasonable BUFSIZ > 64 MiB defined"
Steve Dower39294992016-08-30 21:22:36 -070036#else
37#define SMALLCHUNK BUFSIZ
38#endif
39
40/* BUFMAX determines how many bytes can be read in one go. */
41#define BUFMAX (32*1024*1024)
42
Steve Dower312cef72016-10-03 09:04:58 -070043/* SMALLBUF determines how many utf-8 characters will be
44 buffered within the stream, in order to support reads
45 of less than one character */
46#define SMALLBUF 4
47
Steve Dower39294992016-08-30 21:22:36 -070048char _get_console_type(HANDLE handle) {
49 DWORD mode, peek_count;
50
51 if (handle == INVALID_HANDLE_VALUE)
52 return '\0';
Benjamin Petersone2e792d2016-09-19 22:17:16 -070053
Steve Dower39294992016-08-30 21:22:36 -070054 if (!GetConsoleMode(handle, &mode))
55 return '\0';
56
57 /* Peek at the handle to see whether it is an input or output handle */
58 if (GetNumberOfConsoleInputEvents(handle, &peek_count))
59 return 'r';
60 return 'w';
61}
62
63char _PyIO_get_console_type(PyObject *path_or_fd) {
Steve Dower722e3e22017-02-04 15:07:46 -080064 int fd = PyLong_AsLong(path_or_fd);
Steve Dower39294992016-08-30 21:22:36 -070065 PyErr_Clear();
66 if (fd >= 0) {
Segev Finer5e437fb2021-04-24 01:00:27 +030067 HANDLE handle = _Py_get_osfhandle_noraise(fd);
Steve Dower722e3e22017-02-04 15:07:46 -080068 if (handle == INVALID_HANDLE_VALUE)
Steve Dower39294992016-08-30 21:22:36 -070069 return '\0';
70 return _get_console_type(handle);
71 }
72
Steve Dower722e3e22017-02-04 15:07:46 -080073 PyObject *decoded;
74 wchar_t *decoded_wstr;
Steve Dower39294992016-08-30 21:22:36 -070075
Steve Dower722e3e22017-02-04 15:07:46 -080076 if (!PyUnicode_FSDecoder(path_or_fd, &decoded)) {
Steve Dower39294992016-08-30 21:22:36 -070077 PyErr_Clear();
Steve Dower27f26ad2016-09-17 13:51:23 -070078 return '\0';
79 }
Steve Dower722e3e22017-02-04 15:07:46 -080080 decoded_wstr = PyUnicode_AsWideCharString(decoded, NULL);
Steve Dower27f26ad2016-09-17 13:51:23 -070081 Py_CLEAR(decoded);
Steve Dower722e3e22017-02-04 15:07:46 -080082 if (!decoded_wstr) {
Steve Dower27f26ad2016-09-17 13:51:23 -070083 PyErr_Clear();
84 return '\0';
85 }
Steve Dower39294992016-08-30 21:22:36 -070086
Steve Dowera7e16482017-02-04 17:36:47 -080087 char m = '\0';
88 if (!_wcsicmp(decoded_wstr, L"CONIN$")) {
89 m = 'r';
90 } else if (!_wcsicmp(decoded_wstr, L"CONOUT$")) {
91 m = 'w';
92 } else if (!_wcsicmp(decoded_wstr, L"CON")) {
93 m = 'x';
94 }
95 if (m) {
96 PyMem_Free(decoded_wstr);
97 return m;
98 }
99
Steve Dower722e3e22017-02-04 15:07:46 -0800100 DWORD length;
101 wchar_t name_buf[MAX_PATH], *pname_buf = name_buf;
Victor Stinner29adc132017-06-08 18:19:25 +0200102
Steve Dower722e3e22017-02-04 15:07:46 -0800103 length = GetFullPathNameW(decoded_wstr, MAX_PATH, pname_buf, NULL);
104 if (length > MAX_PATH) {
105 pname_buf = PyMem_New(wchar_t, length);
106 if (pname_buf)
107 length = GetFullPathNameW(decoded_wstr, length, pname_buf, NULL);
108 else
109 length = 0;
110 }
111 PyMem_Free(decoded_wstr);
112
Steve Dower722e3e22017-02-04 15:07:46 -0800113 if (length) {
114 wchar_t *name = pname_buf;
115 if (length >= 4 && name[3] == L'\\' &&
116 (name[2] == L'.' || name[2] == L'?') &&
117 name[1] == L'\\' && name[0] == L'\\') {
118 name += 4;
119 }
120 if (!_wcsicmp(name, L"CONIN$")) {
121 m = 'r';
122 } else if (!_wcsicmp(name, L"CONOUT$")) {
123 m = 'w';
124 } else if (!_wcsicmp(name, L"CON")) {
125 m = 'x';
126 }
Steve Dower39294992016-08-30 21:22:36 -0700127 }
128
Steve Dower722e3e22017-02-04 15:07:46 -0800129 if (pname_buf != name_buf)
130 PyMem_Free(pname_buf);
Steve Dower39294992016-08-30 21:22:36 -0700131 return m;
132}
133
Steve Dower722e3e22017-02-04 15:07:46 -0800134
Steve Dower39294992016-08-30 21:22:36 -0700135/*[clinic input]
136module _io
137class _io._WindowsConsoleIO "winconsoleio *" "&PyWindowsConsoleIO_Type"
138[clinic start generated code]*/
139/*[clinic end generated code: output=da39a3ee5e6b4b0d input=e897fdc1fba4e131]*/
140
Steve Dower39294992016-08-30 21:22:36 -0700141typedef struct {
142 PyObject_HEAD
Steve Dower39294992016-08-30 21:22:36 -0700143 int fd;
144 unsigned int created : 1;
145 unsigned int readable : 1;
146 unsigned int writable : 1;
Segev Finer5e437fb2021-04-24 01:00:27 +0300147 unsigned int closefd : 1;
Steve Dower39294992016-08-30 21:22:36 -0700148 char finalizing;
149 unsigned int blksize;
150 PyObject *weakreflist;
151 PyObject *dict;
Steve Dower312cef72016-10-03 09:04:58 -0700152 char buf[SMALLBUF];
153 wchar_t wbuf;
Steve Dower39294992016-08-30 21:22:36 -0700154} winconsoleio;
155
156PyTypeObject PyWindowsConsoleIO_Type;
157
158_Py_IDENTIFIER(name);
159
160int
161_PyWindowsConsoleIO_closed(PyObject *self)
162{
Segev Finer5e437fb2021-04-24 01:00:27 +0300163 return ((winconsoleio *)self)->fd == -1;
Steve Dower39294992016-08-30 21:22:36 -0700164}
165
166
167/* Returns 0 on success, -1 with exception set on failure. */
168static int
169internal_close(winconsoleio *self)
170{
Segev Finer5e437fb2021-04-24 01:00:27 +0300171 if (self->fd != -1) {
172 if (self->closefd) {
173 _Py_BEGIN_SUPPRESS_IPH
174 close(self->fd);
175 _Py_END_SUPPRESS_IPH
Steve Dower39294992016-08-30 21:22:36 -0700176 }
Steve Dower39294992016-08-30 21:22:36 -0700177 self->fd = -1;
178 }
179 return 0;
180}
181
182/*[clinic input]
183_io._WindowsConsoleIO.close
184
Segev Finer5e437fb2021-04-24 01:00:27 +0300185Close the console object.
Steve Dower39294992016-08-30 21:22:36 -0700186
Segev Finer5e437fb2021-04-24 01:00:27 +0300187A closed console object cannot be used for further I/O operations.
188close() may be called more than once without error.
Steve Dower39294992016-08-30 21:22:36 -0700189[clinic start generated code]*/
190
191static PyObject *
192_io__WindowsConsoleIO_close_impl(winconsoleio *self)
Segev Finer5e437fb2021-04-24 01:00:27 +0300193/*[clinic end generated code: output=27ef95b66c29057b input=68c4e5754f8136c2]*/
Steve Dower39294992016-08-30 21:22:36 -0700194{
195 PyObject *res;
196 PyObject *exc, *val, *tb;
197 int rc;
198 _Py_IDENTIFIER(close);
Jeroen Demeyer59ad1102019-07-11 10:59:05 +0200199 res = _PyObject_CallMethodIdOneArg((PyObject*)&PyRawIOBase_Type,
Zackery Spytzd3952092019-07-19 00:07:06 -0600200 &PyId_close, (PyObject*)self);
Segev Finer5e437fb2021-04-24 01:00:27 +0300201 if (!self->closefd) {
202 self->fd = -1;
Steve Dower39294992016-08-30 21:22:36 -0700203 return res;
204 }
205 if (res == NULL)
206 PyErr_Fetch(&exc, &val, &tb);
207 rc = internal_close(self);
208 if (res == NULL)
209 _PyErr_ChainExceptions(exc, val, tb);
210 if (rc < 0)
211 Py_CLEAR(res);
212 return res;
213}
214
215static PyObject *
216winconsoleio_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
217{
218 winconsoleio *self;
219
220 assert(type != NULL && type->tp_alloc != NULL);
221
222 self = (winconsoleio *) type->tp_alloc(type, 0);
223 if (self != NULL) {
Steve Dower39294992016-08-30 21:22:36 -0700224 self->fd = -1;
225 self->created = 0;
226 self->readable = 0;
227 self->writable = 0;
Segev Finer5e437fb2021-04-24 01:00:27 +0300228 self->closefd = 0;
Steve Dower39294992016-08-30 21:22:36 -0700229 self->blksize = 0;
230 self->weakreflist = NULL;
231 }
232
233 return (PyObject *) self;
234}
235
236/*[clinic input]
237_io._WindowsConsoleIO.__init__
238 file as nameobj: object
239 mode: str = "r"
Serhiy Storchaka202fda52017-03-12 10:10:47 +0200240 closefd: bool(accept={int}) = True
Steve Dower39294992016-08-30 21:22:36 -0700241 opener: object = None
242
243Open a console buffer by file descriptor.
244
245The mode can be 'rb' (default), or 'wb' for reading or writing bytes. All
246other mode characters will be ignored. Mode 'b' will be assumed if it is
247omitted. The *opener* parameter is always ignored.
248[clinic start generated code]*/
249
250static int
251_io__WindowsConsoleIO___init___impl(winconsoleio *self, PyObject *nameobj,
252 const char *mode, int closefd,
253 PyObject *opener)
Serhiy Storchaka202fda52017-03-12 10:10:47 +0200254/*[clinic end generated code: output=3fd9cbcdd8d95429 input=06ae4b863c63244b]*/
Steve Dower39294992016-08-30 21:22:36 -0700255{
256 const char *s;
257 wchar_t *name = NULL;
Steve Dower27f26ad2016-09-17 13:51:23 -0700258 char console_type = '\0';
Steve Dower39294992016-08-30 21:22:36 -0700259 int ret = 0;
260 int rwa = 0;
261 int fd = -1;
262 int fd_is_own = 0;
Segev Finer5e437fb2021-04-24 01:00:27 +0300263 HANDLE handle = NULL;
Steve Dower39294992016-08-30 21:22:36 -0700264
265 assert(PyWindowsConsoleIO_Check(self));
Segev Finer5e437fb2021-04-24 01:00:27 +0300266 if (self->fd >= 0) {
267 if (self->closefd) {
Steve Dower39294992016-08-30 21:22:36 -0700268 /* Have to close the existing file first. */
269 if (internal_close(self) < 0)
270 return -1;
271 }
272 else
Segev Finer5e437fb2021-04-24 01:00:27 +0300273 self->fd = -1;
Steve Dower39294992016-08-30 21:22:36 -0700274 }
275
Steve Dower39294992016-08-30 21:22:36 -0700276 fd = _PyLong_AsInt(nameobj);
277 if (fd < 0) {
278 if (!PyErr_Occurred()) {
279 PyErr_SetString(PyExc_ValueError,
280 "negative file descriptor");
281 return -1;
282 }
283 PyErr_Clear();
284 }
285 self->fd = fd;
286
287 if (fd < 0) {
Victor Stinner29adc132017-06-08 18:19:25 +0200288 PyObject *decodedname;
Steve Dower39294992016-08-30 21:22:36 -0700289
290 int d = PyUnicode_FSDecoder(nameobj, (void*)&decodedname);
291 if (!d)
292 return -1;
293
Serhiy Storchakae613e6a2017-06-27 16:03:14 +0300294 name = PyUnicode_AsWideCharString(decodedname, NULL);
Steve Dower27f26ad2016-09-17 13:51:23 -0700295 console_type = _PyIO_get_console_type(decodedname);
Steve Dower39294992016-08-30 21:22:36 -0700296 Py_CLEAR(decodedname);
297 if (name == NULL)
298 return -1;
Steve Dower39294992016-08-30 21:22:36 -0700299 }
300
301 s = mode;
302 while (*s) {
303 switch (*s++) {
304 case '+':
305 case 'a':
306 case 'b':
307 case 'x':
308 break;
309 case 'r':
310 if (rwa)
311 goto bad_mode;
312 rwa = 1;
313 self->readable = 1;
Steve Dower27f26ad2016-09-17 13:51:23 -0700314 if (console_type == 'x')
315 console_type = 'r';
Steve Dower39294992016-08-30 21:22:36 -0700316 break;
317 case 'w':
318 if (rwa)
319 goto bad_mode;
320 rwa = 1;
321 self->writable = 1;
Steve Dower27f26ad2016-09-17 13:51:23 -0700322 if (console_type == 'x')
323 console_type = 'w';
Steve Dower39294992016-08-30 21:22:36 -0700324 break;
325 default:
326 PyErr_Format(PyExc_ValueError,
327 "invalid mode: %.200s", mode);
328 goto error;
329 }
330 }
331
332 if (!rwa)
333 goto bad_mode;
334
335 if (fd >= 0) {
Segev Finer5e437fb2021-04-24 01:00:27 +0300336 handle = _Py_get_osfhandle_noraise(fd);
337 self->closefd = 0;
Steve Dower39294992016-08-30 21:22:36 -0700338 } else {
339 DWORD access = GENERIC_READ;
340
Segev Finer5e437fb2021-04-24 01:00:27 +0300341 self->closefd = 1;
Steve Dower39294992016-08-30 21:22:36 -0700342 if (!closefd) {
343 PyErr_SetString(PyExc_ValueError,
344 "Cannot use closefd=False with file name");
345 goto error;
346 }
347
348 if (self->writable)
Steve Dower27f26ad2016-09-17 13:51:23 -0700349 access = GENERIC_WRITE;
Steve Dower39294992016-08-30 21:22:36 -0700350
351 Py_BEGIN_ALLOW_THREADS
352 /* Attempt to open for read/write initially, then fall back
353 on the specific access. This is required for modern names
354 CONIN$ and CONOUT$, which allow reading/writing state as
355 well as reading/writing content. */
Segev Finer5e437fb2021-04-24 01:00:27 +0300356 handle = CreateFileW(name, GENERIC_READ | GENERIC_WRITE,
Steve Dower39294992016-08-30 21:22:36 -0700357 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
Segev Finer5e437fb2021-04-24 01:00:27 +0300358 if (handle == INVALID_HANDLE_VALUE)
359 handle = CreateFileW(name, access,
Steve Dower39294992016-08-30 21:22:36 -0700360 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
361 Py_END_ALLOW_THREADS
362
Segev Finer5e437fb2021-04-24 01:00:27 +0300363 if (handle == INVALID_HANDLE_VALUE) {
Steve Dower39294992016-08-30 21:22:36 -0700364 PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, GetLastError(), nameobj);
365 goto error;
366 }
Segev Finer5e437fb2021-04-24 01:00:27 +0300367
368 if (self->writable)
369 self->fd = _Py_open_osfhandle_noraise(handle, _O_WRONLY | _O_BINARY);
370 else
371 self->fd = _Py_open_osfhandle_noraise(handle, _O_RDONLY | _O_BINARY);
372 if (self->fd < 0) {
373 CloseHandle(handle);
374 PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, nameobj);
375 goto error;
376 }
Steve Dower39294992016-08-30 21:22:36 -0700377 }
378
Steve Dower27f26ad2016-09-17 13:51:23 -0700379 if (console_type == '\0')
Segev Finer5e437fb2021-04-24 01:00:27 +0300380 console_type = _get_console_type(handle);
Steve Dower27f26ad2016-09-17 13:51:23 -0700381
382 if (self->writable && console_type != 'w') {
Steve Dower39294992016-08-30 21:22:36 -0700383 PyErr_SetString(PyExc_ValueError,
384 "Cannot open console input buffer for writing");
385 goto error;
386 }
Steve Dower27f26ad2016-09-17 13:51:23 -0700387 if (self->readable && console_type != 'r') {
Steve Dower39294992016-08-30 21:22:36 -0700388 PyErr_SetString(PyExc_ValueError,
389 "Cannot open console output buffer for reading");
390 goto error;
391 }
392
393 self->blksize = DEFAULT_BUFFER_SIZE;
394 memset(self->buf, 0, 4);
395
396 if (_PyObject_SetAttrId((PyObject *)self, &PyId_name, nameobj) < 0)
397 goto error;
398
399 goto done;
400
401bad_mode:
402 PyErr_SetString(PyExc_ValueError,
403 "Must have exactly one of read or write mode");
404error:
405 ret = -1;
406 internal_close(self);
407
408done:
409 if (name)
410 PyMem_Free(name);
411 return ret;
412}
413
414static int
415winconsoleio_traverse(winconsoleio *self, visitproc visit, void *arg)
416{
417 Py_VISIT(self->dict);
418 return 0;
419}
420
421static int
422winconsoleio_clear(winconsoleio *self)
423{
424 Py_CLEAR(self->dict);
425 return 0;
426}
427
428static void
429winconsoleio_dealloc(winconsoleio *self)
430{
431 self->finalizing = 1;
432 if (_PyIOBase_finalize((PyObject *) self) < 0)
433 return;
434 _PyObject_GC_UNTRACK(self);
435 if (self->weakreflist != NULL)
436 PyObject_ClearWeakRefs((PyObject *) self);
437 Py_CLEAR(self->dict);
438 Py_TYPE(self)->tp_free((PyObject *)self);
439}
440
441static PyObject *
442err_closed(void)
443{
444 PyErr_SetString(PyExc_ValueError, "I/O operation on closed file");
445 return NULL;
446}
447
448static PyObject *
449err_mode(const char *action)
450{
451 _PyIO_State *state = IO_STATE();
452 if (state != NULL)
453 PyErr_Format(state->unsupported_operation,
454 "Console buffer does not support %s", action);
455 return NULL;
456}
457
458/*[clinic input]
459_io._WindowsConsoleIO.fileno
460
461Return the underlying file descriptor (an integer).
462
Steve Dower39294992016-08-30 21:22:36 -0700463[clinic start generated code]*/
464
465static PyObject *
466_io__WindowsConsoleIO_fileno_impl(winconsoleio *self)
Segev Finer5e437fb2021-04-24 01:00:27 +0300467/*[clinic end generated code: output=006fa74ce3b5cfbf input=845c47ebbc3a2f67]*/
Steve Dower39294992016-08-30 21:22:36 -0700468{
Steve Dower39294992016-08-30 21:22:36 -0700469 if (self->fd < 0)
Segev Finer5e437fb2021-04-24 01:00:27 +0300470 return err_closed();
Steve Dower39294992016-08-30 21:22:36 -0700471 return PyLong_FromLong(self->fd);
472}
473
474/*[clinic input]
475_io._WindowsConsoleIO.readable
476
477True if console is an input buffer.
478[clinic start generated code]*/
479
480static PyObject *
481_io__WindowsConsoleIO_readable_impl(winconsoleio *self)
482/*[clinic end generated code: output=daf9cef2743becf0 input=6be9defb5302daae]*/
483{
Segev Finer5e437fb2021-04-24 01:00:27 +0300484 if (self->fd == -1)
Steve Dower39294992016-08-30 21:22:36 -0700485 return err_closed();
486 return PyBool_FromLong((long) self->readable);
487}
488
489/*[clinic input]
490_io._WindowsConsoleIO.writable
491
492True if console is an output buffer.
493[clinic start generated code]*/
494
495static PyObject *
496_io__WindowsConsoleIO_writable_impl(winconsoleio *self)
497/*[clinic end generated code: output=e0a2ad7eae5abf67 input=cefbd8abc24df6a0]*/
498{
Segev Finer5e437fb2021-04-24 01:00:27 +0300499 if (self->fd == -1)
Steve Dower39294992016-08-30 21:22:36 -0700500 return err_closed();
501 return PyBool_FromLong((long) self->writable);
502}
503
504static DWORD
505_buflen(winconsoleio *self)
506{
Steve Dower312cef72016-10-03 09:04:58 -0700507 for (DWORD i = 0; i < SMALLBUF; ++i) {
Steve Dower39294992016-08-30 21:22:36 -0700508 if (!self->buf[i])
509 return i;
510 }
Steve Dower312cef72016-10-03 09:04:58 -0700511 return SMALLBUF;
Steve Dower39294992016-08-30 21:22:36 -0700512}
513
514static DWORD
515_copyfrombuf(winconsoleio *self, char *buf, DWORD len)
516{
517 DWORD n = 0;
518
519 while (self->buf[0] && len--) {
Steve Dower312cef72016-10-03 09:04:58 -0700520 buf[n++] = self->buf[0];
521 for (int i = 1; i < SMALLBUF; ++i)
522 self->buf[i - 1] = self->buf[i];
523 self->buf[SMALLBUF - 1] = 0;
Steve Dower39294992016-08-30 21:22:36 -0700524 }
525
526 return n;
527}
528
529static wchar_t *
530read_console_w(HANDLE handle, DWORD maxlen, DWORD *readlen) {
531 int err = 0, sig = 0;
532
533 wchar_t *buf = (wchar_t*)PyMem_Malloc(maxlen * sizeof(wchar_t));
534 if (!buf)
535 goto error;
Steve Dower312cef72016-10-03 09:04:58 -0700536
Steve Dower39294992016-08-30 21:22:36 -0700537 *readlen = 0;
538
Steve Dower312cef72016-10-03 09:04:58 -0700539 //DebugBreak();
Steve Dower39294992016-08-30 21:22:36 -0700540 Py_BEGIN_ALLOW_THREADS
Steve Dower312cef72016-10-03 09:04:58 -0700541 DWORD off = 0;
542 while (off < maxlen) {
Victor Stinnerbcda8f12018-11-21 22:27:47 +0100543 DWORD n = (DWORD)-1;
ValeriyaSinevichce75df32018-07-19 15:34:03 -0700544 DWORD len = min(maxlen - off, BUFSIZ);
Steve Dower39294992016-08-30 21:22:36 -0700545 SetLastError(0);
546 BOOL res = ReadConsoleW(handle, &buf[off], len, &n, NULL);
547
548 if (!res) {
549 err = GetLastError();
550 break;
551 }
ValeriyaSinevichce75df32018-07-19 15:34:03 -0700552 if (n == (DWORD)-1 && (err = GetLastError()) == ERROR_OPERATION_ABORTED) {
553 break;
554 }
Steve Dower39294992016-08-30 21:22:36 -0700555 if (n == 0) {
556 err = GetLastError();
557 if (err != ERROR_OPERATION_ABORTED)
558 break;
559 err = 0;
560 HANDLE hInterruptEvent = _PyOS_SigintEvent();
561 if (WaitForSingleObjectEx(hInterruptEvent, 100, FALSE)
Steve Dower312cef72016-10-03 09:04:58 -0700562 == WAIT_OBJECT_0) {
Steve Dower39294992016-08-30 21:22:36 -0700563 ResetEvent(hInterruptEvent);
564 Py_BLOCK_THREADS
565 sig = PyErr_CheckSignals();
566 Py_UNBLOCK_THREADS
567 if (sig < 0)
568 break;
569 }
570 }
571 *readlen += n;
572
573 /* If we didn't read a full buffer that time, don't try
574 again or we will block a second time. */
575 if (n < len)
576 break;
577 /* If the buffer ended with a newline, break out */
578 if (buf[*readlen - 1] == '\n')
579 break;
Steve Dower312cef72016-10-03 09:04:58 -0700580 /* If the buffer ends with a high surrogate, expand the
581 buffer and read an extra character. */
582 WORD char_type;
583 if (off + BUFSIZ >= maxlen &&
584 GetStringTypeW(CT_CTYPE3, &buf[*readlen - 1], 1, &char_type) &&
585 char_type == C3_HIGHSURROGATE) {
586 wchar_t *newbuf;
587 maxlen += 1;
588 Py_BLOCK_THREADS
589 newbuf = (wchar_t*)PyMem_Realloc(buf, maxlen * sizeof(wchar_t));
590 Py_UNBLOCK_THREADS
591 if (!newbuf) {
592 sig = -1;
593 break;
594 }
595 buf = newbuf;
596 /* Only advance by n and not BUFSIZ in this case */
597 off += n;
598 continue;
599 }
600
601 off += BUFSIZ;
Steve Dower39294992016-08-30 21:22:36 -0700602 }
Steve Dower312cef72016-10-03 09:04:58 -0700603
Steve Dower39294992016-08-30 21:22:36 -0700604 Py_END_ALLOW_THREADS
605
606 if (sig)
607 goto error;
608 if (err) {
609 PyErr_SetFromWindowsErr(err);
610 goto error;
611 }
612
613 if (*readlen > 0 && buf[0] == L'\x1a') {
614 PyMem_Free(buf);
615 buf = (wchar_t *)PyMem_Malloc(sizeof(wchar_t));
616 if (!buf)
617 goto error;
618 buf[0] = L'\0';
619 *readlen = 0;
620 }
621
622 return buf;
623
624error:
625 if (buf)
626 PyMem_Free(buf);
627 return NULL;
628}
629
630
631static Py_ssize_t
632readinto(winconsoleio *self, char *buf, Py_ssize_t len)
633{
Segev Finer5e437fb2021-04-24 01:00:27 +0300634 if (self->fd == -1) {
Steve Dower39294992016-08-30 21:22:36 -0700635 err_closed();
636 return -1;
637 }
638 if (!self->readable) {
639 err_mode("reading");
640 return -1;
641 }
642 if (len == 0)
643 return 0;
644 if (len > BUFMAX) {
645 PyErr_Format(PyExc_ValueError, "cannot read more than %d bytes", BUFMAX);
646 return -1;
647 }
648
Segev Finer5e437fb2021-04-24 01:00:27 +0300649 HANDLE handle = _Py_get_osfhandle(self->fd);
650 if (handle == INVALID_HANDLE_VALUE)
651 return -1;
652
Steve Dower39294992016-08-30 21:22:36 -0700653 /* Each character may take up to 4 bytes in the final buffer.
654 This is highly conservative, but necessary to avoid
655 failure for any given Unicode input (e.g. \U0010ffff).
656 If the caller requests fewer than 4 bytes, we buffer one
657 character.
658 */
659 DWORD wlen = (DWORD)(len / 4);
660 if (wlen == 0) {
661 wlen = 1;
662 }
663
664 DWORD read_len = _copyfrombuf(self, buf, (DWORD)len);
665 if (read_len) {
666 buf = &buf[read_len];
667 len -= read_len;
668 wlen -= 1;
669 }
670 if (len == read_len || wlen == 0)
671 return read_len;
672
673 DWORD n;
Segev Finer5e437fb2021-04-24 01:00:27 +0300674 wchar_t *wbuf = read_console_w(handle, wlen, &n);
Steve Dower39294992016-08-30 21:22:36 -0700675 if (wbuf == NULL)
676 return -1;
677 if (n == 0) {
678 PyMem_Free(wbuf);
679 return read_len;
680 }
681
682 int err = 0;
683 DWORD u8n = 0;
684
685 Py_BEGIN_ALLOW_THREADS
686 if (len < 4) {
687 if (WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
688 self->buf, sizeof(self->buf) / sizeof(self->buf[0]),
689 NULL, NULL))
690 u8n = _copyfrombuf(self, buf, (DWORD)len);
691 } else {
692 u8n = WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
693 buf, (DWORD)len, NULL, NULL);
694 }
695
696 if (u8n) {
697 read_len += u8n;
698 u8n = 0;
699 } else {
700 err = GetLastError();
701 if (err == ERROR_INSUFFICIENT_BUFFER) {
702 /* Calculate the needed buffer for a more useful error, as this
703 means our "/ 4" logic above is insufficient for some input.
704 */
705 u8n = WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
706 NULL, 0, NULL, NULL);
707 }
708 }
709 Py_END_ALLOW_THREADS
710
711 PyMem_Free(wbuf);
712
713 if (u8n) {
714 PyErr_Format(PyExc_SystemError,
Serhiy Storchakad53fe5f2019-03-13 22:59:55 +0200715 "Buffer had room for %zd bytes but %u bytes required",
Steve Dower39294992016-08-30 21:22:36 -0700716 len, u8n);
717 return -1;
718 }
719 if (err) {
720 PyErr_SetFromWindowsErr(err);
721 return -1;
722 }
723
724 return read_len;
725}
726
727/*[clinic input]
728_io._WindowsConsoleIO.readinto
729 buffer: Py_buffer(accept={rwbuffer})
730 /
731
732Same as RawIOBase.readinto().
733[clinic start generated code]*/
734
735static PyObject *
736_io__WindowsConsoleIO_readinto_impl(winconsoleio *self, Py_buffer *buffer)
737/*[clinic end generated code: output=66d1bdfa3f20af39 input=4ed68da48a6baffe]*/
738{
739 Py_ssize_t len = readinto(self, buffer->buf, buffer->len);
740 if (len < 0)
741 return NULL;
742
743 return PyLong_FromSsize_t(len);
744}
745
746static DWORD
747new_buffersize(winconsoleio *self, DWORD currentsize)
748{
749 DWORD addend;
750
751 /* Expand the buffer by an amount proportional to the current size,
752 giving us amortized linear-time behavior. For bigger sizes, use a
753 less-than-double growth factor to avoid excessive allocation. */
754 if (currentsize > 65536)
755 addend = currentsize >> 3;
756 else
757 addend = 256 + currentsize;
758 if (addend < SMALLCHUNK)
759 /* Avoid tiny read() calls. */
760 addend = SMALLCHUNK;
761 return addend + currentsize;
762}
763
764/*[clinic input]
765_io._WindowsConsoleIO.readall
766
767Read all data from the console, returned as bytes.
768
769Return an empty bytes object at EOF.
770[clinic start generated code]*/
771
772static PyObject *
773_io__WindowsConsoleIO_readall_impl(winconsoleio *self)
774/*[clinic end generated code: output=e6d312c684f6e23b input=4024d649a1006e69]*/
775{
776 wchar_t *buf;
777 DWORD bufsize, n, len = 0;
778 PyObject *bytes;
779 DWORD bytes_size, rn;
Segev Finer5e437fb2021-04-24 01:00:27 +0300780 HANDLE handle;
Steve Dower39294992016-08-30 21:22:36 -0700781
Segev Finer5e437fb2021-04-24 01:00:27 +0300782 if (self->fd == -1)
Steve Dower39294992016-08-30 21:22:36 -0700783 return err_closed();
784
Segev Finer5e437fb2021-04-24 01:00:27 +0300785 handle = _Py_get_osfhandle(self->fd);
786 if (handle == INVALID_HANDLE_VALUE)
787 return NULL;
788
Steve Dower39294992016-08-30 21:22:36 -0700789 bufsize = BUFSIZ;
790
791 buf = (wchar_t*)PyMem_Malloc((bufsize + 1) * sizeof(wchar_t));
792 if (buf == NULL)
793 return NULL;
794
795 while (1) {
796 wchar_t *subbuf;
797
798 if (len >= (Py_ssize_t)bufsize) {
799 DWORD newsize = new_buffersize(self, len);
800 if (newsize > BUFMAX)
801 break;
802 if (newsize < bufsize) {
803 PyErr_SetString(PyExc_OverflowError,
804 "unbounded read returned more bytes "
805 "than a Python bytes object can hold");
806 PyMem_Free(buf);
807 return NULL;
808 }
809 bufsize = newsize;
810
Zackery Spytz4c49da02018-12-07 03:11:30 -0700811 wchar_t *tmp = PyMem_Realloc(buf,
812 (bufsize + 1) * sizeof(wchar_t));
813 if (tmp == NULL) {
Steve Dower39294992016-08-30 21:22:36 -0700814 PyMem_Free(buf);
815 return NULL;
816 }
Zackery Spytz4c49da02018-12-07 03:11:30 -0700817 buf = tmp;
Steve Dower39294992016-08-30 21:22:36 -0700818 }
819
Segev Finer5e437fb2021-04-24 01:00:27 +0300820 subbuf = read_console_w(handle, bufsize - len, &n);
Steve Dower39294992016-08-30 21:22:36 -0700821
822 if (subbuf == NULL) {
823 PyMem_Free(buf);
824 return NULL;
825 }
826
827 if (n > 0)
828 wcsncpy_s(&buf[len], bufsize - len + 1, subbuf, n);
829
830 PyMem_Free(subbuf);
831
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700832 /* when the read is empty we break */
833 if (n == 0)
Steve Dower39294992016-08-30 21:22:36 -0700834 break;
835
836 len += n;
837 }
838
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700839 if (len == 0 && _buflen(self) == 0) {
Steve Dower39294992016-08-30 21:22:36 -0700840 /* when the result starts with ^Z we return an empty buffer */
841 PyMem_Free(buf);
842 return PyBytes_FromStringAndSize(NULL, 0);
843 }
844
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700845 if (len) {
846 Py_BEGIN_ALLOW_THREADS
847 bytes_size = WideCharToMultiByte(CP_UTF8, 0, buf, len,
848 NULL, 0, NULL, NULL);
849 Py_END_ALLOW_THREADS
Benjamin Petersone2e792d2016-09-19 22:17:16 -0700850
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700851 if (!bytes_size) {
852 DWORD err = GetLastError();
853 PyMem_Free(buf);
854 return PyErr_SetFromWindowsErr(err);
855 }
856 } else {
857 bytes_size = 0;
Steve Dower39294992016-08-30 21:22:36 -0700858 }
859
860 bytes_size += _buflen(self);
861 bytes = PyBytes_FromStringAndSize(NULL, bytes_size);
862 rn = _copyfrombuf(self, PyBytes_AS_STRING(bytes), bytes_size);
863
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700864 if (len) {
865 Py_BEGIN_ALLOW_THREADS
866 bytes_size = WideCharToMultiByte(CP_UTF8, 0, buf, len,
867 &PyBytes_AS_STRING(bytes)[rn], bytes_size - rn, NULL, NULL);
868 Py_END_ALLOW_THREADS
Steve Dower39294992016-08-30 21:22:36 -0700869
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700870 if (!bytes_size) {
871 DWORD err = GetLastError();
872 PyMem_Free(buf);
873 Py_CLEAR(bytes);
874 return PyErr_SetFromWindowsErr(err);
875 }
876
877 /* add back the number of preserved bytes */
878 bytes_size += rn;
Steve Dower39294992016-08-30 21:22:36 -0700879 }
880
881 PyMem_Free(buf);
882 if (bytes_size < (size_t)PyBytes_GET_SIZE(bytes)) {
883 if (_PyBytes_Resize(&bytes, n * sizeof(wchar_t)) < 0) {
884 Py_CLEAR(bytes);
885 return NULL;
886 }
887 }
888 return bytes;
889}
890
891/*[clinic input]
892_io._WindowsConsoleIO.read
Serhiy Storchaka762bf402017-03-30 09:15:31 +0300893 size: Py_ssize_t(accept={int, NoneType}) = -1
Steve Dower39294992016-08-30 21:22:36 -0700894 /
895
896Read at most size bytes, returned as bytes.
897
898Only makes one system call when size is a positive integer,
899so less data may be returned than requested.
900Return an empty bytes object at EOF.
901[clinic start generated code]*/
902
903static PyObject *
904_io__WindowsConsoleIO_read_impl(winconsoleio *self, Py_ssize_t size)
Serhiy Storchaka762bf402017-03-30 09:15:31 +0300905/*[clinic end generated code: output=57df68af9f4b22d0 input=8bc73bc15d0fa072]*/
Steve Dower39294992016-08-30 21:22:36 -0700906{
907 PyObject *bytes;
908 Py_ssize_t bytes_size;
Benjamin Petersone2e792d2016-09-19 22:17:16 -0700909
Segev Finer5e437fb2021-04-24 01:00:27 +0300910 if (self->fd == -1)
Steve Dower39294992016-08-30 21:22:36 -0700911 return err_closed();
912 if (!self->readable)
913 return err_mode("reading");
914
915 if (size < 0)
916 return _io__WindowsConsoleIO_readall_impl(self);
917 if (size > BUFMAX) {
918 PyErr_Format(PyExc_ValueError, "cannot read more than %d bytes", BUFMAX);
919 return NULL;
920 }
921
922 bytes = PyBytes_FromStringAndSize(NULL, size);
923 if (bytes == NULL)
924 return NULL;
925
926 bytes_size = readinto(self, PyBytes_AS_STRING(bytes), PyBytes_GET_SIZE(bytes));
927 if (bytes_size < 0) {
928 Py_CLEAR(bytes);
929 return NULL;
930 }
931
932 if (bytes_size < PyBytes_GET_SIZE(bytes)) {
933 if (_PyBytes_Resize(&bytes, bytes_size) < 0) {
934 Py_CLEAR(bytes);
935 return NULL;
936 }
937 }
938
939 return bytes;
940}
941
942/*[clinic input]
943_io._WindowsConsoleIO.write
944 b: Py_buffer
945 /
946
947Write buffer b to file, return number of bytes written.
948
949Only makes one system call, so not all of the data may be written.
950The number of bytes actually written is returned.
951[clinic start generated code]*/
952
953static PyObject *
954_io__WindowsConsoleIO_write_impl(winconsoleio *self, Py_buffer *b)
955/*[clinic end generated code: output=775bdb16fbf9137b input=be35fb624f97c941]*/
956{
957 BOOL res = TRUE;
958 wchar_t *wbuf;
959 DWORD len, wlen, n = 0;
Segev Finer5e437fb2021-04-24 01:00:27 +0300960 HANDLE handle;
Steve Dower39294992016-08-30 21:22:36 -0700961
Segev Finer5e437fb2021-04-24 01:00:27 +0300962 if (self->fd == -1)
Steve Dower39294992016-08-30 21:22:36 -0700963 return err_closed();
964 if (!self->writable)
965 return err_mode("writing");
966
Segev Finer5e437fb2021-04-24 01:00:27 +0300967 handle = _Py_get_osfhandle(self->fd);
968 if (handle == INVALID_HANDLE_VALUE)
969 return NULL;
970
Serhiy Storchaka42c35d92018-02-24 18:55:51 +0200971 if (!b->len) {
972 return PyLong_FromLong(0);
973 }
Steve Dower39294992016-08-30 21:22:36 -0700974 if (b->len > BUFMAX)
975 len = BUFMAX;
976 else
977 len = (DWORD)b->len;
978
979 Py_BEGIN_ALLOW_THREADS
980 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, NULL, 0);
981
982 /* issue11395 there is an unspecified upper bound on how many bytes
983 can be written at once. We cap at 32k - the caller will have to
984 handle partial writes.
985 Since we don't know how many input bytes are being ignored, we
986 have to reduce and recalculate. */
987 while (wlen > 32766 / sizeof(wchar_t)) {
988 len /= 2;
989 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, NULL, 0);
990 }
991 Py_END_ALLOW_THREADS
Benjamin Petersone2e792d2016-09-19 22:17:16 -0700992
Steve Dower39294992016-08-30 21:22:36 -0700993 if (!wlen)
994 return PyErr_SetFromWindowsErr(0);
995
996 wbuf = (wchar_t*)PyMem_Malloc(wlen * sizeof(wchar_t));
997
998 Py_BEGIN_ALLOW_THREADS
999 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, wbuf, wlen);
1000 if (wlen) {
Segev Finer5e437fb2021-04-24 01:00:27 +03001001 res = WriteConsoleW(handle, wbuf, wlen, &n, NULL);
Segev Finer523776c2017-06-02 19:26:01 +03001002 if (res && n < wlen) {
Steve Dower39294992016-08-30 21:22:36 -07001003 /* Wrote fewer characters than expected, which means our
1004 * len value may be wrong. So recalculate it from the
1005 * characters that were written. As this could potentially
1006 * result in a different value, we also validate that value.
1007 */
1008 len = WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
1009 NULL, 0, NULL, NULL);
1010 if (len) {
1011 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len,
1012 NULL, 0);
1013 assert(wlen == len);
1014 }
1015 }
1016 } else
1017 res = 0;
1018 Py_END_ALLOW_THREADS
Benjamin Petersone2e792d2016-09-19 22:17:16 -07001019
Steve Dower39294992016-08-30 21:22:36 -07001020 if (!res) {
1021 DWORD err = GetLastError();
1022 PyMem_Free(wbuf);
1023 return PyErr_SetFromWindowsErr(err);
1024 }
1025
1026 PyMem_Free(wbuf);
1027 return PyLong_FromSsize_t(len);
1028}
1029
1030static PyObject *
1031winconsoleio_repr(winconsoleio *self)
1032{
Segev Finer5e437fb2021-04-24 01:00:27 +03001033 if (self->fd == -1)
Steve Dower39294992016-08-30 21:22:36 -07001034 return PyUnicode_FromFormat("<_io._WindowsConsoleIO [closed]>");
1035
1036 if (self->readable)
1037 return PyUnicode_FromFormat("<_io._WindowsConsoleIO mode='rb' closefd=%s>",
Segev Finer5e437fb2021-04-24 01:00:27 +03001038 self->closefd ? "True" : "False");
Steve Dower39294992016-08-30 21:22:36 -07001039 if (self->writable)
1040 return PyUnicode_FromFormat("<_io._WindowsConsoleIO mode='wb' closefd=%s>",
Segev Finer5e437fb2021-04-24 01:00:27 +03001041 self->closefd ? "True" : "False");
Steve Dower39294992016-08-30 21:22:36 -07001042
1043 PyErr_SetString(PyExc_SystemError, "_WindowsConsoleIO has invalid mode");
1044 return NULL;
1045}
1046
1047/*[clinic input]
1048_io._WindowsConsoleIO.isatty
1049
1050Always True.
1051[clinic start generated code]*/
1052
1053static PyObject *
1054_io__WindowsConsoleIO_isatty_impl(winconsoleio *self)
1055/*[clinic end generated code: output=9eac09d287c11bd7 input=9b91591dbe356f86]*/
1056{
Segev Finer5e437fb2021-04-24 01:00:27 +03001057 if (self->fd == -1)
Steve Dower39294992016-08-30 21:22:36 -07001058 return err_closed();
Benjamin Petersone2e792d2016-09-19 22:17:16 -07001059
Steve Dower39294992016-08-30 21:22:36 -07001060 Py_RETURN_TRUE;
1061}
1062
Steve Dower39294992016-08-30 21:22:36 -07001063#include "clinic/winconsoleio.c.h"
1064
1065static PyMethodDef winconsoleio_methods[] = {
1066 _IO__WINDOWSCONSOLEIO_READ_METHODDEF
1067 _IO__WINDOWSCONSOLEIO_READALL_METHODDEF
1068 _IO__WINDOWSCONSOLEIO_READINTO_METHODDEF
1069 _IO__WINDOWSCONSOLEIO_WRITE_METHODDEF
1070 _IO__WINDOWSCONSOLEIO_CLOSE_METHODDEF
1071 _IO__WINDOWSCONSOLEIO_READABLE_METHODDEF
1072 _IO__WINDOWSCONSOLEIO_WRITABLE_METHODDEF
1073 _IO__WINDOWSCONSOLEIO_FILENO_METHODDEF
1074 _IO__WINDOWSCONSOLEIO_ISATTY_METHODDEF
Steve Dower39294992016-08-30 21:22:36 -07001075 {NULL, NULL} /* sentinel */
1076};
1077
1078/* 'closed' and 'mode' are attributes for compatibility with FileIO. */
1079
1080static PyObject *
1081get_closed(winconsoleio *self, void *closure)
1082{
Segev Finer5e437fb2021-04-24 01:00:27 +03001083 return PyBool_FromLong((long)(self->fd == -1));
Steve Dower39294992016-08-30 21:22:36 -07001084}
1085
1086static PyObject *
1087get_closefd(winconsoleio *self, void *closure)
1088{
Segev Finer5e437fb2021-04-24 01:00:27 +03001089 return PyBool_FromLong((long)(self->closefd));
Steve Dower39294992016-08-30 21:22:36 -07001090}
1091
1092static PyObject *
1093get_mode(winconsoleio *self, void *closure)
1094{
1095 return PyUnicode_FromString(self->readable ? "rb" : "wb");
1096}
1097
1098static PyGetSetDef winconsoleio_getsetlist[] = {
1099 {"closed", (getter)get_closed, NULL, "True if the file is closed"},
1100 {"closefd", (getter)get_closefd, NULL,
1101 "True if the file descriptor will be closed by close()."},
1102 {"mode", (getter)get_mode, NULL, "String giving the file mode"},
1103 {NULL},
1104};
1105
1106static PyMemberDef winconsoleio_members[] = {
1107 {"_blksize", T_UINT, offsetof(winconsoleio, blksize), 0},
1108 {"_finalizing", T_BOOL, offsetof(winconsoleio, finalizing), 0},
1109 {NULL}
1110};
1111
1112PyTypeObject PyWindowsConsoleIO_Type = {
1113 PyVarObject_HEAD_INIT(NULL, 0)
1114 "_io._WindowsConsoleIO",
1115 sizeof(winconsoleio),
1116 0,
1117 (destructor)winconsoleio_dealloc, /* tp_dealloc */
Jeroen Demeyer530f5062019-05-31 04:13:39 +02001118 0, /* tp_vectorcall_offset */
Steve Dower39294992016-08-30 21:22:36 -07001119 0, /* tp_getattr */
1120 0, /* tp_setattr */
Jeroen Demeyer530f5062019-05-31 04:13:39 +02001121 0, /* tp_as_async */
Steve Dower39294992016-08-30 21:22:36 -07001122 (reprfunc)winconsoleio_repr, /* tp_repr */
1123 0, /* tp_as_number */
1124 0, /* tp_as_sequence */
1125 0, /* tp_as_mapping */
1126 0, /* tp_hash */
1127 0, /* tp_call */
1128 0, /* tp_str */
1129 PyObject_GenericGetAttr, /* tp_getattro */
1130 0, /* tp_setattro */
1131 0, /* tp_as_buffer */
1132 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
Antoine Pitrouada319b2019-05-29 22:12:38 +02001133 | Py_TPFLAGS_HAVE_GC, /* tp_flags */
Steve Dower39294992016-08-30 21:22:36 -07001134 _io__WindowsConsoleIO___init____doc__, /* tp_doc */
1135 (traverseproc)winconsoleio_traverse, /* tp_traverse */
1136 (inquiry)winconsoleio_clear, /* tp_clear */
1137 0, /* tp_richcompare */
1138 offsetof(winconsoleio, weakreflist), /* tp_weaklistoffset */
1139 0, /* tp_iter */
1140 0, /* tp_iternext */
1141 winconsoleio_methods, /* tp_methods */
1142 winconsoleio_members, /* tp_members */
1143 winconsoleio_getsetlist, /* tp_getset */
1144 0, /* tp_base */
1145 0, /* tp_dict */
1146 0, /* tp_descr_get */
1147 0, /* tp_descr_set */
1148 offsetof(winconsoleio, dict), /* tp_dictoffset */
1149 _io__WindowsConsoleIO___init__, /* tp_init */
1150 PyType_GenericAlloc, /* tp_alloc */
1151 winconsoleio_new, /* tp_new */
1152 PyObject_GC_Del, /* tp_free */
1153 0, /* tp_is_gc */
1154 0, /* tp_bases */
1155 0, /* tp_mro */
1156 0, /* tp_cache */
1157 0, /* tp_subclasses */
1158 0, /* tp_weaklist */
1159 0, /* tp_del */
1160 0, /* tp_version_tag */
1161 0, /* tp_finalize */
1162};
1163
Benjamin Petersone5024512018-09-12 12:06:42 -07001164PyObject * _PyWindowsConsoleIO_Type = (PyObject*)&PyWindowsConsoleIO_Type;
Steve Dower312cef72016-10-03 09:04:58 -07001165
Steve Dower39294992016-08-30 21:22:36 -07001166#endif /* MS_WINDOWS */