حالت توسعه پایتون¶
اضافه شده در نسخهی 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()را ثبت میکند.
حالت توسعه پایتون بهطور پیشفرض ماژول 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 است.