我通过exec
制作了一些功能,可能会出错。但是,当Python出错时,它不会显示发生错误的行。
例如使用:
fn_str = '''\
def fn():
raise Exception()
'''
globs = {}
exec(fn_str, globs)
fn = globs['fn']
fn()
给我们输出:
Traceback (most recent call last):
File "...", line 10, in <module>
fn()
File "<string>", line 2, in fn
Exception
但是,如果我们不使用eval。然后我们得到程序错误的行:
def fn():
raise Exception()
fn()
Traceback (most recent call last):
File "...", line 4, in <module>
fn()
File "...", line 2, in fn
raise Exception()
Exception
我已经考虑过使用__traceback__
,但是我无法找到一种方法来添加回文件&#39;文件&#39;线。所以我能得到的最好的是:
fn_str = '''\
def fn():
try:
raise Exception()
except BaseException as e:
tb = e.__traceback__
if 1 <= tb.tb_lineno <= len(fn_lines):
e.args = ((e.args[0] if e.args else '') + ' - ' + fn_lines[tb.tb_lineno - 1].strip(),)
raise
'''
globs = {'fn_lines': fn_str.split('\n')}
exec(fn_str, globs)
fn = globs['fn']
fn()
Traceback (most recent call last):
File "...", line 16, in <module>
fn()
File "<string>", line 3, in fn
Exception: - raise Exception()
最大的问题是如果eval
调用其他代码,那么- raise Exception()
来自哪里会让人感到困惑。
有没有办法让eval代码提供它错误的行?
答案 0 :(得分:2)
当解释器无法找到源代码时会发生这种情况
那条线无论出于何种原因。这是内置模块的情况,
编译文件,sudo setxkbmap -model thinkpad60 -layout br
字符串等。更具体地说,在回溯中
您可以看到exec
的代码对象的文件名设置为
fn
<string>
因为File "<string>", line 2, in fn
不是有效的文件名,所以引用了
源代码丢失了。
一个选项是创建一个临时文件,在那里写<string>
,
编译fn_str
来设置文件名,执行编译后的代码,然后
终于调用了这个函数。请注意,您需要保持文件存活
至少直到源行被traceback-printing缓存
设施
fn_str
打印
from tempfile import NamedTemporaryFile
import traceback
with NamedTemporaryFile('w') as temp:
code = compile(fn_str, temp.name, 'exec')
print(fn_str, file=temp, flush=True)
globs = {}
exec(code, globs)
fn = globs['fn']
try:
fn()
except:
traceback.print_exc()
由于我们已经创建了一个“真实”文件,我们可以委托
编译和执行runpy.run_path
的代码:
Traceback (most recent call last):
File "test.py", line 16, in <module>
fn()
File "/tmp/tmp9q2bogm6", line 2, in fn
raise Exception()
Exception
答案 1 :(得分:2)
缺少的行表明了Python无法在名为const path = require('path');
const express = require('express');
const multer = require('multer');
const ejs = require('ejs');
const bodyParser = require('body-parser');
const mysql = require('mysql');
const app = express();
// Ensure this folder exists
const DIR = './assets/uploads/featured-img';
let storage = multer.diskStorage({
destination: function (req, file, callback) {
callback(null, DIR);
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname));
}
});
let upload = multer({storage: storage});
app.use(express.static('assets'));
app.set('view engine', 'ejs');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extend: false}));
app.get('/',(req, res) => {
res.render('music_index');
});
app.post('/save',upload.single('featured_img'), function (req, res) {
let sql = "INSERT INTO `music`(`featured_img`, `title`, `band_name`, `audio`) VALUES ('" + req.file.path + "', '"+req.body.title+"', '"+req.body.band_name+"', '"+req.body.audio+"')";
console.log(sql);
});
const port = 3000;
app.listen(port, () => console.log(`Server started on port ${port}`));
的磁盘上找到文件的事实,该文件是编译后的代码片段中内置的文件名。 (尽管如果您创建的文件名与此名称完全相同,Python将从其中打印行!)
方法1。您可以自己捕获异常,无论是在应用程序的顶层还是在其他地方,您都可以调用标准库例程{{1 }}从标准库模块<string>
中提取行。由于traceback.print_exc()
缓存是一个简单的公共Python字典,因此您可以使用它需要打印的源代码预先填充它。参见:
Why does the Python linecache affect the traceback module but not regular tracebacks?
结果代码:
linecache
方法2。您还可以通过自己直接打印异常来避免填充全局缓存的间接行为:您可以让Python将回溯数据作为元组列表返回,逐步完成他们添加缺少的行,然后照常打印它们。
这是一个linecache
函数,该函数在一个打印完整回溯的小程序中用缺少的信息填充回溯:
import linecache
import traceback
source = 'print("Hello, world" + 1)'
source_name = 'Example'
lines = source.splitlines(True)
linecache.cache[source_name] = len(source), None, lines, source_name
compiled = compile(source, source_name, 'exec')
try:
eval(compiled)
except Exception:
traceback.print_exc()
在这里我可以使用化名“ Example”,因为我使用fill_in_lines()
对其进行了设置。在您的情况下,您想将裸字符串import sys
import traceback
def fill_in_lines(frames, source_name, source):
lines = source.splitlines()
for filename, line_number, function_name, text in frames:
if filename == source_name:
text = lines[line_number - 1]
yield filename, line_number, function_name, text
source = 'print("Hello, world" + 1)'
source_name = 'Example'
compiled = compile(source, source_name, 'exec')
try:
eval(compiled)
except Exception as e:
_, _, tb = sys.exc_info()
frames = traceback.extract_tb(tb)
frames = fill_in_lines(frames, source_name, source)
print('Traceback (most recent call last):')
print(''.join(traceback.format_list(frames)), end='')
print('{}: {}'.format(type(e).__name__, str(e)))
作为compile()
参数传递。
答案 2 :(得分:-1)
这是vaultah的deleted answer的转发。
当解释器找不到源代码时会发生这种情况 不管出于什么原因内置模块就是这种情况, 编译文件,
exec
字符串等。更具体地说,在回溯中 您可以看到fn
的代码对象的文件名设置为<string>
File "<string>", line 2, in fn
由于
<string>
不是有效的文件名,因此对 源代码丢失。一种选择是创建一个临时文件,在其中写入
fn_str
, 编译fn_str
来设置文件名,执行编译后的代码,然后 最后调用该函数。请注意,您需要使文件保持活动状态 至少直到源行被traceback-printing缓存为止 设施from tempfile import NamedTemporaryFile import traceback with NamedTemporaryFile('w') as temp: code = compile(fn_str, temp.name, 'exec') print(fn_str, file=temp, flush=True) globs = {} exec(code, globs) fn = globs['fn'] try: fn() except: traceback.print_exc()
打印
Traceback (most recent call last): File "test.py", line 16, in <module> fn() File "/tmp/tmp9q2bogm6", line 2, in fn raise Exception() Exception
由于我们已经创建了一个“真实”文件,因此我们可以委派 编译并执行
runpy.run_path
的代码:from tempfile import NamedTemporaryFile import runpy, traceback with NamedTemporaryFile('w') as temp: print(fn_str, file=temp, flush=True) fn = runpy.run_path(temp.name)['fn'] try: fn() except: traceback.print_exc()