Подтвердить что ты не робот

Печать без круглых скобок с изменением сообщения об ошибке с использованием Python 3

Когда я пытаюсь использовать print без круглых скобок на простое имя в Python 3.4, я получаю:

>>> 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 пропустили реальную проблему из-за ошибочного сообщения об ошибке.

4b9b3361

Ответ 1

Глядя на исходный код для 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;
    }

Обратите также внимание на то, что приведенные выше ссылки выдают # 21669 на bugtracker python с некоторым обсуждением между автором и Guido о том, как это сделать. Поэтому мы следуем за кроликом (то есть _report_missing_parentheses), который находится в самом низу файла, и видим...

legacy_check_result = _check_for_legacy_statements(self, 0);

Однако есть случаи, когда это обходит и SyntaxError обычное сообщение SyntaxError, см. Ответ MSeifert для получения дополнительной информации об этом. Если мы перейдем к одной функции до _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);
}

Итак, чтобы ответить на вопрос: "Почему Python не может обнаружить проблему раньше?", Я бы сказал, что проблема с круглыми скобками - это не то, что обнаружено; он фактически анализируется после синтаксической ошибки. Это синтаксическая ошибка все время, но фактическая незначительная часть о круглых скобках впоследствии улавливается, чтобы дать дополнительный намек.

Ответ 2

Специальное сообщение об исключении для print используемое как выражение вместо функции, фактически реализуется как особый случай.

Грубо говоря, когда создается SyntaxError он вызывает специальную функцию, которая проверяет утверждение print на основе строки, к которой относится исключение.

Однако первый тест в этой функции (тот, который отвечает за сообщение об ошибке "Отсутствует скобка"), - это если в строке есть какая-либо открывающая скобка. Я скопировал исходный код для этой функции (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

Обратите внимание, что позиция курсора для двух имен/переменных, разделенных пробелом, всегда является концом второго имени:

>>> 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 max, потому что это последний символ во втором имени. Все, что следует за вторым именем (например ., (, [ ,...), игнорируется, потому что Python уже нашел SyntaxError, и ему не нужно идти дальше, потому что ничто не может сделать его допустимым синтаксисом.

Ответ 3

Возможно, я что-то не понимаю, но я не понимаю, почему Python должен указать ошибку раньше. print - это регулярная функция, то есть переменная, ссылающаяся на функцию, поэтому все это действительные утверждения:

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

Насколько я понимаю, парсеру необходимо прочитать следующий полный токен после print (в этом случае max), чтобы определить, есть ли синтаксическая ошибка. Он не может просто сказать "сбой, если нет открытой круглой скобки", потому что существует множество разных токенов, которые могут идти после print зависимости от текущего контекста.

Я не думаю, что есть случай, когда print может напрямую сопровождаться другим идентификатором или литералом, поэтому вы можете утверждать, что, как только будет одна буква, число или кавычки, которые вы должны остановить, но это будет смешение анализатора и лексерскую работу.

Ответ 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 синтаксиса сообщения, которое доказывает, что там ляп, когда первый маркер 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, но специальное сообщение все еще появляется.