使用Python 3打印没有括号的错误消息

时间:2018-01-10 09:45:38

标签: python python-3.x syntax-error python-internals

当我尝试在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专家错过了真正的问题。

4 个答案:

答案 0 :(得分:28)

查看_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; } 方法中,我们可以看到

_report_missing_parentheses

另请注意以上引用source code for exceptions.c与作者和Guido之间的一些讨论如何解决这个问题。所以我们跟着位于文件最底部的兔子(即legacy_check_result = _check_for_legacy_statements(self, 0); ),看看......

SyntaxError

但是,在某些情况下会绕过此功能并打印正常的_check_for_legacy_statements消息,有关详细信息,请参阅issue #21669 on the python bugtracker。如果我们最多使用一个函数/* 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); } ,我们最后查看旧版打印语句的实际检查

    public void doSomeWork(){
if (tahunAngka < 0 || tahunAngka > 5) {
            //error message
            Toast.makeText(MainActivity.this, "You Must Enter a Number Between 1 - 5", Toast.LENGTH_SHORT).show();
            return;
        }

        if (bulanAngka < 0 || bulanAngka > 11) {
            Toast.makeText(MainActivity.this, "You Must Enter a Number Between 1 - 12", Toast.LENGTH_SHORT).show();
            return;
        }
}

所以,回答这个问题:&#34;为什么Python不能提前发现问题?&#34;我会说括号的问题不是检测到的问题; 语法错误后实际解析了。它一直是语法错误,但是关于括号的实际小部分后来被捕获只是为了给出一个额外的提示。

答案 1 :(得分:17)

用作语句而不是函数的print的特殊异常消息实际上是作为特殊情况实现的。

粗略地说,当创建SyntaxError时,它会调用一个特殊函数,该函数根据检查print 语句到。

但是,此function (the one responsible for the "Missing parenthesis" error message)中的第一个测试是行中是否有任何左括号。我复制了该函数的源代码(CPython 3.6.4),并用&#34;箭头&#34;标记了相关的行:

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;
}

这意味着它不会触发&#34;缺少括号&#34;如果行中有任何左括号,则会显示该消息。即使左括号位于注释中,这也会导致SyntaxError消息:

print 10  # what(
    print 10  # what(
           ^
SyntaxError: invalid syntax

请注意,由空格分隔的两个名称/变量的光标位置始终是第二个名称的结尾:

>>> 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

因此,毫无疑问光标指向x的{​​{1}},因为它是第二个名称中的最后一个字符。第二个名称后面的所有内容(例如max.(,...)都会被忽略,因为Python已经找到[,但它并没有找到SyntaxError。我需要更进一步,因为没有什么能使它成为有效的语法。

答案 2 :(得分:4)

也许我不理解某些东西,但我不明白为什么Python应该早点指出错误。 print是一个常规函数,它是一个引用函数的变量,所以这些都是有效的语句:

print(10)
print, max, 2
str(print)
print.__doc__
[print] + ['a', 'b']
{print: 2}

据我了解,解析器需要在print(在这种情况下为max)之后读取下一个完整令牌,以确定是否存在语法错误。如果没有开括号,它就不能说'#34;失败&#34;,因为根据当前的上下文,print之后可能会有许多不同的标记。

我不认为有一种情况print可能会直接跟着另一个标识符或文字,所以你可以争辩说,只要有一个字母,一个数字或引号就应该停止,但这会混合解析器和lexer的工作。

答案 3 :(得分:4)

除了那些优秀的答案,甚至没有查看源代码,我们可能已经猜到print特殊错误消息是一个kludge:

这样:

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

即使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,但仍会显示特定消息。