วิธีบันทึกค่า traceback / sys.exc_info () ในตัวแปร


127

ฉันต้องการบันทึกชื่อของข้อผิดพลาดและรายละเอียดการย้อนกลับลงในตัวแปร นี่คือความพยายามของฉัน

import sys
try:
    try:
        print x
    except Exception, ex:
        raise NameError
except Exception, er:
    print "0", sys.exc_info()[0]
    print "1", sys.exc_info()[1]
    print "2", sys.exc_info()[2]

เอาท์พุท:

0 <type 'exceptions.NameError'>
1 
2 <traceback object at 0xbd5fc8>

ผลลัพธ์ที่ต้องการ:

0 NameError
1
2 Traceback (most recent call last):
  File "exception.py", line 6, in <module>
    raise NameError

ป.ล. ฉันรู้ว่าสิ่งนี้สามารถทำได้อย่างง่ายดายโดยใช้โมดูลการย้อนกลับ แต่ฉันต้องการทราบการใช้งานวัตถุ sys.exc_info () [2] ที่นี่


คุณลองพิมพ์ sys.exc_info () [x] .__ str __ () หรือไม่
zmbq

3
คุณอาจเข้าใจผิดว่าเกิดอะไรขึ้นในโปรแกรมของคุณ: สิ่งที่คุณอ้างถึงว่า "sys.exc_info () [2] object" คืออินสแตนซ์ของวัตถุ traceback (= คุณใช้โมดูล traceback อยู่แล้ว) ตอนนี้คุณสามารถจัดการกับวัตถุนั้นได้โดยไม่ต้องใช้ฟังก์ชันตัวช่วยในโมดูลการย้อนกลับ แต่นั่นไม่ได้เปลี่ยนความจริงที่ว่าคุณยังใช้งานอยู่ :)
mac

1
@mac โปรดช่วยฉันใช้การเข้าถึงค่าจากวัตถุนี้โดยใช้หรือไม่ใช้ฟังก์ชันตัวช่วย
codersofthedark

1
@dragosrsupercool - ดังที่ได้กล่าวไว้ในคำตอบด้านล่างนี้คุณควรดูเอกสารการย้อนกลับ ฉันให้ตัวอย่างวิธีดึงข้อมูลแบบข้อความ แต่มีวิธีการอื่น ๆ ของวัตถุที่อนุญาตให้คุณแยกชื่อข้อยกเว้นแถวของรหัส ฯลฯ ... อันที่ถูกต้องขึ้นอยู่กับว่าคุณต้องการจัดการกับ มูลค่าหลังจากนั้น ...
แม็ค

1
คำตอบของฉันสำหรับคำถามอื่นอาจช่วยอธิบายรายละเอียด - พร้อมลิงก์! สำหรับสตริงกระป๋องโมดูลการย้อนกลับไลบรารีมาตรฐานดูเหมือนจะใช้ได้ หากคุณต้องการรับรายละเอียดโปรดอ่านแหล่งที่มา ( <python install path>/Lib/traceback.py) สำหรับข้อมูลเพิ่มเติม
pythonlarry

คำตอบ:


180

นี่คือวิธีที่ฉันทำ:

>>> import traceback
>>> try:
...   int('k')
... except:
...   var = traceback.format_exc()
... 
>>> print var
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ValueError: invalid literal for int() with base 10: 'k'

อย่างไรก็ตามคุณควรดูเอกสารการย้อนกลับเนื่องจากคุณอาจพบว่ามีวิธีการที่เหมาะสมกว่านี้ขึ้นอยู่กับว่าคุณต้องการประมวลผลตัวแปรของคุณอย่างไรในภายหลัง ...


1
ฉันกำลังมองหาวิธีการโดยไม่ต้องใช้โมดูลการย้อนกลับ มีบางครั้งที่เราสามารถพิมพ์รายละเอียดย้อนกลับจากการอ้างอิงวัตถุนี้ได้หรือไม่? sys.exc_info () [2]
codersofthedark

