Python contextlib.suppress: กลืนเฉพาะ error ที่ขั้นตอนนั้นยอมรับได้
งาน cleanup แบบ idempotent มักยอมรับได้ถ้าไฟล์หายไปแล้ว แต่ไม่ควรกลืน permission error หรือ disk failure การเขียน try/except Exception: pass ทำให้ระบบดูเหมือนสำเร็จทั้งที่มีปัญหาจริง contextlib.suppress ทำให้เราเขียนขอบเขตสั้น ๆ และระบุ exception ที่ตั้งใจเพิกเฉยได้ตรงจุดครับ
from collections.abc import Callable
from contextlib import suppress
import unittest
def remove_if_present(remove: Callable[[], None]) -> None:
with suppress(FileNotFoundError):
remove()
class SuppressTests(unittest.TestCase):
def test_missing_resource_is_already_clean(self):
def missing() -> None:
raise FileNotFoundError("gone")
remove_if_present(missing)
def test_permission_problem_is_not_hidden(self):
def denied() -> None:
raise PermissionError("read-only")
with self.assertRaises(PermissionError):
remove_if_present(denied)
if __name__ == "__main__":
unittest.main()
ชื่อ exception คือ policy ของฟังก์ชัน: FileNotFoundError หมายถึง “ปลายทางที่ไม่มีถือว่าสะอาดแล้ว” แต่ PermissionError หมายถึง “เรายังไม่รู้ว่า cleanup สำเร็จหรือไม่” จึงต้องส่งต่อให้ผู้เรียกตัดสินใจ การครอบโค้ดยาวหลายบรรทัดทำให้ไม่ชัดว่า exception มาจากคำสั่งใด ควรจำกัด with suppress(...) ให้เล็กที่สุด
ถ้าต้องบันทึก warning, เพิ่ม metric หรือคืนสถานะว่าเกิดอะไรขึ้น ใช้ try/except ปกติจะชัดกว่า suppress และหาก exception บ่งชี้ bug เช่น TypeError อย่าเพิ่มเข้า list เพียงเพื่อให้ job ผ่าน การกลืน error ควรเป็นผลจากสัญญาของ operation ไม่ใช่ทางลัดลดเสียง log
Recap
- suppress เฉพาะ exception ที่เท่ากับสถานะสำเร็จของ operation
- ครอบ statement ให้น้อยที่สุด
- เทสต์ว่า exception อื่นยัง propagate
- ใช้
try/exceptเมื่อจำเป็นต้อง log หรือคืนสถานะ