使用 Python 打印不带括号的不同错误消息 3
Printing without parentheses varying error message using Python 3
当我尝试在 Python 3.4 的简单名称上使用不带括号的 print
时,我得到:
>>> print max
Traceback (most recent call last):
...
File "<interactive input>", line 1
print max
^
SyntaxError: Missing parentheses in call to 'print'
好的,现在我明白了,我只是忘了移植我的 Python 2 代码。
但是现在当我尝试打印函数的结果时:
>>> print max([1,2])
Traceback (most recent call last):
...
print max([1,2])
^
SyntaxError: invalid syntax
或者:
print max.__call__(23)
^
SyntaxError: invalid syntax
(请注意,在这种情况下,光标指向第一个点之前的字符。)
消息不同(并且有点误导,因为标记位于 max
函数下方)。
为什么 Python 不能及早发现问题?
注意:这个问题的灵感来自围绕这个问题的困惑:Pandas read.csv syntax error,一些 Python 专家错过了真正的问题,因为误导性错误消息。
用作语句而不是函数的 print
的特殊异常消息实际上是作为 特例 .
实现的
粗略地说,当创建 SyntaxError
时,它会调用一个特殊函数来检查 print
语句 基于 行异常是指。
但是,function (the one responsible for the "Missing parenthesis" error message) 中的第一个测试是行中是否有左括号。我复制了该函数的源代码 (CPython 3.6.4),并用 "arrows":
标记了相关行
static int
_report_missing_parentheses(PySyntaxErrorObject *self)
{
Py_UCS4 left_paren = 40;
Py_ssize_t left_paren_index;
Py_ssize_t text_len = PyUnicode_GET_LENGTH(self->text);
int legacy_check_result = 0;
/* Skip entirely if there is an opening parenthesis <---------------------------- */
left_paren_index = PyUnicode_FindChar(self->text, left_paren,
0, text_len, 1);
if (left_paren_index < -1) {
return -1;
}
if (left_paren_index != -1) {
/* Use default error message for any line with an opening parenthesis <------------ */
return 0;
}
/* Handle the simple statement case */
legacy_check_result = _check_for_legacy_statements(self, 0);
if (legacy_check_result < 0) {
return -1;
}
if (legacy_check_result == 0) {
/* Handle the one-line complex statement case */
Py_UCS4 colon = 58;
Py_ssize_t colon_index;
colon_index = PyUnicode_FindChar(self->text, colon,
0, text_len, 1);
if (colon_index < -1) {
return -1;
}
if (colon_index >= 0 && colon_index < text_len) {
/* Check again, starting from just after the colon */
if (_check_for_legacy_statements(self, colon_index+1) < 0) {
return -1;
}
}
}
return 0;
}
这意味着如果行中有任何左括号,它不会触发"Missing parenthesis"消息。即使左括号在评论中,这也会导致一般的 SyntaxError
消息:
print 10 # what(
print 10 # what(
^
SyntaxError: invalid syntax
请注意,由白色 space 分隔的两个 names/variables 的光标位置始终是第二个姓名的结尾:
>>> 10 100
10 100
^
SyntaxError: invalid syntax
>>> name1 name2
name1 name2
^
SyntaxError: invalid syntax
>>> name1 name2([1, 2])
name1 name2([1, 2])
^
SyntaxError: invalid syntax
难怪光标指向max
的x
,因为它是第二个名字的最后一个字符。第二个名字后面的所有内容(如 .
、(
、[
、...)都将被忽略,因为 Python 已经找到 SyntaxError
,并且它不需要更进一步,因为没有什么可以使它成为有效的语法。
可能是我没看懂,但我不明白为什么Python要早点指出错误。 print
是一个常规函数,即一个引用函数的变量,所以这些都是有效的语句:
print(10)
print, max, 2
str(print)
print.__doc__
[print] + ['a', 'b']
{print: 2}
据我了解,解析器需要读取print
(在本例中为max
)之后的下一个完整标记,以确定是否存在语法错误。它不能只说 "fail if there is no open parenthesis",因为在 print
之后可能会有许多不同的标记,具体取决于当前上下文。
我不认为 print
后面可以直接跟另一个标识符或文字,所以你可以争辩说只要有一个字母、一个数字或引号你就应该停止,但这会混淆解析器和词法分析器的工作。
查看 source code for exceptions.c
,就在 _set_legacy_print_statement_msg
上方,有一个不错的块评论:
/* To help with migration from Python 2, SyntaxError.__init__ applies some
* heuristics to try to report a more meaningful exception when print and
* exec are used like statements.
*
* The heuristics are currently expected to detect the following cases:
* - top level statement
* - statement in a nested suite
* - trailing section of a one line complex statement
*
* They're currently known not to trigger:
* - after a semi-colon
*
* The error message can be a bit odd in cases where the "arguments" are
* completely illegal syntactically, but that isn't worth the hassle of
* fixing.
*
* We also can't do anything about cases that are legal Python 3 syntax
* but mean something entirely different from what they did in Python 2
* (omitting the arguments entirely, printing items preceded by a unary plus
* or minus, using the stream redirection syntax).
*/
这里有一些有趣的信息。另外,在同一个文件中的SyntaxError_init
方法中,我们可以看到
/*
* Issue #21669: Custom error for 'print' & 'exec' as statements
*
* Only applies to SyntaxError instances, not to subclasses such
* as TabError or IndentationError (see issue #31161)
*/
if ((PyObject*)Py_TYPE(self) == PyExc_SyntaxError &&
self->text && PyUnicode_Check(self->text) &&
_report_missing_parentheses(self) < 0) {
return -1;
}
另请注意,以上引用 issue #21669 on the python bugtracker 以及作者和 Guido 之间关于如何着手进行的一些讨论。于是我们跟着文件最下面的兔子(也就是_report_missing_parentheses
),看到...
legacy_check_result = _check_for_legacy_statements(self, 0);
然而,在某些情况下,这会被绕过并打印正常的 SyntaxError
消息,请参阅 了解更多信息。如果我们将一个函数执行到 _check_for_legacy_statements
,我们 最终 会看到遗留打印语句的 实际检查 。
/* Check for legacy print statements */
if (print_prefix == NULL) {
print_prefix = PyUnicode_InternFromString("print ");
if (print_prefix == NULL) {
return -1;
}
}
if (PyUnicode_Tailmatch(self->text, print_prefix,
start, text_len, -1)) {
return _set_legacy_print_statement_msg(self, start);
}
所以,为了回答这个问题:"Why isn't Python able to detect the problem earlier?",我想说括号的问题不是检测到的;它实际上是 在 语法错误之后被解析。这一直是一个语法错误,但后来发现了关于括号的实际小部分,只是为了提供额外的提示。
除了那些出色的答案之外,即使不查看源代码,我们也能猜到 print
特殊错误消息是一个杂凑:
所以:
print dfjdkf
^
SyntaxError: Missing parentheses in call to 'print'
但是:
>>> a = print
>>> a dsds
Traceback (most recent call last):
File "<interactive input>", line 1
a dsds
^
SyntaxError: invalid syntax
even if a == print
但在那个阶段,它还没有被评估,所以你得到的是通用的无效语法消息而不是被黑的 print
语法消息,这证明有一个kludge当第一个标记是 print
.
如果需要,再提供一个证明:
>>> print = None
>>> print a
Traceback (most recent call last):
File "C:\Python34\lib\code.py", line 63, in runsource
print a
^
SyntaxError: Missing parentheses in call to 'print'
在那种情况下 print == None
,但特定消息仍会出现。
当我尝试在 Python 3.4 的简单名称上使用不带括号的 print
时,我得到:
>>> print max
Traceback (most recent call last):
...
File "<interactive input>", line 1
print max
^
SyntaxError: Missing parentheses in call to 'print'
好的,现在我明白了,我只是忘了移植我的 Python 2 代码。
但是现在当我尝试打印函数的结果时:
>>> print max([1,2])
Traceback (most recent call last):
...
print max([1,2])
^
SyntaxError: invalid syntax
或者:
print max.__call__(23)
^
SyntaxError: invalid syntax
(请注意,在这种情况下,光标指向第一个点之前的字符。)
消息不同(并且有点误导,因为标记位于 max
函数下方)。
为什么 Python 不能及早发现问题?
注意:这个问题的灵感来自围绕这个问题的困惑:Pandas read.csv syntax error,一些 Python 专家错过了真正的问题,因为误导性错误消息。
用作语句而不是函数的 print
的特殊异常消息实际上是作为 特例 .
粗略地说,当创建 SyntaxError
时,它会调用一个特殊函数来检查 print
语句 基于 行异常是指。
但是,function (the one responsible for the "Missing parenthesis" error message) 中的第一个测试是行中是否有左括号。我复制了该函数的源代码 (CPython 3.6.4),并用 "arrows":
标记了相关行static int
_report_missing_parentheses(PySyntaxErrorObject *self)
{
Py_UCS4 left_paren = 40;
Py_ssize_t left_paren_index;
Py_ssize_t text_len = PyUnicode_GET_LENGTH(self->text);
int legacy_check_result = 0;
/* Skip entirely if there is an opening parenthesis <---------------------------- */
left_paren_index = PyUnicode_FindChar(self->text, left_paren,
0, text_len, 1);
if (left_paren_index < -1) {
return -1;
}
if (left_paren_index != -1) {
/* Use default error message for any line with an opening parenthesis <------------ */
return 0;
}
/* Handle the simple statement case */
legacy_check_result = _check_for_legacy_statements(self, 0);
if (legacy_check_result < 0) {
return -1;
}
if (legacy_check_result == 0) {
/* Handle the one-line complex statement case */
Py_UCS4 colon = 58;
Py_ssize_t colon_index;
colon_index = PyUnicode_FindChar(self->text, colon,
0, text_len, 1);
if (colon_index < -1) {
return -1;
}
if (colon_index >= 0 && colon_index < text_len) {
/* Check again, starting from just after the colon */
if (_check_for_legacy_statements(self, colon_index+1) < 0) {
return -1;
}
}
}
return 0;
}
这意味着如果行中有任何左括号,它不会触发"Missing parenthesis"消息。即使左括号在评论中,这也会导致一般的 SyntaxError
消息:
print 10 # what(
print 10 # what(
^
SyntaxError: invalid syntax
请注意,由白色 space 分隔的两个 names/variables 的光标位置始终是第二个姓名的结尾:
>>> 10 100
10 100
^
SyntaxError: invalid syntax
>>> name1 name2
name1 name2
^
SyntaxError: invalid syntax
>>> name1 name2([1, 2])
name1 name2([1, 2])
^
SyntaxError: invalid syntax
难怪光标指向max
的x
,因为它是第二个名字的最后一个字符。第二个名字后面的所有内容(如 .
、(
、[
、...)都将被忽略,因为 Python 已经找到 SyntaxError
,并且它不需要更进一步,因为没有什么可以使它成为有效的语法。
可能是我没看懂,但我不明白为什么Python要早点指出错误。 print
是一个常规函数,即一个引用函数的变量,所以这些都是有效的语句:
print(10)
print, max, 2
str(print)
print.__doc__
[print] + ['a', 'b']
{print: 2}
据我了解,解析器需要读取print
(在本例中为max
)之后的下一个完整标记,以确定是否存在语法错误。它不能只说 "fail if there is no open parenthesis",因为在 print
之后可能会有许多不同的标记,具体取决于当前上下文。
我不认为 print
后面可以直接跟另一个标识符或文字,所以你可以争辩说只要有一个字母、一个数字或引号你就应该停止,但这会混淆解析器和词法分析器的工作。
查看 source code for exceptions.c
,就在 _set_legacy_print_statement_msg
上方,有一个不错的块评论:
/* To help with migration from Python 2, SyntaxError.__init__ applies some
* heuristics to try to report a more meaningful exception when print and
* exec are used like statements.
*
* The heuristics are currently expected to detect the following cases:
* - top level statement
* - statement in a nested suite
* - trailing section of a one line complex statement
*
* They're currently known not to trigger:
* - after a semi-colon
*
* The error message can be a bit odd in cases where the "arguments" are
* completely illegal syntactically, but that isn't worth the hassle of
* fixing.
*
* We also can't do anything about cases that are legal Python 3 syntax
* but mean something entirely different from what they did in Python 2
* (omitting the arguments entirely, printing items preceded by a unary plus
* or minus, using the stream redirection syntax).
*/
这里有一些有趣的信息。另外,在同一个文件中的SyntaxError_init
方法中,我们可以看到
/*
* Issue #21669: Custom error for 'print' & 'exec' as statements
*
* Only applies to SyntaxError instances, not to subclasses such
* as TabError or IndentationError (see issue #31161)
*/
if ((PyObject*)Py_TYPE(self) == PyExc_SyntaxError &&
self->text && PyUnicode_Check(self->text) &&
_report_missing_parentheses(self) < 0) {
return -1;
}
另请注意,以上引用 issue #21669 on the python bugtracker 以及作者和 Guido 之间关于如何着手进行的一些讨论。于是我们跟着文件最下面的兔子(也就是_report_missing_parentheses
),看到...
legacy_check_result = _check_for_legacy_statements(self, 0);
然而,在某些情况下,这会被绕过并打印正常的 SyntaxError
消息,请参阅 _check_for_legacy_statements
,我们 最终 会看到遗留打印语句的 实际检查 。
/* Check for legacy print statements */
if (print_prefix == NULL) {
print_prefix = PyUnicode_InternFromString("print ");
if (print_prefix == NULL) {
return -1;
}
}
if (PyUnicode_Tailmatch(self->text, print_prefix,
start, text_len, -1)) {
return _set_legacy_print_statement_msg(self, start);
}
所以,为了回答这个问题:"Why isn't Python able to detect the problem earlier?",我想说括号的问题不是检测到的;它实际上是 在 语法错误之后被解析。这一直是一个语法错误,但后来发现了关于括号的实际小部分,只是为了提供额外的提示。
除了那些出色的答案之外,即使不查看源代码,我们也能猜到 print
特殊错误消息是一个杂凑:
所以:
print dfjdkf
^
SyntaxError: Missing parentheses in call to 'print'
但是:
>>> a = print
>>> a dsds
Traceback (most recent call last):
File "<interactive input>", line 1
a dsds
^
SyntaxError: invalid syntax
even if a == print
但在那个阶段,它还没有被评估,所以你得到的是通用的无效语法消息而不是被黑的 print
语法消息,这证明有一个kludge当第一个标记是 print
.
如果需要,再提供一个证明:
>>> print = None
>>> print a
Traceback (most recent call last):
File "C:\Python34\lib\code.py", line 63, in runsource
print a
^
SyntaxError: Missing parentheses in call to 'print'
在那种情况下 print == None
,但特定消息仍会出现。