Почему Python считывает из текущего каталога при печати трассировки?

$ echo "Your code is bad and you should feel bad" > "<stdin>"
$ python
Python 3.6.0 (default, Dec 28 2016, 19:53:26) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-11)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 2 + '2'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
    Your code is bad and you should feel bad
TypeError: unsupported operand type(s) for +: 'int' and 'str'

Почему Python путает строку "<stdin>" с файлом, соответствующим этому имени файла? Я не хотел, чтобы Python пытался просто читать любые файлы с моего диска, если он столкнулся с необработанным исключением.

Вы также можете получить его с именем "<string>" filename:

$ echo "pining for the fjords" > "<string>"
$ python -c 'wat'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
    pining for the fjords
NameError: name 'wat' is not defined

Есть ли способ предотвратить это поведение или он жестко закодирован в REPL?

Ответы

Ответ 1

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

Когда Python должен печатать трассировку, он пытается найти исходный код, соответствующий всем используемым фреймам стека. Имя файла и номер строки, который вы видите в трассировке стека, все Python должен продолжаться. Если он использовал модуль traceback, путь кода прошел бы через раздел в linecache, который исключает имена файлов, начинающиеся и заканчивающиеся на < и >, но по умолчанию sys.excepthook не проходит этот путь.

default sys.excepthook проходит через собственный вызов PyErr_Display, который в конечном итоге заканчивается использованием _Py_DisplaySourceLine для отображения отдельных строк источника. _Py_DisplaySourceLine безоговорочно пытается найти файл в текущем рабочем каталоге (по какой-то причине - ошибочная оптимизация?), затем вызывает _Py_FindSourceFile для поиска sys.path для файла, соответствующего этому имени, если в рабочем каталоге его нет. Обычно он не находит файл <stdin> или <string>, и он просто пропускает исходный код печати, когда он не может найти файл, но если он находит его, он печатает из этого файла.

Первоначально я думал, что вы можете предотвратить это, запустив Python с -I flag, поставив его в изолированном режиме. Одним из эффектов изолированного режима является удаление каталога script из sys.path. Эксперимент доказал, что это не меняло вещи, а именно, когда я понял, что _Py_DisplaySourceLine пытается запустить рабочий каталог независимо от того, что.

Было бы довольно просто исправить это, исключив <> имена файлов в пути собственного кода, например linecache. Также необходимо изменить код, который безошибочно ищет текущий каталог для файла.