文件&#34; <string>&#34;跟踪线预览

时间:2017-11-08 15:16:58

标签: python python-3.x traceback

我通过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代码提供它错误的行?

3 个答案:

答案 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)

这是vaultahdeleted 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()