Trong Python, làm cách nào để bắt các cảnh báo như thể chúng là ngoại lệ?


103

Thư viện của bên thứ ba (được viết bằng C) mà tôi sử dụng trong mã python của mình đang đưa ra cảnh báo. Tôi muốn có thể sử dụng try exceptcú pháp để xử lý đúng các cảnh báo này. Có cách nào để làm việc này không?


2
Có phải những cảnh báo đó chỉ là tin nhắn văn bản được viết do stderr?
Fenikso

1
Fenikso: Tôi không biết chắc chắn, có vẻ như cảnh báo thực
Boris Gorelik

1
Làm thế nào để bạn nhận ra "cảnh báo thực sự"? Tôi nghĩ rằng trong C bạn nhận được cảnh báo thực sự trong quá trình biên dịch.
Fenikso

warnings.filterwarningschính xác những gì bạn muốn, tôi không hiểu vấn đề của bạn với nó là gì?
Rosh Oxymoron

4
@Fenikso, @Rosh Oxymoron, bạn đã đúng. Lỗi của tôi. warnings.filterwarnigns('error')Làm công việc. Tôi không thể tìm thấy câu trả lời ban đầu mà đề xuất giải pháp này
Boris Gorelik

Câu trả lời:


51

Để trích dẫn từ cẩm nang python ( 27.6.4. Cảnh báo thử nghiệm ):

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)

6
Đây là câu trả lời cho bạn biết cách sử dụng try exceptcú pháp.
Unapiedra

Điều này có lợi thế, so với câu trả lời của niekas, rằng nếu fnxtrả về một cái gì đó, bạn sẽ giữ kết quả đó (và vẫn có thể quản lý cảnh báo).
Pietro Battiston

Điều này không trả lời cho câu hỏi của OP, đó là về việc xử lý các dây, không phải kiểm tra chúng. Tuy nhiên, câu trả lời của niekas dưới đây cho thấy cách xử lý các cảnh báo.
Biggsy

Chỉ cần lưu ý rằng chức năng trên sẽ không hoạt động nếu hàm của bạn chỉ trả về cảnh báo không liên tục vì trong trường hợp fxn()không trả về cảnh báo, khi đó wsẽ là một danh sách trống. Nếu wlà một danh sách rỗng (tức là []), sau đó chạy mã sẽ cung cấp cho bạn các lỗi sau: IndexError: list index out of range. Nếu bạn chỉ muốn định dạng hoặc kiểm tra thuộc tính của các cảnh báo đã chụp, thì tốt hơn nên sử dụng vòng lặp for:for x in w: print(f'{x.category.__name__}: {str(x.message)}')
Steven M. Mortimer

130

Để xử lý cảnh báo là lỗi, chỉ cần sử dụng điều này:

import warnings
warnings.filterwarnings("error")

Sau đó, bạn sẽ có thể nhận được các cảnh báo giống như lỗi, ví dụ như điều này sẽ hoạt động:

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

PS Đã thêm câu trả lời này vì câu trả lời hay nhất trong các nhận xét chứa lỗi chính tả: filterwarnignsthay vì filterwarnings.


8
Và nếu bạn chỉ muốn xem dấu vết ngăn xếp, hai dòng đầu tiên là tất cả những gì bạn cần.
z0r

5
Đây là hoàn hảo. Tôi chỉ muốn tập lệnh của mình ngừng thực thi ngay sau khi cảnh báo được đưa ra để tôi có thể in thông tin gỡ lỗi có liên quan và khắc phục sự cố.
Praveen

1
Bạn không cần lệnh filterwarningsgọi để bắt Warnings, ít nhất là trong python 3. nó chỉ hoạt động.
naught101

1
Câu trả lời được chấp nhận không trả lời câu hỏi của OP. Câu trả lời này không. Đây là câu trả lời tôi đã tìm kiếm khi tìm kiếm của tôi tìm thấy câu hỏi này.
Biggsy


15

Đây là một biến thể giúp bạn hiểu rõ hơn về cách hoạt động chỉ với các cảnh báo tùy chỉnh.

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)

4

Trong một số trường hợp, bạn cần sử dụng ctypes để biến cảnh báo thành lỗi. Ví dụ:

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

Câu trả lời này chỉ mang tính xây dựng để chỉ ra cách chỉ lỗi trong một số loại cảnh báo nhất định. Đối với hầu hết bất kỳ dự án phần mềm lớn nào, nếu bạn làm vậy, warnings.simplefilter('error')bạn sẽ không nhận được dấu vết cho cảnh báo bạn đã thấy trong nhật ký, nhưng thay vào đó nhận được dấu vết từ các cảnh báo đã lọc trước đó. Sử dụng simplefiltercũng là cách nhanh nhất để đi đến câu trả lời của bạn nếu bạn có một số yêu cầu CLI.
AlanSE
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.