2011-01-28 26 views
53

Tôi đã có một đoạn mã tương tự như sau:Exception traceback bị ẩn nếu không tái huy động ngay lập tức

import sys 

def func1(): 
    func2() 

def func2(): 
    raise Exception('test error') 

def main(): 
    err = None 

    try: 
     func1() 
    except: 
     err = sys.exc_info()[1] 
     pass 

    # some extra processing, involving checking err details (if err is not None) 

    # need to re-raise err so caller can do its own handling 
    if err: 
     raise err 

if __name__ == '__main__': 
    main() 

Khi func2 đặt ra một ngoại lệ tôi nhận được traceback sau:

Traceback (most recent call last): 
    File "err_test.py", line 25, in <module> 
    main() 
    File "err_test.py", line 22, in main 
    raise err 
Exception: test error 

Từ đây tôi không thấy nơi mà ngoại lệ đến từ đâu. Traceback gốc bị mất.

Làm cách nào tôi có thể giữ nguyên lượt truy nguyên gốc và tăng lại nó? Tôi muốn nhìn thấy một cái gì đó tương tự như sau:

Traceback (most recent call last): 
    File "err_test.py", line 26, in <module> 
    main() 
    File "err_test.py", line 13, in main 
    func1() 
    File "err_test.py", line 4, in func1 
    func2() 
    File "err_test.py", line 7, in func2 
    raise Exception('test error') 
Exception: test error 

Trả lời

92

Trống raise tăng ngoại lệ cuối cùng.

# need to re-raise err so caller can do its own handling 
if err: 
    raise 

Nếu bạn sử dụng raise something Python không có cách nào để biết nếu something là một ngoại lệ chỉ bắt trước đó, hoặc một ngoại lệ mới với một chồng dấu vết mới. Đó là lý do tại sao có raise để giữ lại dấu vết ngăn xếp.

Reference here

+2

Đáng nói rằng điều này không hoạt động trong Python 3. – yprez

+8

"Điều này" trong nhận xét của yprez có nghĩa là "tăng trống sau khi rời khỏi khối ngoại trừ". Các "nâng cao" trần không hoạt động trong Python 3 (nhưng chỉ bên trong khối ngoại trừ.) – jtniehof

4

Bạn có thể nhận được rất nhiều thông tin về các ngoại lệ thông qua sys.exc_info() cùng với các mô-đun traceback

thử phần mở rộng sau để mã của bạn.

import sys 
import traceback 

def func1(): 
    func2() 

def func2(): 
    raise Exception('test error') 

def main(): 

    try: 
     func1() 
    except: 
     exc_type, exc_value, exc_traceback = sys.exc_info() 
     # Do your verification using exc_value and exc_traceback 

     print "*** print_exception:" 
     traceback.print_exception(exc_type, exc_value, exc_traceback, 
            limit=3, file=sys.stdout) 

if __name__ == '__main__': 
    main() 

Điều này sẽ in, tương tự như những gì bạn muốn.

*** print_exception: 
Traceback (most recent call last): 
    File "err_test.py", line 14, in main 
    func1() 
    File "err_test.py", line 5, in func1 
    func2() 
    File "err_test.py", line 8, in func2 
    raise Exception('test error') 
Exception: test error 
+0

Không, tôi không muốn in nó trong 'main()'. Tôi muốn nâng cấp lại nó với truy nguyên gốc và để người gọi 'main()' xử lý nó (ví dụ: bỏ qua, in ra bàn điều khiển, lưu vào db, v.v.). Giải pháp của Jochen hoạt động. – parxier

1

Chức năng chính của bạn cần phải trông như thế này:

def main(): 
    try: 
     func1() 
    except Exception, err: 
     # error processing 
     raise 

Đây là cách tiêu chuẩn xử lý (và tái huy động) lỗi. Here is a codepad demonstration.

+0

Tôi có cảm giác rằng 'ngoại trừ ngoại lệ, err: 'có thể được bỏ qua với kiểu cũ" nâng cao "ngoại lệ xấu" 'cách nâng cao ngoại lệ – parxier

+0

@parxier sau đó sử dụng' ngoại trừ đối tượng, err' –

+0

Nó không khác với 'err = sys.exc_info() [1] '. Dù sao thì, điểm chính là tăng lại 'err' bên ngoài khối' except' mà không mất đi lần truy nguyên gốc. Giải pháp của Jochen hoạt động. – parxier

53

Có thể modify and rethrow một ngoại lệ:

