Как узнать точную команду, которая вызвала исключение? - PullRequest
3 голосов
/ 03 апреля 2019

Смежный вопрос по SO (сам по себе ранее сегодня): Почему при трассировке ошибок отображается отредактированный скрипт вместо того, что на самом деле выполнялось? Теперь я знаю , почему это происходит, тогда я хочутеперь как Я могу справиться с этим.

Я вижу некоторые вопросы, такие как Как эффективно отлаживать с помощью spyder в Python? и Как распечатать сообщения отладки в консоли Google Chrome JavaScript? хорошо принято,поэтому я полагаю, что вопрос о методах отладки стоит по теме, верно?

Справочная информация

Я пишу скрипт, который вызывает исключение в строке n , запускаю его из терминала, добавляюстрока посередине, пока скрипт еще работает, и сохраните измененный файл.Таким образом, файл сценария изменяется, пока его выполняет интерпретатор.Особенно изменился номер самой строки, которая вызовет исключение.Отчет об отслеживании ошибок интерпретатором Python показывает мне строку n «модифицированной» версии скрипта, а не фактической «работающей» версии.

Минимальный пример

Допустим, я запускаю скрипт:

import time

time.sleep(5)
raise Exception

, и, хотя переводчик застрял на time.sleep(5), я добавляю строку после этого.

Так что теперь у меня есть:

import time

time.sleep(5)
print("Hello World")
raise Exception

Затем интерпретатор выходит из спящего режима, выполняется следующая команда raise Exception, и программа завершается со следующей трассировкой.

Трассировка (последний вызов последним):
Файл "test / minimal_error.py", строка 4, в
print ("Hello World")
Исключение

Таким образом, он правильно сообщает номер строки (из оригинального скрипта, поэтому фактически бесполезен, если у нас есть только модифицированный скрипт) и сообщения об ошибке («Исключение»).Но он показывает совершенно неверную строку кода, которая фактически вызвала ошибку;если бы это было чем-то полезным, должно отображаться raise Exception, а не print("Hello World"), что даже не было выполнено переводчиком.

Почему это важно

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

Иногда это не так просто угадать, поэтому я копирую скрипт в буфер обмена и откатываю код, отменив то, чтонаписано после запуска скрипта, проверьте строку, вызвавшую ошибку, и вставьте обратно из буфера обмена.Иногда это очень раздражает, потому что не всегда можно вспомнить точное состояние скрипта, когда я его запускал.(«Нужно ли отменить больше для отката? Или это именно тот сценарий, который я запустил?»)

Иногда сценарий запускается более 10 минут или даже за час доэто вызывает исключение.В таком случае «откат отменить» практически невозможен.Иногда я даже не знаю, как долго будет работать скрипт, прежде чем он будет запущен.Я, очевидно, не могу просто сидеть и держать свой скрипт без изменений до его завершения.

Вопрос

Каким образом я могу правильно отследить команду, вызвавшую исключение?

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

Другой способ - это git-commit каждый раз, когда я хочу его запустить, так чтоЯ могу вернуться и посмотреть оригинальную версию, когда мне нужно, но это сделает историю коммитов очень грязной, поэтому я думаю, что это даже хуже, чем другая.

Я такжепробовал python -m pdb -m script.py, но он показывает ту же "модифицированную версию строки n ", как и обычная трассировка.

Так есть ли какое-нибудь практическое решение, которое я могу практиковать, скажем, каждые десять минут?

Ответы [ 4 ]

3 голосов
/ 18 апреля 2019

Вместо фиксации при каждом запуске скрипта, просто используйте git stashing, таким образом вы не добавите dirty commits в свою историю.

Итак, прежде чем запускать скрипт, git stash локальные изменения, проверьте ошибку, затем git stash pop.

Подробнее о git stash здесь .

В этом решении предполагается, что запущенный скрипт находится на HEAD текущей ветви,


Другое решение, если вышеприведенное условие не применяется, - это создать произвольную ветвь, вызвать ее (running-script), git stash ваши локальные изменения, которые еще не зафиксированы, checkout для этой новой ветки, git apply stash и запустить скрипт. Затем вернитесь в исходную ветку, снова примените тайник и возобновите работу.

Вы могли бы просто написать файл сценария bash, который автоматизирует этот процесс, следующим образом

git stash
git checkout -b running-script # potential param
git stash apply stash
RUN script # replace with the actual command to run the script in the background
git checkout original-branch # potential param
git stash apply stash

Вы можете передать run-script и original-branch в bash-файл в качестве параметров.

1 голос
/ 24 апреля 2019

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

Вот несколько рекомендаций.

  1. разбейте вашу кодовую логику на несколько файлов.примеры ..

    • утилита,
    • помощник,
    • модель,
    • компонент,
    • поезд,
    • тест,
    • функция
  2. сделать вашу функцию всего 10 строк (если это возможно)

  3. , если выиспользуя класс, который не должен превышать 125 строк
  4. размер файла не должен пересекать 150 строк

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

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

Надеюсь, это минимизирует вашу проблему.

1 голос
/ 24 апреля 2019

Я, вероятно, собираюсь дать упрощенный ответ, и может быть не применим в каждом сценарии.

Использование PyCharm

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

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

  1. Код в Pycharm.
  2. Тестирование в PyCharm и продолжение кодирования. (Я получаю правильную ошибку, если это терпит неудачу)
  3. Как только я доволен производительностью, я перемещаю ее на сервер и запустите его снова (я также получаю правильную ошибку здесь)
1 голос
/ 23 апреля 2019

@ комментарий chepner действителен:

Я почти уверен, что практическое решение - "не делай этого".Не изменяйте исполняемый код.

В качестве относительно простого обходного пути вы можете выполнить это с помощью сценария bash (или аналогичного подхода на основе сценариев в любой среде, которую вы используете, если bash недоступен).

Для bash будет работать скрипт, подобный приведенному ниже.Он принимает имя файла в качестве параметра и использует date для создания уникального временного имени файла, затем копирует файл в него и выполняет его.Таким образом, у вас всегда есть статическая копия работающего кода, и вы можете использовать псевдонимы, чтобы сделать его тривиальным:

filename=$1

# extract file name and extension
extension="${filename##*.}"
filename="${filename%.*}"

# create a unique temporary name (using date)
today=`date +%Y-%m-%d-%H:%M:%S` # or whatever pattern you desire
newname="$filename-$today.$extension"

# copy and run the python script
cp $1 $newname
echo "Executing from $newname..."
/path/to/python $newname

# clean it up when done, if you care to
rm $newname

Затем вы можете присвоить псевдоним этому python, если хотите, чтобы вы не делалине нужно думать об этом, с чем-то вроде этого в вашем .bashrc или .bash_aliases:

alias python="source path/to/copy_execute.sh"

Хотя может быть лучше дать ему другое имя, например

alias mypy="source path/to/copy_execute.sh"

Затем вы можете запустить свой скрипт, модифицировать и запустить еще несколько с mypy myscript.py, и вы никогда не будете редактировать текущий исполняемый код.

Один недостаток заключается в том, что, хотя этот скрипт будет очищать и удалять файлы после завершения работы, он будет создавать много временных файлов, которые будут присутствовать во время работы.Чтобы обойти это, вы всегда можете скопировать куда-нибудь в /tmp или в другое место, где временные файлы не будут мешать.Другая проблема заключается в том, что это становится сложнее для больших баз кода, которые вы можете не копировать повсеместно.Я оставлю это тебе.

Подобный подход может быть создан для Windows с powershell или cmd.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...