Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions Lib/test/test_msvcrt.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,22 @@ def run_in_separated_process(self, code):
subprocess.run(cmd, check=True, capture_output=True,
creationflags=subprocess.CREATE_NEW_CONSOLE)

@requires_resource('gui')
def assert_console_read_raises_after_freeconsole(self, funcname):
code = dedent(f'''
import ctypes
import msvcrt
import sys

ctypes.windll.kernel32.FreeConsole()
try:
msvcrt.{funcname}()
except OSError:
sys.exit(0)
sys.exit(1)
''')
self.run_in_separated_process(code)

def test_kbhit(self):
code = dedent('''
import msvcrt
Expand All @@ -81,6 +97,9 @@ def test_getch(self):
msvcrt.ungetch(b'c')
self.assertEqual(msvcrt.getch(), b'c')

def test_getch_without_console(self):
self.assert_console_read_raises_after_freeconsole('getch')

def check_getwch(self, funcname):
code = dedent(f'''
import msvcrt
Expand All @@ -94,13 +113,22 @@ def check_getwch(self, funcname):
def test_getwch(self):
self.check_getwch('getwch')

def test_getwch_without_console(self):
self.assert_console_read_raises_after_freeconsole('getwch')

def test_getche(self):
msvcrt.ungetch(b'c')
self.assertEqual(msvcrt.getche(), b'c')

def test_getche_without_console(self):
self.assert_console_read_raises_after_freeconsole('getche')

def test_getwche(self):
self.check_getwch('getwche')

def test_getwche_without_console(self):
self.assert_console_read_raises_after_freeconsole('getwche')

def test_putch(self):
msvcrt.putch(b'c')

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix :func:`msvcrt.getch`, :func:`msvcrt.getche`, :func:`msvcrt.getwch`, and
:func:`msvcrt.getwche` to raise :exc:`OSError` instead of returning the CRT
EOF sentinel when no console is attached.
26 changes: 23 additions & 3 deletions PC/clinic/msvcrtmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 18 additions & 2 deletions PC/msvcrtmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,11 @@ class byte_char_return_converter(CReturnConverter):
type = 'int'
def render(self, function, data):
self.declare(data)
self.err_occurred_if("_return_value == EOF", data)
data.declarations.append('char s[1];')
data.return_value = 's[0]'
data.return_conversion.append(
's[0] = (char)_return_value;\n')
data.return_conversion.append(
'return_value = PyBytes_FromStringAndSize(s, 1);\n')
Expand All @@ -72,10 +75,11 @@ class wchar_t_return_converter(CReturnConverter):
def render(self, function, data):
self.declare(data)
self.err_occurred_if("_return_value == WEOF", data)
data.return_conversion.append(
'return_value = PyUnicode_FromOrdinal(_return_value);\n')
[python start generated code]*/
/*[python end generated code: output=da39a3ee5e6b4b0d input=ff031be44ab3250d]*/
/*[python end generated code: output=da39a3ee5e6b4b0d input=4da08376e54a1a4d]*/

/*[clinic input]
module msvcrt
Expand Down Expand Up @@ -252,6 +256,9 @@ msvcrt_getch_impl(PyObject *module)
Py_BEGIN_ALLOW_THREADS
ch = _getch();
Py_END_ALLOW_THREADS
if (ch == EOF) {
PyErr_SetFromWindowsErr(0);
}
return ch;
}

Expand All @@ -272,6 +279,9 @@ msvcrt_getwch_impl(PyObject *module)
Py_BEGIN_ALLOW_THREADS
ch = _getwch();
Py_END_ALLOW_THREADS
if (ch == WEOF) {
PyErr_SetFromWindowsErr(0);
}
return ch;
}

Expand All @@ -292,6 +302,9 @@ msvcrt_getche_impl(PyObject *module)
Py_BEGIN_ALLOW_THREADS
ch = _getche();
Py_END_ALLOW_THREADS
if (ch == EOF) {
PyErr_SetFromWindowsErr(0);
}
return ch;
}

Expand All @@ -312,6 +325,9 @@ msvcrt_getwche_impl(PyObject *module)
Py_BEGIN_ALLOW_THREADS
ch = _getwche();
Py_END_ALLOW_THREADS
if (ch == WEOF) {
PyErr_SetFromWindowsErr(0);
}
return ch;
}

Expand Down
Loading