นากมิ้นต์ต้นฉบับแขวนป้ายคิวเก็บกวาดที่ประตูทางออกห้องแล็บ ก่อนปิดไฟดวงสุดท้าย

Python atexit: จองคิวเก็บกวาดก่อนโปรเซสจบ

น้องกายEngineering

สกิลป์ต์สั้นๆ ที่เปิดไฟล์ชั่วคราวหรือล็อกพอร์ต มักลืมปิดของตอนจบครับ atexit.register ใส่ฟังก์ชันเข้าคิวให้ interpreter เรียกก่อนจบแบบปกติ ลำดับเป็น LIFO — ตัวที่ลงทะเบียนทีหลังถูกเรียกก่อน

WHY: cleanup ที่ไม่ต้องห่อ try/finally ทั้ง main

เหมาะกับปลั๊กอินหรือไลบรารีที่ไม่ได้ควบคุม main ของคนเรียก แต่ต้องคืนทรัพยากร เอกสารบอกชัดว่า handler จะไม่รันถ้าโปรเซสถูกฆ่าด้วยสัญญาณที่ไม่อนุญาตให้ cleanup หรือถ้าเรียก os._exit

import atexit
import unittest


class AtexitRegisterTests(unittest.TestCase):
    def test_register_runs_lifo_and_unregister(self):
        log = []

        def first():
            log.append("first")

        def second():
            log.append("second")

        atexit.register(first)
        atexit.register(second)
        # Call the private runner used by the interpreter shutdown path in tests.
        atexit._run_exitfuncs()
        self.assertEqual(log, ["second", "first"])

        log.clear()
        atexit.register(first)
        atexit.unregister(first)
        atexit._run_exitfuncs()
        self.assertEqual(log, [])


if __name__ == "__main__":
    unittest.main()

ข้อจำกัดที่ควรรู้

atexit ไม่ใช่ที่พึ่งสำหรับ SIGKILL หรือไฟดับฉับพลัน งานที่ต้องทนพังกลางคันยังต้องใช้ journal หรือ transactional write อยู่ดี ผมใช้มันเป็นชั้นสุดท้ายสำหรับปิดไฟล์ล็อกและพิมพ์สรุปสั้นๆ ก่อนจบครับ

Recap

  • register ใส่คิว LIFO
  • unregister ถอด handler ที่ยังไม่รัน
  • อย่าพึ่ง atexit แทนการจัดการทรัพยากรใน scope ที่ควบคุมได้