ใช่นั่นคือเหตุผลที่ฉันคิดว่าเราสามารถทำบางอย่างเช่น sys.exc_info () [2] .format_exc () แต่มันไม่ได้ผล .. ดังนั้นฉันสงสัยว่าฉันจะดึงค่าจากวัตถุย้อนกลับได้อย่างไร sys.exc_info () [2] ความคิดใด ๆ ?
codersofthedark

1
sys.exc_info () [2] .tb_text ให้ข้อผิดพลาดตาม -> AttributeError: วัตถุ 'traceback' ไม่มีแอตทริบิวต์ 'tb_text'
codersofthedark

4
@dragosrsupercool - sys.exc_info()[2].tb_frame.f_code.co_names[3]แต่มันไม่สมเหตุสมผลเลย ... หากมีโมดูลที่เรียกว่าtracebackในไลบรารีมาตรฐานมีเหตุผลสำหรับมัน ... :)
แมค

2
@codersofthedark traceback.format_exception(*sys.exc_info())เป็นวิธีที่จะทำ แต่เทียบเท่ากับฟังก์ชันtraceback.format_exc().
wizzwizz4

25

sys.exc_info () ส่งคืนทูเปิลที่มีค่าสามค่า (ประเภทค่าการติดตามย้อนกลับ)

  1. ประเภทที่นี่รับประเภทข้อยกเว้นของข้อยกเว้นที่กำลังจัดการ
  2. ค่าคืออาร์กิวเมนต์ที่ถูกส่งไปยังตัวสร้างของคลาสข้อยกเว้น
  3. การย้อนกลับมีข้อมูลสแต็กเช่นที่ซึ่งเกิดข้อยกเว้นเป็นต้น

ตัวอย่างเช่นในโปรแกรมต่อไปนี้

try:

    a = 1/0

except Exception,e:

    exc_tuple = sys.exc_info()

ตอนนี้ถ้าเราพิมพ์ทูเปิลค่าจะเป็นเท่านี้

  1. ค่า exc_tuple [0] จะเป็น " ZeroDivisionError "
  2. exc_tuple [1] ค่าจะเป็น "การหารจำนวนเต็มหรือโมดูโลโดยศูนย์ " (สตริงที่ส่งผ่านเป็นพารามิเตอร์ไปยังคลาสข้อยกเว้น)
  3. ค่า exc_tuple [2] จะเป็น " วัตถุ trackback ที่ (ที่อยู่หน่วยความจำบางส่วน) "

นอกจากนี้ยังสามารถดึงรายละเอียดข้างต้นได้ด้วยการพิมพ์ข้อยกเว้นในรูปแบบสตริง

print str(e)

สำหรับ Python3 exc_tuple [1] (aka value) คืออินสแตนซ์ของข้อยกเว้นไม่ใช่ "สตริงที่ส่งผ่านเป็นพารามิเตอร์" ดู: docs.python.org/3/library/sys.html#sys.exc_info
Shi

มันควรจะเป็น "except Exception as e:" ไม่ใช่หรือ?
Henrik

20

ใช้traceback.extract_stack()หากคุณต้องการเข้าถึงชื่อโมดูลและฟังก์ชันและหมายเลขบรรทัดที่สะดวก

ใช้''.join(traceback.format_stack())ถ้าคุณต้องการเพียงแค่สตริงที่ดูเหมือนtraceback.print_stack()เอาต์พุต

ขอให้สังเกตว่าแม้จะมี''.join()คุณจะได้รับสตริงหลายคู่สายเนื่องจากองค์ประกอบของประกอบด้วยformat_stack() \nดูผลลัพธ์ด้านล่าง

อย่าลืมimport traceback.

นี่คือผลลัพธ์จากtraceback.extract_stack(). เพิ่มการจัดรูปแบบเพื่อให้อ่านง่าย

