Skip to content

Commit 6b93670

Browse files
gh-149324: Raise OSError for msvcrt getch() without console
1 parent 055cbf0 commit 6b93670

4 files changed

Lines changed: 72 additions & 5 deletions

File tree

Lib/test/test_msvcrt.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,22 @@ def run_in_separated_process(self, code):
7070
subprocess.run(cmd, check=True, capture_output=True,
7171
creationflags=subprocess.CREATE_NEW_CONSOLE)
7272

73+
@requires_resource('gui')
74+
def assert_console_read_raises_after_freeconsole(self, funcname):
75+
code = dedent(f'''
76+
import ctypes
77+
import msvcrt
78+
import sys
79+
80+
ctypes.windll.kernel32.FreeConsole()
81+
try:
82+
msvcrt.{funcname}()
83+
except OSError:
84+
sys.exit(0)
85+
sys.exit(1)
86+
''')
87+
self.run_in_separated_process(code)
88+
7389
def test_kbhit(self):
7490
code = dedent('''
7591
import msvcrt
@@ -81,6 +97,9 @@ def test_getch(self):
8197
msvcrt.ungetch(b'c')
8298
self.assertEqual(msvcrt.getch(), b'c')
8399

100+
def test_getch_without_console(self):
101+
self.assert_console_read_raises_after_freeconsole('getch')
102+
84103
def check_getwch(self, funcname):
85104
code = dedent(f'''
86105
import msvcrt
@@ -94,13 +113,22 @@ def check_getwch(self, funcname):
94113
def test_getwch(self):
95114
self.check_getwch('getwch')
96115

116+
def test_getwch_without_console(self):
117+
self.assert_console_read_raises_after_freeconsole('getwch')
118+
97119
def test_getche(self):
98120
msvcrt.ungetch(b'c')
99121
self.assertEqual(msvcrt.getche(), b'c')
100122

123+
def test_getche_without_console(self):
124+
self.assert_console_read_raises_after_freeconsole('getche')
125+
101126
def test_getwche(self):
102127
self.check_getwch('getwche')
103128

129+
def test_getwche_without_console(self):
130+
self.assert_console_read_raises_after_freeconsole('getwche')
131+
104132
def test_putch(self):
105133
msvcrt.putch(b'c')
106134

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix :func:`msvcrt.getch`, :func:`msvcrt.getche`, :func:`msvcrt.getwch`, and
2+
:func:`msvcrt.getwche` to raise :exc:`OSError` instead of returning the CRT
3+
EOF sentinel when no console is attached.

PC/clinic/msvcrtmodule.c.h

Lines changed: 23 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

PC/msvcrtmodule.c

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,11 @@ class byte_char_return_converter(CReturnConverter):
6262
type = 'int'
6363
6464
def render(self, function, data):
65+
self.declare(data)
66+
self.err_occurred_if("_return_value == EOF", data)
6567
data.declarations.append('char s[1];')
66-
data.return_value = 's[0]'
68+
data.return_conversion.append(
69+
's[0] = (char)_return_value;\n')
6770
data.return_conversion.append(
6871
'return_value = PyBytes_FromStringAndSize(s, 1);\n')
6972
@@ -72,10 +75,11 @@ class wchar_t_return_converter(CReturnConverter):
7275
7376
def render(self, function, data):
7477
self.declare(data)
78+
self.err_occurred_if("_return_value == WEOF", data)
7579
data.return_conversion.append(
7680
'return_value = PyUnicode_FromOrdinal(_return_value);\n')
7781
[python start generated code]*/
78-
/*[python end generated code: output=da39a3ee5e6b4b0d input=ff031be44ab3250d]*/
82+
/*[python end generated code: output=da39a3ee5e6b4b0d input=4da08376e54a1a4d]*/
7983

8084
/*[clinic input]
8185
module msvcrt
@@ -252,6 +256,9 @@ msvcrt_getch_impl(PyObject *module)
252256
Py_BEGIN_ALLOW_THREADS
253257
ch = _getch();
254258
Py_END_ALLOW_THREADS
259+
if (ch == EOF) {
260+
PyErr_SetFromWindowsErr(0);
261+
}
255262
return ch;
256263
}
257264

@@ -272,6 +279,9 @@ msvcrt_getwch_impl(PyObject *module)
272279
Py_BEGIN_ALLOW_THREADS
273280
ch = _getwch();
274281
Py_END_ALLOW_THREADS
282+
if (ch == WEOF) {
283+
PyErr_SetFromWindowsErr(0);
284+
}
275285
return ch;
276286
}
277287

@@ -292,6 +302,9 @@ msvcrt_getche_impl(PyObject *module)
292302
Py_BEGIN_ALLOW_THREADS
293303
ch = _getche();
294304
Py_END_ALLOW_THREADS
305+
if (ch == EOF) {
306+
PyErr_SetFromWindowsErr(0);
307+
}
295308
return ch;
296309
}
297310

@@ -312,6 +325,9 @@ msvcrt_getwche_impl(PyObject *module)
312325
Py_BEGIN_ALLOW_THREADS
313326
ch = _getwche();
314327
Py_END_ALLOW_THREADS
328+
if (ch == WEOF) {
329+
PyErr_SetFromWindowsErr(0);
330+
}
315331
return ch;
316332
}
317333

0 commit comments

Comments
 (0)