有关SO的相关问题(今天早些时候由我本人):Why does error traceback show edited script instead of what actually ran?现在我知道为什么发生,然后我想现在如何它。
我看到诸如How do I debug efficiently with spyder in Python?和How do I print debug messages in the Google Chrome JavaScript Console?之类的一些问题很受欢迎,所以我想问问有关调试实践的话题是对的,对吧?
我编写了一个脚本,该脚本在 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行 打印(“ Hello World”)
例外
因此它正确地报告了行号(来自原始脚本,因此,如果我们只有经过修改的脚本,则实际上是无用的)和错误消息(“ Exception”)。但是它显示出完全错误的代码行,实际上引发了错误。如果有帮助,应该显示raise Exception
,而不显示print("Hello World")
,这甚至没有由解释器执行。
在实际实践中,我实现了程序的一部分,运行该程序以查看该部分是否运行良好,并且在该程序仍在运行时,我继续进行下一步。当脚本引发错误时,我必须找到导致错误的实际代码行。我通常只是阅读错误消息,然后尝试推断出导致错误的原始代码。
有时候不容易猜到,所以我将脚本复制到剪贴板中,并通过撤消运行脚本后编写的内容来回滚代码,检查导致错误的行,然后从剪贴板粘贴回去。有时,这很烦人,因为在我运行脚本时并不总是能够记住脚本的确切状态。 (“我还需要撤消来回滚吗?或者这是我运行的确切脚本吗?”)
有时,该脚本将运行10分钟以上,甚至一个小时才能引发异常。在这种情况下,“撤消还原”实际上是不可能的。有时,我什至不知道脚本实际运行多长时间。显然,我不能坐下来让脚本在终止之前保持不变。
通过哪种做法,我可以正确地追踪导致异常的命令?
一种假设的解决方案是,每次我要运行脚本时,将脚本复制到一个新文件,运行复制的版本,然后继续编辑原始文件。但是我认为,每当需要运行脚本以查看其是否运行正常时,每十分钟便会感到麻烦。
另一种方法是每次我想运行它时都进行git-commit,这样我可以在需要时返回并查看原始版本,但这会使提交历史非常 ,所以我认为这比另一个更糟糕。
我也尝试过python -m pdb -m script.py
,但它显示了相同的“ n 行的修改版本”,就像普通的回溯一样。
那么,有没有我可以练习的实用解决方案,比如每十分钟一次?
答案 0 :(得分:3)
不必每次运行脚本都提交,而只需使用git stashing,这样就不会在历史记录中添加dirty commits
。
因此,在运行脚本之前,先git stash
进行本地更改,先检查错误,然后再检查git stash pop
。
详细了解git stash here。
此解决方案假定正在运行的脚本位于当前分支的HEAD
如果上述条件不适用,另一种解决方案是创建一个任意分支,将其称为(运行脚本),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
您可以将运行脚本和原始分支作为参数传递给bash文件。
答案 1 :(得分:1)
@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
中的某个位置或临时文件不会妨碍的其他位置。另一个问题是,对于大型代码库而言,这变得更加复杂,您可能不想在各处复制它们。我将那个留给你。
可以为具有Powershell或cmd的Windows设计类似的方法。
答案 2 :(得分:1)
我可能会给出一个过于简单的答案,可能并不适用于所有情况。
使用PyCharm
我通常需要花费几分钟到几小时才能完成的代码,我需要不断运行它以查看其性能,并在运行时继续进行编码。如果失败,我将收到引发错误的原始行。
我还必须在没有GUI的Ubuntu服务器中运行它,因此这是我每次接收正确错误的方式:
答案 3 :(得分:1)
我并不是说会完全避免,但是您可以减少此错误。 如果您将所有逻辑编码在一个文件中,请停止执行。
这里是一些建议。
将您的代码逻辑拆分为多个文件。例子..
使您的函数小到10行(如果可能)
现在,如果发生任何异常,则它的回溯可能会传播到更多文件中,我想并不是所有文件都可以一口气修改以实现您的更改。 好消息是,如果您的异常是从您没有更改的文件开始的,那么它很容易捕获并修复该行,否则,只需很少的精力即可找到确切的行。
如果您还使用 git ,并且尚未提交 ,则还可以比较修订版本以获取可能导致错误的确切代码。
希望这可以最大程度地减少您的问题。