حالت توسعه پایتون

اضافه شده در نسخه‌ی 3.7.

حالت توسعه پایتون، بررسی‌های ران‌تایم اضافی‌ای را ارائه می‌دهد که فعال بودن آن‌ها به‌طور پیش‌فرض بیش از حد پرهزینه است. اگر کد صحیح باشد، نباید پرجزئیات‌تر از حالت پیش‌فرض باشد؛ هشدارهای جدید تنها زمانی نشان داده می‌شوند که مشکلی شناسایی شود.

می‌توان آن را با استفاده از گزینه‌ی خط فرمان -X dev یا با تنظیم متغیر محیطی PYTHONDEVMODE روی 1 فعال کرد.

همچنین ببینید ساخت اشکال‌زدایی پایتون.

اثرات حالت توسعه پایتون

فعال‌سازی حالت توسعه پایتون مشابه دستور زیر است، اما با اثرات اضافی که در زیر شرح داده شده‌اند:

PYTHONMALLOC=debug PYTHONASYNCIODEBUG=1 python -W default -X faulthandler

تأثیرات حالت توسعه پایتون:

  • افزودن فیلتر هشدار default. هشدارهای زیر نمایش داده می‌شوند:

    معمولاً فیلترهای هشدار پیش‌فرض، هشدارهای بالا را فیلتر می‌کنند.

    این‌گونه رفتار می‌کند که گویی گزینه خط فرمان -W default استفاده شده است.

    برای در نظر گرفتن هشدارها به‌عنوان خطا، از گزینه‌ی خط فرمان -W error استفاده کنید یا متغیر محیطی PYTHONWARNINGS را روی error تنظیم کنید.

  • برای بررسی، قلاب‌های اشکال‌زدایی را روی تخصیص‌دهنده‌های حافظه نصب کنید:

    • زیرریز بافر (Buffer underflow)

    • سرریز بافر

    • نقض API تخصیص‌دهنده حافظه

    • استفاده‌ی ناایمن از GIL

    به تابع C PyMem_SetupDebugHooks() مراجعه کنید.

    به‌گونه‌ای رفتار می‌کند که گویی متغیر محیطی PYTHONMALLOC روی debug تنظیم شده است.

    برای فعال کردن حالت توسعه پایتون بدون نصب قلاب‌های اشکال‌زدایی روی تخصیص‌دهنده‌های حافظه، متغیر محیطی PYTHONMALLOC را روی default تنظیم کنید.

  • در زمان راه‌اندازی پایتون، faulthandler.enable() را فراخوانی کنید تا هندلرهایی برای سیگنال‌های SIGSEGV، SIGFPE، SIGABRT، SIGBUS و SIGILL نصب شوند که در صورت فروپاشی، ردگیری پشته پایتون را برون‌ریزی می‌کنند.

    این به‌گونه‌ای رفتار می‌کند که گویی از گزینه‌ی خط فرمان -X faulthandler استفاده شده باشد یا متغیر محیطی PYTHONFAULTHANDLER روی 1 تنظیم شده باشد.

  • حالت اشکال‌زدایی asyncio را فعال کنید. برای مثال، asyncio هم‌روال‌هایی را که await نشده‌اند بررسی می‌کند و آن‌ها را ثبت می‌کند.

    این به‌گونه‌ای رفتار می‌کند که گویی متغیر محیطی PYTHONASYNCIODEBUG روی 1 تنظیم شده است.

  • آرگومان‌های encoding و errors را برای عملیات کدگذاری و کدگشایی رشته بررسی کنید. مثال‌ها: open()، str.encode() و bytes.decode().

    به‌طور پیش‌فرض، برای بهترین عملکرد، آرگومان errors فقط در اولین خطای کدگذاری/کدگشایی بررسی می‌شود و آرگومان encoding گاهی برای رشته‌های خالی نادیده گرفته می‌شود.

  • تخریب‌کننده‌ی io.IOBase استثناهای close() را ثبت می‌کند.

  • ویژگی dev_mode از sys.flags را روی True تنظیم کنید.

حالت توسعه پایتون به‌طور پیش‌فرض ماژول tracemalloc را فعال نمی‌کند، زیرا هزینه سربار (برای عملکرد و حافظه) بیش از حد زیاد خواهد بود. فعال‌سازی ماژول tracemalloc اطلاعات بیشتری درباره منشأ برخی خطاها فراهم می‌کند. برای مثال، ResourceWarning ردگیری پشته محل تخصیص منبع را ثبت می‌کند، و یک خطای سرریز بافر ردگیری پشته محل تخصیص بلوک حافظه را ثبت می‌کند.