>>> traceback.extract_stack()
[
   ('<string>', 1, '<module>', None),
   ('C:\\Python\\lib\\idlelib\\run.py', 126, 'main', 'ret = method(*args, **kwargs)'),
   ('C:\\Python\\lib\\idlelib\\run.py', 353, 'runcode', 'exec(code, self.locals)'),
   ('<pyshell#1>', 1, '<module>', None)
]

นี่คือผลลัพธ์จาก''.join(traceback.format_stack()). เพิ่มการจัดรูปแบบเพื่อให้อ่านง่าย

>>> ''.join(traceback.format_stack())
'  File "<string>", line 1, in <module>\n
   File "C:\\Python\\lib\\idlelib\\run.py", line 126, in main\n
       ret = method(*args, **kwargs)\n
   File "C:\\Python\\lib\\idlelib\\run.py", line 353, in runcode\n
       exec(code, self.locals)\n  File "<pyshell#2>", line 1, in <module>\n'

4

โปรดใช้ความระมัดระวังเมื่อคุณนำวัตถุข้อยกเว้นหรือวัตถุย้อนกลับออกจากตัวจัดการข้อยกเว้นเนื่องจากจะทำให้เกิดการอ้างอิงแบบวงกลมและgc.collect()จะไม่สามารถรวบรวมได้ สิ่งนี้ดูเหมือนจะเป็นปัญหาเฉพาะในสภาพแวดล้อมโน้ตบุ๊ก ipython / jupyter ซึ่งอ็อบเจ็กต์การย้อนกลับไม่ได้รับการล้างในเวลาที่เหมาะสมและแม้แต่การเรียกที่ชัดเจนไปยังgc.collect()ในfinallyส่วนก็ไม่ทำอะไรเลย และนั่นเป็นปัญหาใหญ่หากคุณมีวัตถุขนาดใหญ่ที่ไม่ได้รับการเรียกคืนหน่วยความจำเนื่องจากสิ่งนั้น (เช่น CUDA จากข้อยกเว้นหน่วยความจำที่ไม่มีโซลูชันนี้จำเป็นต้องรีสตาร์ทเคอร์เนลทั้งหมดเพื่อกู้คืน)

โดยทั่วไปถ้าคุณต้องการบันทึกวัตถุย้อนกลับคุณต้องล้างจากการอ้างอิงถึงlocals()ดังนี้:

import sys, traceback, gc
type, val, tb = None, None, None
try:
    myfunc()
except:
    type, val, tb = sys.exc_info()
    traceback.clear_frames(tb)
# some cleanup code
gc.collect()
# and then use the tb:
if tb:
    raise type(val).with_traceback(tb)

ในกรณีของสมุดบันทึก jupyter คุณต้องทำอย่างน้อยที่สุดภายในตัวจัดการข้อยกเว้น:

try:
    myfunc()
except:
    type, val, tb = sys.exc_info()
    traceback.clear_frames(tb)
    raise type(val).with_traceback(tb)
finally:
    # cleanup code in here
    gc.collect()

ทดสอบด้วย python 3.7

ps ปัญหาเกี่ยวกับ ipython หรือ jupyter notebook env คือมันมี%tbเวทย์มนตร์ที่บันทึกการย้อนกลับและทำให้พร้อมใช้งานในภายหลัง และด้วยเหตุนี้locals()ทุกเฟรมที่เข้าร่วมในการย้อนกลับจะไม่ถูกปล่อยให้ว่างจนกว่าโน้ตบุ๊กจะออกหรือข้อยกเว้นอื่นจะเขียนทับ backtrace ที่เก็บไว้ก่อนหน้านี้ นี่เป็นปัญหามาก ไม่ควรจัดเก็บการย้อนกลับโดยไม่ทำความสะอาดเฟรม ส่งการแก้ไขที่นี่แล้ว


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.