Nếu không có biểu thức có mặt, raise lại làm tăng ngoại lệ trước rằng đã hoạt động trong phạm vi hiện tại. Nếu không có ngoại lệ nào hoạt động trong phạm vi hiện tại , ngoại lệ TypeError được nêu rõ rằng đây là lỗi (nếu chạy dưới IDLE, thay vào đó, Queue.Empty ngoại lệ sẽ được nâng lên ).

Nếu không, raise sẽ đánh giá các biểu thức để nhận ba đối tượng, sử dụng None làm giá trị của các biểu thức bị bỏ qua. Hai đối tượng đầu tiên là được sử dụng để xác định loại và giá trị của ngoại lệ.

Nếu một đối tượng thứ ba là hiện tại và không None, nó phải là một đối tượng traceback (xem phần Các hệ thống phân cấp loại tiêu chuẩn), và nó là thay thay vì vị trí hiện tại là nơi ngoại lệ xảy ra.Nếu đối tượng thứ ba có mặt và không phải là một đối tượng traceback hoặc None, ngoại lệ TypeError được nêu ra.

Ba-biểu hình thức raise rất hữu ích để tái huy động một ngoại lệ minh bạch trong một khoản except, nhưng raise không có biểu hiện nên được ưa thích nếu ngoại trừ phải được tái huy động là một ngoại lệ thời gian gần đây hoạt động mạnh nhất trong phạm vi hiện tại.

Vì vậy, nếu bạn muốn thay đổi các ngoại lệ và rethrow nó, bạn có thể làm điều này:

try: 
    buggy_code_which_throws_exception() 
except Exception as e: 
    raise Exception, "The code is buggy: %s" % e, sys.exc_info()[2] 
+0

Thú vị. Câu trả lời được chấp nhận xử lý trường hợp sử dụng của OP tốt hơn điều này, nhưng điều này là thú vị như một câu trả lời chung hơn. Tôi không thể thấy nhiều việc sử dụng cho nó mặc dù từ traceback được thực sự gây hiểu lầm nếu bạn bắt, nói, một 'ValueError' và nâng cao' RuntimeError' (bạn không thể thấy, sau đó, một ValueError đã từng tham gia), và những trường hợp duy nhất tôi gặp cá nhân khi tôi muốn bảo vệ truy nguyên nhưng làm điều gì đó phức tạp hơn chỉ là 'nâng cao 'mà không tranh cãi là trường hợp tôi muốn đưa ra một ngoại lệ của một kiểu khác. –

+4

Tôi đã sử dụng điều này để tính lại cùng một ngoại lệ với một thông báo khác, bao gồm thêm chi tiết về các điều kiện gây ra ngoại lệ, có sẵn trong phạm vi bên ngoài nhưng không có bên trong. – qris

+1

Đôi khi có những lý do rất tốt để sử dụng hình thức biểu hiện ba nâng cao này.Câu trả lời của bạn chỉ giúp tôi viết một trang trí kết thúc tốt đẹp các bài kiểm tra tích hợp và thất bại có một ảnh chụp màn hình. Và sau đó làm tăng sự thất bại khẳng định ban đầu. Vấn đề là việc truy nguyên đã bị che khuất bởi try/excepts trong ảnh chụp màn hình lấy mã. Cảm ơn! – aychedee

1

Trong khi @ câu trả lời Jochen của hoạt động tốt trong các trường hợp đơn giản, nó không phải là khả năng xử lý các trường hợp phức tạp hơn , nơi mà bạn không trực tiếp bắt và rethrowing, nhưng vì một số lý do cho các trường hợp ngoại lệ như là một đối tượng và muốn ném lại trong một bối cảnh hoàn toàn mới (tức là nếu bạn cần phải xử lý nó trong một quá trình khác nhau).

Trong trường hợp này, tôi đề nghị như sau:

  1. được exc_info gốc
  2. định dạng được thông báo lỗi ban đầu, với vết đống
  3. ném một ngoại lệ mới với thông điệp đầy lỗi (stack trace incl.) được nhúng

Trước khi bạn thực hiện việc này, hãy xác định loại ngoại lệ mới mà bạn sẽ quay lại sau ...

class ChildTaskException(Exception): 
    pass 

Trong mã vi phạm ...

import sys 
import traceback 

try: 
    # do something dangerous 
except: 
    error_type, error, tb = sys.exc_info() 
    error_lines = traceback.format_exception(error_type, error, tb) 
    error_msg = ''.join(error_lines) 
    # for example, if you are doing multiprocessing, you might want to send this to another process via a pipe 
    connection.send(error_msg) 

rethrow ...

# again, a multiprocessing example of receiving that message through a pipe 
error_msg = pcon.recv() 
raise ChildTaskException(error_msg) 
Các vấn đề liên quan