Skip to content

Commit 3a7f035

Browse files
CuriousLearnerserhiy-storchaka
authored andcommitted
bpo-30597: Show expected input in custom 'print' error message. (#2009)
1 parent 90e01e5 commit 3a7f035

File tree

3 files changed

+76
-3
lines changed

3 files changed

+76
-3
lines changed

Lib/test/test_print.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,5 +128,33 @@ def flush(self):
128128
raise RuntimeError
129129
self.assertRaises(RuntimeError, print, 1, file=noflush(), flush=True)
130130

131+
132+
class TestPy2MigrationHint(unittest.TestCase):
133+
"""Test that correct hint is produced analogous to Python3 syntax,
134+
if print statement is executed as in Python 2.
135+
"""
136+
137+
def test_normal_string(self):
138+
python2_print_str = 'print "Hello World"'
139+
with self.assertRaises(SyntaxError) as context:
140+
exec(python2_print_str)
141+
142+
self.assertIn('print("Hello World")', str(context.exception))
143+
144+
def test_string_with_soft_space(self):
145+
python2_print_str = 'print "Hello World",'
146+
with self.assertRaises(SyntaxError) as context:
147+
exec(python2_print_str)
148+
149+
self.assertIn('print("Hello World", end=" ")', str(context.exception))
150+
151+
def test_string_with_excessive_whitespace(self):
152+
python2_print_str = 'print "Hello World", '
153+
with self.assertRaises(SyntaxError) as context:
154+
exec(python2_print_str)
155+
156+
self.assertIn('print("Hello World", end=" ")', str(context.exception))
157+
158+
131159
if __name__ == "__main__":
132160
unittest.main()

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ What's New in Python 3.7.0 alpha 1?
1010
Core and Builtins
1111
-----------------
1212

13+
- bpo-30597: ``print`` now shows expected input in custom error message when
14+
used as a Python 2 statement. Patch by Sanyam Khurana.
15+
1316
- bpo-30682: Removed a too-strict assertion that failed for certain f-strings,
1417
such as eval("f'\\\n'") and eval("f'\\\r'").
1518

Objects/exceptions.c

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2862,6 +2862,49 @@ _PyErr_TrySetFromCause(const char *format, ...)
28622862
* or minus, using the stream redirection syntax).
28632863
*/
28642864

2865+
2866+
// Static helper for setting legacy print error message
2867+
static int
2868+
_set_legacy_print_statement_msg(PySyntaxErrorObject *self, Py_ssize_t start)
2869+
{
2870+
PyObject *strip_sep_obj = PyUnicode_FromString(" \t\r\n");
2871+
if (strip_sep_obj == NULL)
2872+
return -1;
2873+
2874+
// PRINT_OFFSET is to remove `print ` word from the data.
2875+
const int PRINT_OFFSET = 6;
2876+
Py_ssize_t text_len = PyUnicode_GET_LENGTH(self->text);
2877+
PyObject *data = PyUnicode_Substring(self->text, PRINT_OFFSET, text_len);
2878+
2879+
if (data == NULL) {
2880+
Py_DECREF(strip_sep_obj);
2881+
return -1;
2882+
}
2883+
PyObject *new_data = _PyUnicode_XStrip(data, 2, strip_sep_obj);
2884+
Py_DECREF(data);
2885+
Py_DECREF(strip_sep_obj);
2886+
2887+
if (new_data == NULL) {
2888+
return -1;
2889+
}
2890+
// gets the modified text_len after stripping `print `
2891+
text_len = PyUnicode_GET_LENGTH(new_data);
2892+
const char *maybe_end_arg = "";
2893+
if (text_len > 0 && PyUnicode_READ_CHAR(new_data, text_len-1) == ',') {
2894+
maybe_end_arg = " end=\" \"";
2895+
}
2896+
PyObject *error_msg = PyUnicode_FromFormat(
2897+
"Missing parentheses in call to 'print'. Did you mean print(%U%s)?",
2898+
new_data, maybe_end_arg
2899+
);
2900+
Py_DECREF(new_data);
2901+
if (error_msg == NULL)
2902+
return -1;
2903+
2904+
Py_XSETREF(self->msg, error_msg);
2905+
return 1;
2906+
}
2907+
28652908
static int
28662909
_check_for_legacy_statements(PySyntaxErrorObject *self, Py_ssize_t start)
28672910
{
@@ -2897,9 +2940,8 @@ _check_for_legacy_statements(PySyntaxErrorObject *self, Py_ssize_t start)
28972940
}
28982941
if (PyUnicode_Tailmatch(self->text, print_prefix,
28992942
start, text_len, -1)) {
2900-
Py_XSETREF(self->msg,
2901-
PyUnicode_FromString("Missing parentheses in call to 'print'"));
2902-
return 1;
2943+
2944+
return _set_legacy_print_statement_msg(self, start);
29032945
}
29042946

29052947
/* Check for legacy exec statements */

0 commit comments

Comments
 (0)