خانه / آموزش‌ها / مدیریت چندین خطا با Exception Groups در پایتون

مدیریت چندین خطا با Exception Groups در پایتون

🐍 HomeOfPython
|
📅 1404/10/18

مدیریت خطاها در پایتون همواره بر این اساس بوده است که "در هر لحظه تنها یک خطا رخ می‌دهد". اما در برنامه‌نویسی مدرن، به‌ویژه در برنامه‌های ناهمگام (Asynchronous) یا هنگام اعتبارسنجی‌های پیچیده داده‌ها، ممکن است چندین خطا همزمان رخ دهند.

پایتون ۳.۱۱ با معرفی Exception Groups و دستور جدید except* این مشکل را حل کرده است. در این مقاله، از مفاهیم پایه تا تکنیک‌های پیشرفته‌ی این قابلیت را بررسی می‌کنیم.

ساختار درختی Exception Group

سطح مقدماتی (Beginner Level)

در سطح مقدماتی، یاد می‌گیریم که ExceptionGroup چیست، چگونه ایجاد می‌شود و چطور می‌توانیم با استفاده از دستور جدید except* آن‌ها را مدیریت کنیم.

۱. مفهوم ExceptionGroup چیست؟

به زبان ساده، ExceptionGroup جعبه‌ای است که می‌تواند چندین استثنا (Exception) دیگر را درون خود نگه دارد. قبل از پایتون ۳.۱۱، اگر شما لیستی از خطاها داشتید، نمی‌توانستید همه آن‌ها را یکجا raise کنید، مگر اینکه آن‌ها را در یک لیست معمولی بگذارید که استاندارد نبود.

مثال اول: ساخت و پرتاب یک ExceptionGroup

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

Python

مثال دوم: تفاوت ظاهری در Traceback

اگر یک ExceptionGroup مدیریت نشود (uncaught)، پایتون آن را به صورت درختی نمایش می‌دهد.

python
# (Static) این کد صرفاً جهت نمایش ساختار است و نیاز به اجرا در محیط تعاملی دارد
raise ExceptionGroup("Errors", [
    ValueError("Bad value"),
    TypeError("Bad type")
])
# Output sketch:
#  + Exception Group Traceback (most recent call last):
#  |   ...
#  | ExceptionGroup: Errors (2 sub-exceptions)
#  +-+---------------- 1 ----------------
#    | ValueError: Bad value
#    +---------------- 2 ----------------
#    | TypeError: Bad type
#    +------------------------------------

۲. مدیریت خطاها با except*

مهم‌ترین تغییر در سینتکس پایتون برای پشتیبانی از این قابلیت، معرفی except* است. دستور except معمولی (بدون ستاره) کل گروه را به عنوان یک شیء می‌بیند، اما except* (با ستاره) داخل گروه را می‌گردد و فقط خطاهایی که با نوع مشخص شده تطابق دارند را استخراج و مدیریت می‌کند.

مثال اول: استفاده از except*

در اینجا می‌بینیم که چگونه می‌توانیم خطاهای مختلف داخل یک گروه را جداگانه مدیریت کنیم.

Python

مثال دوم: تودرتویی (Nesting)

ExceptionGroupها می‌توانند تودرتو باشند. دستور except* به صورت بازگشتی (Recursive) تمام درخت خطا را جستجو می‌کند تا نوع مورد نظر را پیدا کند.

Python

سطح پیشرفته (Professional Level)

در این بخش به مکانیزم داخلی "تقسیم و انتشار" (Split and Propagate)، استفاده در asyncio و TaskGroup، و نکات ظریف کار با BaseExceptionGroup می‌پردازیم.

۱. مکانیزم انتشار و فیلترینگ (Bubbling Logic)

وقتی از except* استفاده می‌کنید، پایتون عملاً شیء ExceptionGroup اصلی را "تکه‌تکه" می‌کند.

  1. خطاهایی که با نوع except* مطابقت دارند جدا شده و به آن بلاک فرستاده می‌شوند.
  2. خطاهایی که مطابقت ندارند، در یک ExceptionGroup جدید باقی مانده و به بیرون پرتاب (Propagate) می‌شوند.

این رفتار متفاوت از except معمولی است که اگر یک خطا را نگیرد، کل زنجیره را رها می‌کند. در except* ممکن است بخشی از گروه مدیریت شود و بخشی دیگر باعث کرش برنامه شود.

مثال اول: مدیریت بخشی از خطاها

در کد زیر، ValueError مدیریت می‌شود اما TypeError مدیریت نمی‌شود و باعث توقف برنامه (یا پرتاب به لایه بالاتر) می‌شود.

Python

مثال دوم: ساختار سلسله‌مراتبی

توجه داشته باشید که ExceptionGroup از Exception ارث‌بری می‌کند، اما BaseExceptionGroup از BaseException. اگر می‌خواهید خطاهای سیستمی مثل KeyboardInterrupt را در گروه مدیریت کنید، باید به این نکته دقت کنید.

python
# (Static) تعریف سلسله مراتب
# BaseException
#  ├── BaseExceptionGroup
#  │    └── ExceptionGroup
#  └── Exception

۲. کاربرد در برنامه‌نویسی ناهمگام (Asyncio TaskGroup)

اصلی‌ترین دلیل اضافه شدن Exception Groups به پایتون، مدیریت خطاهای همزمان در asyncio بود. کلاس asyncio.TaskGroup (جایگزین مدرن gather) از این قابلیت استفاده می‌کند. اگر چندین Task همزمان خطا دهند، همه آن‌ها در یک ExceptionGroup جمع می‌شوند.

مثال اول: استفاده از TaskGroup

این کد نشان می‌دهد چطور دو تسک همزمان خطا می‌دهند و ما هر دو را می‌گیریم.

Python

۳. افزودن یادداشت به خطاها (add_note)

پایتون ۳.۱۱ متد add_note() را به تمام استثناها اضافه کرد. این قابلیت در کنار Exception Groups بسیار قدرتمند است، زیرا به شما اجازه می‌دهد بدون تغییر نوع خطا، اطلاعات زمینه‌ای (Context) را به خطاهای داخل گروه اضافه کنید تا در زمان دیباگ مشخص شود هر خطا مربوط به کدام بخش بوده است.

مثال اول: غنی‌سازی خطاها در گروه

در این مثال، خطاها را قبل از گروه‌بندی با اطلاعات اضافی برچسب‌گذاری می‌کنیم.

Python

مثال دوم: دستکاری ساختار ExceptionGroup (Split)

شما می‌توانید به صورت دستی و با متد split() یک گروه خطا را فیلتر کنید. این همان کاری است که except* در پشت صحنه انجام می‌دهد.

Python