From c8d3c3f6e7a35c7b06468d4cf56321d77250caba Mon Sep 17 00:00:00 2001 From: AN Long Date: Sun, 10 Sep 2023 20:00:47 +0800 Subject: [PATCH 1/6] gh-108996: fix and enable test_msvcrt --- Lib/test/test_msvcrt.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_msvcrt.py b/Lib/test/test_msvcrt.py index 3a63de351e095d..fbbcc71c496c07 100644 --- a/Lib/test/test_msvcrt.py +++ b/Lib/test/test_msvcrt.py @@ -1,9 +1,8 @@ +import ctypes import os import sys import unittest -raise unittest.SkipTest("FIXME! broken test see: https://github.com/python/cpython/pull/109004") - from test.support import os_helper from test.support.os_helper import TESTFN, TESTFN_ASCII @@ -63,6 +62,12 @@ def test_get_osfhandle(self): class TestConsoleIO(unittest.TestCase): + def setUp(self): + # The stdin may have left over contents by other tests (especially test_winconsoleio), + # so flush it before every test case. + h = msvcrt.get_osfhandle(sys.stdin.fileno()) + ctypes.windll.kernel32.FlushConsoleInputBuffer(h) + def test_kbhit(self): self.assertEqual(msvcrt.kbhit(), 0) From 5832f2b18c36603751e043038d0c3593ebef897c Mon Sep 17 00:00:00 2001 From: AN Long Date: Sun, 10 Sep 2023 21:07:31 +0800 Subject: [PATCH 2/6] close console io after each test --- Lib/test/test_msvcrt.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Lib/test/test_msvcrt.py b/Lib/test/test_msvcrt.py index fbbcc71c496c07..1d018dd6f5493a 100644 --- a/Lib/test/test_msvcrt.py +++ b/Lib/test/test_msvcrt.py @@ -76,28 +76,28 @@ def test_getch(self): self.assertEqual(msvcrt.getch(), b'c') def test_getwch(self): - stdin = open('CONIN$', 'r') - old_stdin = sys.stdin - try: - sys.stdin = stdin - write_input(stdin.buffer.raw, c_encoded) - self.assertEqual(msvcrt.getwch(), c) - finally: - sys.stdin = old_stdin + with open('CONIN$', 'r') as stdin: + old_stdin = sys.stdin + try: + sys.stdin = stdin + write_input(stdin.buffer.raw, c_encoded) + self.assertEqual(msvcrt.getwch(), c) + finally: + sys.stdin = old_stdin def test_getche(self): msvcrt.ungetch(b'c') self.assertEqual(msvcrt.getche(), b'c') def test_getwche(self): - stdin = open('CONIN$', 'r') - old_stdin = sys.stdin - try: - sys.stdin = stdin - write_input(stdin.buffer.raw, c_encoded) - self.assertEqual(msvcrt.getwche(), c) - finally: - sys.stdin = old_stdin + with open('CONIN$', 'r') as stdin: + old_stdin = sys.stdin + try: + sys.stdin = stdin + write_input(stdin.buffer.raw, c_encoded) + self.assertEqual(msvcrt.getwche(), c) + finally: + sys.stdin = old_stdin def test_putch(self): msvcrt.putch(b'c') From 351baac0e02e7de585d19bff2c8c4eafe06fc67c Mon Sep 17 00:00:00 2001 From: AN Long Date: Sun, 10 Sep 2023 22:29:14 +0800 Subject: [PATCH 3/6] call input flush in function --- Lib/test/test_msvcrt.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Lib/test/test_msvcrt.py b/Lib/test/test_msvcrt.py index 1d018dd6f5493a..9d5e6e712ab6d7 100644 --- a/Lib/test/test_msvcrt.py +++ b/Lib/test/test_msvcrt.py @@ -62,13 +62,9 @@ def test_get_osfhandle(self): class TestConsoleIO(unittest.TestCase): - def setUp(self): - # The stdin may have left over contents by other tests (especially test_winconsoleio), - # so flush it before every test case. + def test_kbhit(self): h = msvcrt.get_osfhandle(sys.stdin.fileno()) ctypes.windll.kernel32.FlushConsoleInputBuffer(h) - - def test_kbhit(self): self.assertEqual(msvcrt.kbhit(), 0) def test_getch(self): @@ -76,11 +72,14 @@ def test_getch(self): self.assertEqual(msvcrt.getch(), b'c') def test_getwch(self): - with open('CONIN$', 'r') as stdin: + with open('CONIN$', 'rb', buffering=0) as stdin: + h = msvcrt.get_osfhandle(stdin.fileno()) + ctypes.windll.kernel32.FlushConsoleInputBuffer(h) + old_stdin = sys.stdin try: sys.stdin = stdin - write_input(stdin.buffer.raw, c_encoded) + write_input(stdin, c_encoded) self.assertEqual(msvcrt.getwch(), c) finally: sys.stdin = old_stdin @@ -90,11 +89,14 @@ def test_getche(self): self.assertEqual(msvcrt.getche(), b'c') def test_getwche(self): - with open('CONIN$', 'r') as stdin: + with open('CONIN$', 'rb', buffering=0) as stdin: + h = msvcrt.get_osfhandle(stdin.fileno()) + ctypes.windll.kernel32.FlushConsoleInputBuffer(h) + old_stdin = sys.stdin try: sys.stdin = stdin - write_input(stdin.buffer.raw, c_encoded) + write_input(stdin, c_encoded) self.assertEqual(msvcrt.getwche(), c) finally: sys.stdin = old_stdin From e2a748192223a11990c891b89e7065ac708b4121 Mon Sep 17 00:00:00 2001 From: AN Long Date: Sun, 10 Sep 2023 23:06:11 +0800 Subject: [PATCH 4/6] update news --- .../next/Tests/2023-09-10-23-05-50.gh-issue-108996.tJBru6.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Tests/2023-09-10-23-05-50.gh-issue-108996.tJBru6.rst diff --git a/Misc/NEWS.d/next/Tests/2023-09-10-23-05-50.gh-issue-108996.tJBru6.rst b/Misc/NEWS.d/next/Tests/2023-09-10-23-05-50.gh-issue-108996.tJBru6.rst new file mode 100644 index 00000000000000..ab6b5b5952b044 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-09-10-23-05-50.gh-issue-108996.tJBru6.rst @@ -0,0 +1 @@ +Fix and enable ``test_msvcrt``. From b80cca079dc871fdaf7fc0579df850e0cf50c086 Mon Sep 17 00:00:00 2001 From: AN Long Date: Mon, 11 Sep 2023 17:20:37 +0800 Subject: [PATCH 5/6] add flush_console_input_buffer to replace ctypes in tests --- Lib/test/test_msvcrt.py | 9 +++-- PC/_testconsole.c | 37 ++++++++++++++++++++ PC/clinic/_testconsole.c.h | 70 +++++++++++++++++++++++++++++++++++++- 3 files changed, 110 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_msvcrt.py b/Lib/test/test_msvcrt.py index 9d5e6e712ab6d7..1ed791c4d61f11 100644 --- a/Lib/test/test_msvcrt.py +++ b/Lib/test/test_msvcrt.py @@ -1,4 +1,3 @@ -import ctypes import os import sys import unittest @@ -12,7 +11,7 @@ import _winapi import msvcrt; -from _testconsole import write_input +from _testconsole import write_input, flush_console_input_buffer class TestFileOperations(unittest.TestCase): @@ -64,7 +63,7 @@ def test_get_osfhandle(self): class TestConsoleIO(unittest.TestCase): def test_kbhit(self): h = msvcrt.get_osfhandle(sys.stdin.fileno()) - ctypes.windll.kernel32.FlushConsoleInputBuffer(h) + flush_console_input_buffer(h) self.assertEqual(msvcrt.kbhit(), 0) def test_getch(self): @@ -74,7 +73,7 @@ def test_getch(self): def test_getwch(self): with open('CONIN$', 'rb', buffering=0) as stdin: h = msvcrt.get_osfhandle(stdin.fileno()) - ctypes.windll.kernel32.FlushConsoleInputBuffer(h) + flush_console_input_buffer(h) old_stdin = sys.stdin try: @@ -91,7 +90,7 @@ def test_getche(self): def test_getwche(self): with open('CONIN$', 'rb', buffering=0) as stdin: h = msvcrt.get_osfhandle(stdin.fileno()) - ctypes.windll.kernel32.FlushConsoleInputBuffer(h) + flush_console_input_buffer(h) old_stdin = sys.stdin try: diff --git a/PC/_testconsole.c b/PC/_testconsole.c index 3221b985d01ba0..5e5a771b96bfec 100644 --- a/PC/_testconsole.c +++ b/PC/_testconsole.c @@ -35,6 +35,23 @@ PyModuleDef_Slot testconsole_slots[] = { {0, NULL}, }; +/*[python input] +class HANDLE_converter(CConverter): + type = 'void *' + format_unit = '"_Py_PARSE_UINTPTR"' + + def parse_arg(self, argname, displayname, *, limited_capi): + return self.format_code(""" + {paramname} = PyLong_AsVoidPtr({argname}); + if (!{paramname} && PyErr_Occurred()) {{{{ + goto exit; + }}}} + """, + argname=argname) +[python start generated code]*/ +/*[python end generated code: output=da39a3ee5e6b4b0d input=380aa5c91076742b]*/ +/*[python end generated code:]*/ + /*[clinic input] module _testconsole @@ -116,11 +133,31 @@ _testconsole_read_output_impl(PyObject *module, PyObject *file) Py_RETURN_NONE; } +/*[clinic input] +_testconsole.flush_console_input_buffer + handle: HANDLE + +Flushes the console input buffer. + +All input records currently in the input buffer are discarded. +[clinic start generated code]*/ + +static PyObject * +_testconsole_flush_console_input_buffer_impl(PyObject *module, void *handle) +/*[clinic end generated code: output=1f923a81331465ce input=be8203ae84a288f5]*/ +/*[clinic end generated code:]*/ +{ + FlushConsoleInputBuffer(handle); + + Py_RETURN_NONE; +} + #include "clinic\_testconsole.c.h" PyMethodDef testconsole_methods[] = { _TESTCONSOLE_WRITE_INPUT_METHODDEF _TESTCONSOLE_READ_OUTPUT_METHODDEF + _TESTCONSOLE_FLUSH_CONSOLE_INPUT_BUFFER_METHODDEF {NULL, NULL} }; diff --git a/PC/clinic/_testconsole.c.h b/PC/clinic/_testconsole.c.h index 99cd302ff34698..b76588909782ea 100644 --- a/PC/clinic/_testconsole.c.h +++ b/PC/clinic/_testconsole.c.h @@ -132,6 +132,70 @@ _testconsole_read_output(PyObject *module, PyObject *const *args, Py_ssize_t nar #endif /* defined(MS_WINDOWS) */ +#if defined(MS_WINDOWS) + +PyDoc_STRVAR(_testconsole_flush_console_input_buffer__doc__, +"flush_console_input_buffer($module, /, handle)\n" +"--\n" +"\n" +"Flushes the console input buffer.\n" +"\n" +"All input records currently in the input buffer are discarded."); + +#define _TESTCONSOLE_FLUSH_CONSOLE_INPUT_BUFFER_METHODDEF \ + {"flush_console_input_buffer", _PyCFunction_CAST(_testconsole_flush_console_input_buffer), METH_FASTCALL|METH_KEYWORDS, _testconsole_flush_console_input_buffer__doc__}, + +static PyObject * +_testconsole_flush_console_input_buffer_impl(PyObject *module, void *handle); + +static PyObject * +_testconsole_flush_console_input_buffer(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(handle), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"handle", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "flush_console_input_buffer", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + void *handle; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + handle = PyLong_AsVoidPtr(args[0]); + if (!handle && PyErr_Occurred()) { + goto exit; + } + return_value = _testconsole_flush_console_input_buffer_impl(module, handle); + +exit: + return return_value; +} + +#endif /* defined(MS_WINDOWS) */ + #ifndef _TESTCONSOLE_WRITE_INPUT_METHODDEF #define _TESTCONSOLE_WRITE_INPUT_METHODDEF #endif /* !defined(_TESTCONSOLE_WRITE_INPUT_METHODDEF) */ @@ -139,4 +203,8 @@ _testconsole_read_output(PyObject *module, PyObject *const *args, Py_ssize_t nar #ifndef _TESTCONSOLE_READ_OUTPUT_METHODDEF #define _TESTCONSOLE_READ_OUTPUT_METHODDEF #endif /* !defined(_TESTCONSOLE_READ_OUTPUT_METHODDEF) */ -/*[clinic end generated code: output=f59fe72cd4e73704 input=a9049054013a1b77]*/ + +#ifndef _TESTCONSOLE_FLUSH_CONSOLE_INPUT_BUFFER_METHODDEF + #define _TESTCONSOLE_FLUSH_CONSOLE_INPUT_BUFFER_METHODDEF +#endif /* !defined(_TESTCONSOLE_FLUSH_CONSOLE_INPUT_BUFFER_METHODDEF) */ +/*[clinic end generated code: output=5d488564f2500dd9 input=a9049054013a1b77]*/ From dfd39920ca1bccc9a44c320cf57ca67713bf308a Mon Sep 17 00:00:00 2001 From: AN Long Date: Thu, 21 Sep 2023 13:02:58 +0800 Subject: [PATCH 6/6] do not update the sys.stdin in console tests --- Lib/test/test_msvcrt.py | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/Lib/test/test_msvcrt.py b/Lib/test/test_msvcrt.py index 1ed791c4d61f11..81ec13026014e6 100644 --- a/Lib/test/test_msvcrt.py +++ b/Lib/test/test_msvcrt.py @@ -75,13 +75,8 @@ def test_getwch(self): h = msvcrt.get_osfhandle(stdin.fileno()) flush_console_input_buffer(h) - old_stdin = sys.stdin - try: - sys.stdin = stdin - write_input(stdin, c_encoded) - self.assertEqual(msvcrt.getwch(), c) - finally: - sys.stdin = old_stdin + write_input(stdin, c_encoded) + self.assertEqual(msvcrt.getwch(), c) def test_getche(self): msvcrt.ungetch(b'c') @@ -92,13 +87,8 @@ def test_getwche(self): h = msvcrt.get_osfhandle(stdin.fileno()) flush_console_input_buffer(h) - old_stdin = sys.stdin - try: - sys.stdin = stdin - write_input(stdin, c_encoded) - self.assertEqual(msvcrt.getwche(), c) - finally: - sys.stdin = old_stdin + write_input(stdin, c_encoded) + self.assertEqual(msvcrt.getwche(), c) def test_putch(self): msvcrt.putch(b'c')