atexit --- هندلرهای خروج


ماژول atexit توابعی را برای ثبت و لغو ثبت هندلرهای خروج (exit handlers) تعریف می‌کند: توابعی که به‌صورت خودکار «در خروج» اجرا می‌شوند، یعنی در هنگام خاتمه‌ی عادی برنامه (مثلاً اگر sys.exit() فراخوانی شود یا اجرای ماژول اصلی کامل شود) یا، به‌صورت کلی‌تر، در هنگام خاتمه‌ی مفسر.

در خروج، تمام هندلرهای خروج ثبت‌شده به ترتیب معکوس ترتیبی که ثبت شده‌اند فراخوانی می‌شوند. اگر A، B، و C را ثبت کنید، در زمان خاتمه‌ی مفسر آن‌ها به ترتیب C، B، A اجرا خواهند شد. فرض این است که ماژول‌های سطح پایین معمولاً قبل از ماژول‌های سطح بالا وارد (import) می‌شوند و بنابراین باید بعداً پاک‌سازی شوند.

اگر یک استثنا در طول اجرای یک هندلر خروج رخ دهد، یک ردگیری چاپ می‌شود (مگر اینکه SystemExit رخ دهد) و اطلاعات استثنا ذخیره می‌شود. پس از اینکه تمام هندلرهای خروج فرصت اجرا را داشته باشند، آخرین استثنای رخ‌داده مجدداً پرتاب می‌شود.

در برنامه‌هایی که از چندین مفسر استفاده می‌کنند، هر مفسر پشته‌ی خودِ هندلرهای خروج را دارد، که هنگام خاتمه‌ی مفسر اجرا می‌شوند (مثلاً با concurrent.interpreters.Interpreter.close() یا API زبان C Py_EndInterpreter()). توابع ثبت در این ماژول تنها بر مفسری که از آن فراخوانی شده‌اند تأثیر می‌گذارند.

نکته: هندلرهای خروج فراخوانی نمی‌شوند وقتی برنامه توسط سیگنالی که توسط پایتون مدیریت نمی‌شود متوقف می‌شود، وقتی یک خطای داخلی مهلک پایتون شناسایی می‌شود، یا وقتی os._exit() فراخوانی می‌شود.

توجه: اثر ثبت یا لغو ثبت توابع از درون یک تابع پاک‌سازی تعریف‌نشده است.

هشدار

هنگام نوشتن هندلرهای خروج، به‌ویژه در افزونه‌های API زبان C، به یاد داشته باشید که سایر هندلرهای خروج همچنان ممکن است کد پایتون دلخواه را پس از پاک‌سازی شما اجرا کنند. چنین کدی باید با موفقیت انجام شود یا با یک استثنا شکست بخورد، نه اینکه باعث فروپاشی شود.

تغییر یافته در نسخه‌ی 3.12: تلاش برای شروع یک نخ جدید یا os.fork() یک فرایند جدید در یک هندلر خروج اکنون منجر به RuntimeError می‌شود. پیش از این، این می‌توانست شرایط رقابتی را بین نخ اصلی ران‌تایم پایتون که وضعیت نخ‌ها را آزاد می‌کند در حالی که روال‌های داخلی threading یا فرایند جدید سعی در استفاده از آن وضعیت دارند، ایجاد کند که می‌توانست منجر به فروپاشی شود تا خاتمه‌ی تمیز.

تغییر یافته در نسخه‌ی 3.7: هنگام استفاده با زیرمفسرها، توابع ثبت‌شده محلی به مفسری هستند که در آن ثبت شده‌اند.

atexit.register(func, *args, **kwargs)

func را به عنوان یک هندلر خروج ثبت کنید. هر آرگومان اختیاری که قرار است به func پاس داده شود باید به عنوان آرگومان به register() پاس داده شود. امکان ثبت یک تابع و آرگومان‌های یکسان بیش از یک بار وجود دارد.

این تابع، func را برمی‌گرداند که امکان استفاده از آن به‌عنوان دکوراتور را فراهم می‌کند.

atexit.unregister(func)

func را از فهرست هندلرهای خروج حذف کنید. اگر func قبلاً ثبت نشده باشد، unregister() به‌صورت بی‌صدا کاری انجام نمی‌دهد. اگر func بیش از یک بار ثبت شده باشد، هر وقوع از آن تابع در پشته‌ی فراخوانی atexit حذف خواهد شد. مقایسه‌های برابری (==) به‌صورت داخلی در طول لغو ثبت استفاده می‌شوند، بنابراین ارجاعهای تابع نیازی به داشتن هویت‌های مطابق ندارند.

همچنین ملاحظه نمائید

ماژول readline

مثالی مفید از atexit برای خواندن و نوشتن پرونده‌های تاریخچه‌ی readline.

مثال atexit

مثال ساده‌ی زیر نشان می‌دهد که چگونه یک ماژول می‌تواند هنگامی که ایمپورت می‌شود، یک شمارنده را از یک پرونده مقداردهی اولیه کند و مقدار به‌روزشده‌ی شمارنده را به‌صورت خودکار هنگام پایان برنامه ذخیره کند، بدون این‌که به فراخوانی صریح این ماژول توسط برنامه کاربردی در زمان پایان نیاز باشد.

try:
    with open('counterfile') as infile:
        _count = int(infile.read())
except FileNotFoundError:
    _count = 0

def incrcounter(n):
    global _count
    _count = _count + n

def savecounter():
    with open('counterfile', 'w') as outfile:
        outfile.write('%d' % _count)

import atexit

atexit.register(savecounter)

همچنین می‌توان آرگومان‌های جایگاهی و کلیدواژه‌ای را به register() ارسال کرد تا در زمان فراخوانی تابع ثبت‌شده، به آن تابع ارسال شوند:

def goodbye(name, adjective):
    print('Goodbye %s, it was %s to meet you.' % (name, adjective))

import atexit

atexit.register(goodbye, 'Donny', 'nice')
# or:
atexit.register(goodbye, adjective='nice', name='Donny')

استفاده به‌عنوان یک دکوراتور:

import atexit

@atexit.register
def goodbye():
    print('You are now leaving the Python sector.')

این تنها با توابعی کار می‌کند که بتوان آن‌ها را بدون آرگومان فراخوانی کرد.