Jawaban atas pertanyaan ini bergantung pada versi Python yang Anda gunakan.
Dengan Python 3
Sederhana: pengecualian dilengkapi dengan __traceback__
atribut yang berisi traceback. Atribut ini juga bisa ditulis, dan bisa diatur dengan mudah menggunakan with_traceback
metode pengecualian:
raise Exception("foo occurred").with_traceback(tracebackobj)
Fitur-fitur ini secara minimal dijelaskan sebagai bagian dari raise
dokumentasi.
Semua kredit untuk bagian jawaban ini harus diberikan kepada Vyctor, yang pertama kali memposting informasi ini . Saya memasukkannya di sini hanya karena jawaban ini tertahan di atas, dan Python 3 menjadi lebih umum.
Dengan Python 2
Ini sangat rumit. Masalah dengan tracebacks adalah bahwa mereka memiliki referensi ke frame stack, dan frame stack memiliki referensi ke traceback yang memiliki referensi ke frame stack yang memiliki referensi ke ... Anda mendapatkan idenya. Ini menyebabkan masalah bagi pengumpul sampah. (Terima kasih kepada ecatmur untuk pertama kali menunjukkan ini.)
Cara yang bagus untuk menyelesaikan ini adalah dengan memutus siklus setelah meninggalkan except
klausa, yang dilakukan Python 3. Solusi Python 2 jauh lebih buruk: Anda diberikan fungsi ad-hoc sys.exc_info()
, yang hanya bekerja di dalam except
klausa . Ini mengembalikan tupel yang berisi pengecualian, jenis pengecualian, dan traceback untuk pengecualian apa pun yang saat ini ditangani.
Jadi jika Anda berada di dalam except
klausa, Anda dapat menggunakan output sys.exc_info()
bersama traceback
modul untuk melakukan berbagai hal berguna:
>>> import sys, traceback
>>> def raise_exception():
... try:
... raise Exception
... except Exception:
... ex_type, ex, tb = sys.exc_info()
... traceback.print_tb(tb)
... finally:
... del tb
...
>>> raise_exception()
File "<stdin>", line 3, in raise_exception
Tapi seperti mengedit Anda menunjukkan, Anda mencoba untuk mendapatkan traceback yang akan telah dicetak jika pengecualian Anda belum ditangani, setelah itu telah sudah ditangani. Itu pertanyaan yang jauh lebih sulit. Sayangnya, sys.exc_info
kembali (None, None, None)
jika tidak ada pengecualian yang ditangani. sys
Atribut terkait lainnya juga tidak membantu. sys.exc_traceback
tidak berlaku lagi dan tidak ditentukan saat tidak ada pengecualian yang ditangani; sys.last_traceback
tampak sempurna, tetapi tampaknya hanya ditentukan selama sesi interaktif.
Jika Anda dapat mengontrol bagaimana pengecualian dimunculkan, Anda mungkin dapat menggunakan inspect
dan pengecualian khusus untuk menyimpan beberapa informasi. Tapi saya tidak sepenuhnya yakin bagaimana itu akan berhasil.
Sejujurnya, menangkap dan mengembalikan pengecualian adalah hal yang tidak biasa untuk dilakukan. Ini mungkin pertanda bahwa Anda perlu melakukan refactor.
sys.exc_info
hubungannya dengan pendekatan panggilan balik yang saya sarankan untuk pertanyaan Anda yang lain.Sejak Python 3.0 [PEP 3109] kelas bawaan
Exception
memiliki__traceback__
atribut yang berisitraceback object
(dengan Python 3.2.3):Masalahnya adalah setelah Googling
__traceback__
untuk beberapa saat saya hanya menemukan beberapa artikel tetapi tidak ada yang menjelaskan apakah atau mengapa Anda harus (tidak) menggunakan__traceback__
.Namun, dokumentasi Python 3 untuk
raise
mengatakan bahwa:Jadi saya berasumsi itu dimaksudkan untuk digunakan.
sumber
__
dalam nama yang menunjukkan bahwa ini adalah detail implementasi, bukan milik publik?__foo
adalah metode pribadi tetapi__foo__
(dengan garis bawah juga) adalah metode "ajaib" (dan bukan pribadi).__traceback__
atribut ini 100% aman digunakan sesuka Anda, tanpa implikasi GC. Sulit untuk mengatakannya dari dokumentasinya, tapi ecatmur menemukan bukti kuat .Cara untuk mendapatkan traceback sebagai string dari objek pengecualian di Python 3:
traceback.format_tb(...)
mengembalikan daftar string.''.join(...)
menggabungkan mereka bersama. Untuk referensi lebih lanjut, silakan kunjungi: https://docs.python.org/3/library/traceback.html#traceback.format_tbsumber
Sebagai tambahan, jika Anda benar-benar ingin mendapatkan pelacakan balik penuh seperti yang Anda lihat dicetak ke terminal Anda, Anda menginginkan ini:
Jika Anda menggunakan
format_tb
seperti di atas jawaban menyarankan Anda akan mendapatkan lebih sedikit informasi:sumber
etype=type(exc)
dapat dihilangkan sekarang btw: "Berubah di versi 3.5: Argumen etype diabaikan dan disimpulkan dari jenis nilainya." docs.python.org/3.7/library/… Diuji dengan Python 3.7.3.Ada alasan yang sangat bagus mengapa pelacakan tidak disimpan dalam pengecualian; karena traceback menyimpan referensi ke stack lokal-nya, hal ini akan mengakibatkan referensi melingkar dan memori (sementara) bocor hingga GC melingkar masuk. (Inilah mengapa Anda tidak boleh menyimpan traceback dalam variabel lokal .)
Tentang satu-satunya hal yang dapat saya pikirkan adalah bagi Anda untuk monkeypatch
stuff
's global sehingga ketika mengira itu menangkapnyaException
sebenarnya menangkap tipe khusus dan pengecualian menyebar ke Anda sebagai pemanggil:sumber
e.__traceback__
.except
blok, sesuai PEP 3110 .