`SyntaxError.print_file_and_line` 是做什么用的?

What is `SyntaxError.print_file_and_line` for?

在 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'}]

这是做什么用的?

据我所知,它的存在被用作标记来触发 print_exceptionpythonrun.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