Dengan Python, bagaimana seseorang menangkap peringatan seolah-olah itu adalah pengecualian?

103

Perpustakaan pihak ketiga (ditulis dalam C) yang saya gunakan dalam kode python saya mengeluarkan peringatan. Saya ingin dapat menggunakan try exceptsintaks untuk menangani peringatan ini dengan benar. Apakah ada cara untuk melakukan ini?

Boris Gorelik
sumber
2
Apakah peringatan itu hanya pesan teks yang ditulis do stderr?
Fenikso
1
Fenikso: Saya tidak tahu pasti, sepertinya peringatan yang nyata
Boris Gorelik
1
Bagaimana Anda mengenali "peringatan nyata"? Saya pikir di C Anda mendapatkan peringatan nyata selama kompilasi.
Fenikso
warnings.filterwarningsmelakukan apa yang Anda inginkan, saya tidak mengerti apa masalah Anda?
Rosh Oxymoron
4
@Fenikso, @Rosh Oxyoron Anda benar. Kesalahanku. warnings.filterwarnigns('error')melakukan pekerjaan itu. Saya tidak dapat menemukan jawaban asli yang mengusulkan solusi ini
Boris Gorelik

Jawaban:

51

Mengutip dari buku pegangan python ( 27.6.4. Peringatan Pengujian ):

import warnings

def fxn():
    warnings.warn("deprecated", DeprecationWarning)

with warnings.catch_warnings(record=True) as w:
    # Cause all warnings to always be triggered.
    warnings.simplefilter("always")
    # Trigger a warning.
    fxn()
    # Verify some things
    assert len(w) == 1
    assert issubclass(w[-1].category, DeprecationWarning)
    assert "deprecated" in str(w[-1].message)
Bobby Powers
sumber
6
Inilah jawabannya, yang memberi tahu Anda cara menggunakan try exceptsintaks.
Unapiedra
Ini memiliki keuntungan, dibandingkan jawaban niekas, bahwa jika fnxmengembalikan sesuatu, Anda menyimpan hasil itu (dan masih bisa mengatur peringatan).
Pietro Battiston
Ini tidak menjawab pertanyaan OP, yaitu tentang menangani wanrings, bukan mengujinya. Namun, jawaban niekas di bawah ini menunjukkan bagaimana menangani peringatan.
Biggsy
Sekadar catatan bahwa fungsi di atas tidak akan berfungsi jika fungsi Anda hanya sesekali mengembalikan peringatan karena jika fxn()tidak mengembalikan peringatan, maka wakan menjadi daftar kosong. Jika wadalah daftar kosong (yaitu []), kemudian menjalankan kode akan memberikan error berikut: IndexError: list index out of range. Jika Anda hanya ingin memformat atau memeriksa properti peringatan yang ditangkap, maka lebih baik menggunakan for-loop:for x in w: print(f'{x.category.__name__}: {str(x.message)}')
Steven M. Mortimer
130

Untuk menangani peringatan sebagai kesalahan cukup gunakan ini:

import warnings
warnings.filterwarnings("error")

Setelah ini Anda akan dapat menangkap peringatan yang sama dengan kesalahan, misalnya ini akan berfungsi:

try:
    some_heavy_calculations()
except RuntimeWarning:
    import ipdb; ipdb.set_trace()

PS Menambahkan jawaban ini karena jawaban terbaik dalam komentar mengandung kesalahan eja: filterwarnignsbukan filterwarnings.

niekas
sumber
8
Dan jika Anda hanya ingin melihat jejak tumpukan, Anda hanya membutuhkan dua baris pertama.
z0r
5
Ini sempurna. Saya hanya ingin skrip saya menghentikan eksekusi segera setelah peringatan dikeluarkan, sehingga saya dapat mencetak informasi debug yang relevan dan memperbaiki masalah.
Praveen
1
Anda tidak perlu filterwarningspanggilan untuk menangkap Warnings, setidaknya di python 3. itu hanya berfungsi.
n nothing101
1
Jawaban yang diterima tidak menjawab pertanyaan OP. Jawaban ini bisa. Ini adalah jawaban yang saya cari ketika pencarian saya menemukan pertanyaan ini.
Biggsy
15

Berikut adalah variasi yang memperjelas cara bekerja hanya dengan peringatan khusus Anda.

import warnings
with warnings.catch_warnings(record=True) as w:
    # Cause all warnings to always be triggered.
    warnings.simplefilter("always")

    # Call some code that triggers a custom warning.
    functionThatRaisesWarning()

    # ignore any non-custom warnings that may be in the list
    w = filter(lambda i: issubclass(i.category, UserWarning), w)

    if len(w):
        # do something with the first warning
        email_admins(w[0].message)
mcqwerty.dll
sumber
4

Dalam beberapa kasus, Anda perlu menggunakan ctypes untuk mengubah peringatan menjadi kesalahan. Sebagai contoh:

str(b'test')  # no error
import warnings
warnings.simplefilter('error', BytesWarning)
str(b'test')  # still no error
import ctypes
ctypes.c_int.in_dll(ctypes.pythonapi, 'Py_BytesWarningFlag').value = 2
str(b'test')  # this raises an error
Collin Anderson
sumber
Jawaban ini konstruktif hanya untuk menunjukkan bagaimana melakukan kesalahan hanya pada jenis peringatan tertentu. Untuk hampir semua proyek perangkat lunak besar, jika Anda melakukannya, warnings.simplefilter('error')Anda tidak akan mendapatkan traceback untuk peringatan yang Anda lihat di log, melainkan mendapatkan traceback dari peringatan yang difilter sebelumnya. Menggunakan simplefilterjuga merupakan cara tercepat untuk mendapatkan jawaban Anda jika Anda memiliki beberapa permintaan CLI.
AlanSE