Для чего нужен SyntaxError.print_file_and_line? - PullRequest
1 голос
/ 24 марта 2020

В Python, SyntaxError объекты имеют атрибут print_file_and_line:

>>> SyntaxError.print_file_and_line
<member 'print_file_and_line' of 'SyntaxError' objects>

>>> help(SyntaxError.print_file_and_line)
Help on member descriptor builtins.SyntaxError.print_file_and_line:

print_file_and_line
    exception print_file_and_line

>>> s = SyntaxError()
>>> s.print_file_and_line
# None
>>> s.print_file_and_line = [{'what am I for'}]

>>> s.print_file_and_line
[{'what am I for'}]

Для чего это нужно?

1 Ответ

1 голос
/ 25 марта 2020

Насколько я могу судить, его наличие используется в качестве маркера для запуска дополнительного кода в print_exception в pythonrun.c для распечатки файла и строки синтаксической ошибки, а также фактического текста линия и каретка, обозначающие позицию ошибки, после распечатки остальной части трассировки стека. Помните, что код с синтаксической ошибкой никогда не выполняется - в конце концов, его не удалось импортировать - поэтому он фактически не является частью трассировки стека. У меня сложилось впечатление, что print_file_and_line - это скорее фрагмент реализации, чем то, с чем вы можете с пользой взаимодействовать.

Когда вы видите трассировку, напечатанную из-за синтаксической ошибки, эти строки, отмеченные <<< ниже, печатаются из-за этого кода.

$ echo ')' > syntax_error.py
$ python -c 'import syntax_error'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "syntax_error.py", line 1           <<<
    )                                      <<<
    ^                                      <<<
SyntaxError: invalid syntax

Код находится в pythonrun.c здесь:

https://github.com/python/cpython/blob/37fcbb65d4589fbb5a72153e9338cf8e6495f64f/Python/pythonrun.c#L795 -L827

И выглядит это:

if (err == 0 &&
    _PyObject_HasAttrId(value, &PyId_print_file_and_line))
{
    PyObject *message, *filename, *text;
    int lineno, offset;
    if (!parse_syntax_error(value, &message, &filename,
                            &lineno, &offset, &text))
        PyErr_Clear();
    else {
        PyObject *line;

        Py_DECREF(value);
        value = message;

        line = PyUnicode_FromFormat("  File \"%S\", line %d\n",
                                      filename, lineno);
        Py_DECREF(filename);
        if (line != NULL) {
            PyFile_WriteObject(line, f, Py_PRINT_RAW);
            Py_DECREF(line);
        }

        if (text != NULL) {
            print_error_text(f, offset, text);
            Py_DECREF(text);
        }

        /* Can't be bothered to check all those
           PyFile_WriteString() calls */
        if (PyErr_Occurred())
            err = -1;
    }
}

Я попытался провести эксперимент, чтобы проверить это. Если я запускаю этот скрипт:

class FakeSyntaxException(Exception):
    print_file_and_line = None
    def __init__(self):
        self.text = 'Here is some text'
        self.lineno = 123
        self.offset = 6
        self.msg = 'Something went wrong'
        self.filename = 'example.txt'

raise FakeSyntaxException()

Я вижу:

$ python3 python_syntax_test.py
Traceback (most recent call last):
  File "python_syntax_test.py", line 10, in <module>
    raise FakeSyntaxException()
  File "example.txt", line 123
    Here is some text
         ^
__main__.FakeSyntaxException: Something went wrong

Принимая во внимание, что если я закомментирую строку print_file_and_line, я вижу только:

$ python3 python_syntax_test.py
Traceback (most recent call last):
  File "python_syntax_test.py", line 10, in <module>
    raise FakeSyntaxException()
__main__.FakeSyntaxException
...