حالت توسعه پایتون مانع از آن نمی‌شود که گزینه خط فرمان -O دستورهای assert را حذف کند یا __debug__ را روی False تنظیم کند.

حالت توسعه پایتون را فقط می‌توان در هنگام راه‌اندازی پایتون فعال کرد. مقدار آن را می‌توان از sys.flags.dev_mode خواند.

تغییر یافته در نسخه‌ی 3.8: تخریب‌کننده‌ی io.IOBase اکنون استثناهای close() را ثبت می‌کند.

تغییر یافته در نسخه‌ی 3.9: آرگومان‌های encoding و errors اکنون برای عملیات کدگذاری و کدگشایی رشته بررسی می‌شوند.

مثال ResourceWarning

مثالی از یک اسکریپت برای شمارش تعداد سطرهای پرونده متنی مشخص‌شده در خط فرمان:

import sys

def main():
    fp = open(sys.argv[1])
    nlines = len(fp.readlines())
    print(nlines)
    # The file is closed implicitly

if __name__ == "__main__":
    main()

اسکریپت پرونده را به‌صراحت نمی‌بندد. به‌طور پیش‌فرض، پایتون هیچ هشداری نشان نمی‌دهند. مثالی با استفاده از README.txt که ۲۶۹ خط دارد:

$ python script.py README.txt
269

فعال‌سازی حالت توسعه پایتون یک هشدار ResourceWarning را نمایش می‌دهد:

$ python -X dev script.py README.txt
269
script.py:10: ResourceWarning: unclosed file <_io.TextIOWrapper name='README.rst' mode='r' encoding='UTF-8'>
  main()
ResourceWarning: Enable tracemalloc to get the object allocation traceback

علاوه بر این، فعال‌سازی tracemalloc سطری را نشان می‌دهد که پرونده در آن باز شده است:

$ python -X dev -X tracemalloc=5 script.py README.rst
269
script.py:10: ResourceWarning: unclosed file <_io.TextIOWrapper name='README.rst' mode='r' encoding='UTF-8'>
  main()
Object allocated at (most recent call last):
  File "script.py", lineno 10
    main()
  File "script.py", lineno 4
    fp = open(sys.argv[1])

راه‌حل این است که پرونده را به‌صورت صریح ببندید. مثال با استفاده از یک مدیر زمینه:

def main():
    # Close the file explicitly when exiting the with block
    with open(sys.argv[1]) as fp:
        nlines = len(fp.readlines())
    print(nlines)

نبستن یک منبع به‌طور صریح می‌تواند باعث شود آن منبع بسیار بیشتر از حد انتظار باز بماند؛ این موضوع می‌تواند هنگام خروج از Python مشکلات شدیدی ایجاد کند. این موضوع در CPython بد است، اما در PyPy حتی بدتر است. بستن منابع به‌طور صریح، یک برنامه را قطعی‌تر و قابل‌اعتمادتر می‌کند.

مثال خطای توصیف‌گر پرونده نامعتبر

اسکریپتی که خط نخست خود را نمایش می‌دهد:

import os

def main():
    fp = open(__file__)
    firstline = fp.readline()
    print(firstline.rstrip())
    os.close(fp.fileno())
    # The file is closed implicitly

main()

به‌طور پیش‌فرض، پایتون هیچ هشداری نشان نمی‌دهد:

$ python script.py
import os

حالت توسعه پایتون هنگام نهایی‌سازی شیء پرونده، یک ResourceWarning را نمایش می‌دهد و خطای «Bad file descriptor» را ثبت می‌کند:

$ python -X dev script.py
import os
script.py:10: ResourceWarning: unclosed file <_io.TextIOWrapper name='script.py' mode='r' encoding='UTF-8'>
  main()
ResourceWarning: Enable tracemalloc to get the object allocation traceback
Exception ignored in: <_io.TextIOWrapper name='script.py' mode='r' encoding='UTF-8'>
Traceback (most recent call last):
  File "script.py", line 10, in <module>
    main()
OSError: [Errno 9] Bad file descriptor

os.close(fp.fileno()) توصیف‌گر پرونده را می‌بندد. هنگامی که نهایی‌ساز شیء پرونده تلاش می‌کند توصیف‌گر پرونده را دوباره ببندد، با خطای Bad file descriptor مواجه می‌شود. توصیف‌گر پرونده باید تنها یک بار بسته شود. در بدترین حالت، دو بار بستن آن می‌تواند منجر به فروپاشی شود (برای نمونه bpo-18748 را ببینید).

راه‌حل، حذف خط os.close(fp.fileno()) یا باز کردن پرونده با closefd=False است.