web-dev-qa-db-ja.com

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構文エラーここで、いくつかのPython専門家は、誤解を招くエラーメッセージのために実際の問題を見逃しました。

30

exceptions.cのソースコード を見ると、_set_legacy_print_statement_msgのすぐ上に、このNiceブロックのコメントがあります。

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

また、上記の参考文献 python bugtracker の問題#21669)で、著者とGuidoの間でこれについての議論が行われていることに注意してください。つまり、_report_missing_parentheses)はファイルの一番下にあり、参照してください...

legacy_check_result = _check_for_legacy_statements(self, 0);

ただし、これがバイパスされ、通常のSyntaxErrorメッセージが出力される場合があります。詳細については、 MSeifertの答え を参照してください。 1つの関数を_check_for_legacy_statementsまで実行すると、finallyレガシー印刷ステートメントのactual checkが表示されます。

/* 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);
}

したがって、「なぜPython問題をより早く検出できないのですか? 』」という質問に答えるために、括弧の問題は検出されたものではなく、実際に解析されますafter構文エラーこれは常に構文エラーですが、括弧に関する実際のマイナーな部分は、追加のヒントを与えるために後でキャッチされます。

28
alkasm

関数としてではなくステートメントとして使用されるprintの特別な例外メッセージは、実際にはspecial caseとして実装されます。

大まかに言って、SyntaxErrorが作成されると、例外が参照するlineに基づいてprintstatementをチェックする特別な関数を呼び出します。

ただし、この最初のテスト 関数( "かっこがありません"エラーメッセージの原因となるもの) は、行に開きかっこがあるかどうかです。その関数のソースコードをコピーし(CPython 3.6.4)、関連する行を「矢印」でマークしました。

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

つまり、行に任意の開き括弧がある場合、「括弧がありません」というメッセージはトリガーされません。これは、開始かっこがコメント内にある場合でも、一般的なSyntaxErrorメッセージにつながります。

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

空白で区切られた2つの名前/変数のカーソル位置は、常に2番目の名前の終わりであることに注意してください。

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

カーソルがxmaxを指しているのも不思議ではありません。これは、2番目の名前の最後の文字だからです。 2番目の名前(.([、...など)に続くものはすべて無視されます。これは、Pythonが既にSyntaxErrorを見つけたためです。何も有効な構文にすることができなかったため、さらに進む必要はありません。

17
MSeifert

これらの優れた答えに加えて、ソースコードを見なくても、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

たとえ a == printしかし、その段階ではまだ評価されていないので、ハッキングされたprint構文メッセージの代わりに一般的な無効な構文メッセージを取得します。これは、最初のトークンが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が先にエラーを指摘する必要があるのか​​わかりません。printは関数を参照する変数です。 、したがって、これらはすべて有効なステートメントです。

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

私の理解では、構文エラーがあるかどうかを判断するために、パーサーはprint(この場合はmax)の後の次の完全なトークンを読み取る必要があります。現在のコンテキストに応じてprintの後に続く可能性のあるさまざまなトークンがあるため、単に「開き括弧がない場合は失敗」と言うことはできません。

printの直後に別の識別子またはリテラルが続く場合があるとは思わないため、1つの文字、数字、または引用符があるとすぐに停止する必要があると主張できますが、パーサーの仕事とレクサーの仕事を混ぜることになります。

4
jdehesa