IlmHamroh
Python kursi/Xatolar4/8-dars38 daqiqa
Mundarija (21)

9.4-dars: Bir nechta istisno

9-QISM — XATOLAR VA ISTISNOLAR · 4-dars


1. Kirish va motivatsiya

Bu kod hech qachon ikkinchi blokka kirmaydi:

python
try:
    json.loads(matn)
except ValueError:
    print("qiymat xatosi")
except json.JSONDecodeError:        # ❌ O'LIK KOD
    print("JSON xatosi")

JSONDecodeError — ValueError avlodi.

Va bu — muammoni yashiradi:

python
try:
    natija = quvur(malumot)
except Exception as e:              # ⚠️ hammasi bir xil
    return None

To'g'ri yondashuv — ierarxiyadan foydalanish:

python
try:
    natija = quvur(malumot)
except (ConnectionError, TimeoutError):
    qayta_urin()                    # ⭐ vaqtinchalik
except (ValueError, TypeError) as e:
    jurnal.error("Ma'lumot xatosi: %s", e)   # ⭐ doimiy
    raise
except OSError as e:
    jurnal.critical("Tizim xatosi: %s", e)   # ⭐ jiddiy
    raise

Bu darsda:

  • Bir nechta except va tartib qoidasi
  • Tuple bilan guruhlash
  • Istisnolar ierarxiyasi va guruh ushlash
  • ExceptionGroup va except* (3.11+)
  • Bir vaqtda bir necha xatoni yig'ish
  • Xatolarni toifalarga ajratish strategiyasi

2. Nazariya — chuqur tushuntirish

2.1. Bir nechta except bloki

python
try:
    ...
except AniqXato:                    # ⭐ 1
    ...
except BoshqaXato:                  # ⭐ 2
    ...
except UmumiyXato:                  # ⭐ 3
    ...

Qoidalar:

  1. Faqat BITTA blok bajariladi
  2. Tartib bilan tekshiriladi (yuqoridan pastga)
  3. isinstance bo'yicha mos keladi
  4. Birinchi moslik — g'olib
  5. Hech biri mos kelmasa — yuqoriga uzatiladi

Tartib xatosi:

python
except ValueError: ...              # ⚠️ birinchi
except json.JSONDecodeError: ...    # ❌ o'lik kod

Qoida: aniqdan umumiyga.

2.2. Tuple bilan guruhlash

python
except (ValueError, TypeError):     # ⭐ ikkalasi ham
    ...

except (ValueError, TypeError) as e:
    print(type(e).__name__)         # aniq turini bilish mumkin

Qavs shart:

python
except ValueError, TypeError:       # ❌ SyntaxError (Python 3)
except (ValueError, TypeError):     # ✅

Python 2 da except X, e: — as e ma'nosini bergan. Python 3 da bu taqiqlangan.

O'zgaruvchi bilan:

python
QAYTA_URINISH = (ConnectionError, TimeoutError, OSError)

try:
    ...
except QAYTA_URINISH:               # ⭐ ro'yxatni bir joyda saqlash
    ...

2.3. Istisnolar ierarxiyasi

BaseException
 ├── SystemExit
 ├── KeyboardInterrupt
 ├── GeneratorExit
 └── Exception
      ├── ArithmeticError
      │    ├── ZeroDivisionError
      │    ├── OverflowError
      │    └── FloatingPointError
      ├── LookupError
      │    ├── KeyError
      │    └── IndexError
      ├── OSError                    (= IOError, EnvironmentError)
      │    ├── FileNotFoundError
      │    ├── PermissionError
      │    ├── FileExistsError
      │    ├── IsADirectoryError
      │    ├── NotADirectoryError
      │    ├── InterruptedError
      │    ├── TimeoutError
      │    ├── BlockingIOError
      │    ├── ChildProcessError
      │    └── ConnectionError
      │         ├── BrokenPipeError
      │         ├── ConnectionAbortedError
      │         ├── ConnectionRefusedError
      │         └── ConnectionResetError
      ├── ValueError
      │    └── UnicodeError
      │         ├── UnicodeDecodeError
      │         ├── UnicodeEncodeError
      │         └── UnicodeTranslateError
      ├── TypeError
      ├── AttributeError
      ├── NameError
      │    └── UnboundLocalError
      ├── RuntimeError
      │    ├── RecursionError
      │    └── NotImplementedError
      ├── ImportError
      │    └── ModuleNotFoundError
      ├── SyntaxError
      │    └── IndentationError
      │         └── TabError
      ├── StopIteration
      ├── StopAsyncIteration
      ├── AssertionError
      ├── BufferError
      ├── EOFError
      ├── MemoryError
      ├── ReferenceError
      ├── SystemError
      ├── Warning (ierarxiyasi)
      └── ExceptionGroup            (3.11+)

Guruh ushlash:

python
except LookupError:                 # KeyError + IndexError
except ArithmeticError:             # ZeroDivisionError, Overflow, FloatingPoint
except OSError:                     # barcha fayl/tarmoq
except ConnectionError:             # 4 ta ulanish xatosi
except UnicodeError:                # Decode + Encode + Translate

Foydali kombinatsiyalar:

python
except (ValueError, TypeError):     # ma'lumot xatolari
except (OSError, IOError):          # ⚠️ IOError == OSError (3.3+)
except (KeyError, IndexError):      # ≡ LookupError

2.4. Xatolarni toifalarga ajratish

Amaliy strategiya — uch toifa:

python
# 1. QAYTA URINISH MUMKIN (vaqtinchalik)
VAQTINCHALIK = (ConnectionError, TimeoutError, BlockingIOError)

# 2. MA'LUMOT XATOSI (foydalanuvchi tuzatadi)
MALUMOT = (ValueError, TypeError, KeyError, json.JSONDecodeError)

# 3. TIZIM XATOSI (dasturchi tuzatadi)
TIZIM = (OSError, MemoryError, RuntimeError)
python
try:
    ish()
except VAQTINCHALIK:
    qayta_urin()
except MALUMOT as e:
    return {"xato": str(e)}, 400    # ⭐ HTTP 400
except TIZIM as e:
    jurnal.critical(...)
    return {"xato": "Ichki xato"}, 500      # ⭐ HTTP 500

2.5. ExceptionGroup (3.11+)

Muammo: parallel ishlarda bir nechta xato bo'ladi.

python
# ⚠️ Python 3.10 va oldin:
xatolar = []
for ish in ishlar:
    try:
        ish()
    except Exception as e:
        xatolar.append(e)
if xatolar:
    raise xatolar[0]                # ⚠️ faqat BIRINCHISI

Python 3.11+ (PEP 654):

python
raise ExceptionGroup("Ishlar bajarilmadi", [
    ValueError("1-ish"),
    TypeError("2-ish"),
    ConnectionError("3-ish"),
])

except* — turlar bo'yicha ajratish:

python
try:
    ish()
except* ValueError as eg:
    print(f"{len(eg.exceptions)} ta ValueError")
except* TypeError as eg:
    print(f"{len(eg.exceptions)} ta TypeError")

Muhim farqlar:

except except*
Nechta blok ishlaydi Bitta Bir nechta
as obyekti Istisno ExceptionGroup
Guruhga bo'linadi
Aralashtirish — except bilan birga bo'lmaydi

BaseExceptionGroup:

python
ExceptionGroup                      # faqat Exception avlodlari
BaseExceptionGroup                  # BaseException ham (KeyboardInterrupt)

Amaliy ishlatilishi:

python
async with asyncio.TaskGroup() as tg:       # ⭐ 3.11+
    tg.create_task(ish1())
    tg.create_task(ish2())
# → bir nechta xato bo'lsa ExceptionGroup

2.6. Xatolarni yig'ish naqshlari

A) Ro'yxatga yig'ish:

python
natijalar, xatolar = [], []
for x in malumot:
    try:
        natijalar.append(ishla(x))
    except Exception as e:
        xatolar.append((x, e))

B) ExceptionGroup bilan (3.11+):

python
xatolar = []
for x in malumot:
    try:
        ishla(x)
    except Exception as e:
        xatolar.append(e)
if xatolar:
    raise ExceptionGroup(f"{len(xatolar)} xato", xatolar)

C) Kontekst menejeri:

python
@contextmanager
def yigish():
    xatolar = []
    yield xatolar
    if xatolar:
        raise ExceptionGroup("Xatolar", xatolar)

2.7. warnings — istisno bo'lmagan ogohlantirish

python
import warnings

warnings.warn("Bu funksiya eskirgan", DeprecationWarning)

Ierarxiya:

Warning
 ├── UserWarning                    (sukut)
 ├── DeprecationWarning             ⚠️ sukut bo'yicha yashirin
 ├── PendingDeprecationWarning
 ├── SyntaxWarning
 ├── RuntimeWarning
 ├── FutureWarning                  ⭐ foydalanuvchilarga
 ├── ImportWarning
 ├── UnicodeWarning
 ├── BytesWarning
 ├── ResourceWarning                ⚠️ yopilmagan fayllar
 └── EncodingWarning                (3.10+)

Boshqarish:

python
warnings.simplefilter("error")      # ⭐ ogohlantirish → istisno
warnings.simplefilter("ignore")
warnings.filterwarnings("ignore", category=DeprecationWarning)

with warnings.catch_warnings(record=True) as w:
    warnings.simplefilter("always")
    kod()
    print(len(w))                   # ⭐ ushlangan ogohlantirishlar

3. Tez ma'lumotnoma

Bir nechta except

python
except AniqXato: ...            ⭐ 1. aniq
except (A, B) as e: ...         ⭐ 2. guruh (qavs SHART)
except OtaSinf: ...             ⭐ 3. umumiy

Faqat BITTA blok ishlaydi
Tartib: ANIQDAN UMUMIYGA

Foydali guruhlar

LookupError        KeyError, IndexError
ArithmeticError    ZeroDivisionError, OverflowError, FloatingPointError
OSError            FileNotFound, Permission, Connection, Timeout, ...
ConnectionError    BrokenPipe, Aborted, Refused, Reset
UnicodeError       Decode, Encode, Translate
ImportError        ModuleNotFoundError
RuntimeError       RecursionError, NotImplementedError

ExceptionGroup (3.11+)

python
raise ExceptionGroup("xabar", [e1, e2, e3])

try: ...
except* ValueError as eg:       ⭐ BIR NECHTA blok ishlashi mumkin
    eg.exceptions               tuple
except* TypeError as eg: ...

⚠️ except va except* ni ARALASHTIRIB bo'lmaydi

Toifalar strategiyasi

python
VAQTINCHALIK = (ConnectionError, TimeoutError)   → qayta urin
MALUMOT = (ValueError, TypeError, KeyError)      → 400
TIZIM = (OSError, MemoryError)                   → 500

4. Batafsil misollar

Misol 1 — Tartib va ierarxiya

python
"""Bir nechta except va ierarxiya."""

import builtins
import json
import sys

print("=== ⭐ 1. Tartib qoidasi ===\n")


def togri_tartib(matn: str) -> str:
    try:
        return f"✅ {json.loads(matn)}"
    except json.JSONDecodeError as e:            # ⭐ AVLOD
        return f"JSONDecodeError: {e.msg}"
    except ValueError as e:                      # ota-sinf
        return f"ValueError: {e}"
    except TypeError as e:
        return f"TypeError: {e}"


def notogri_tartib(matn) -> str:
    try:
        return f"✅ {json.loads(matn)}"
    except ValueError as e:                      # ⚠️ OTA-SINF avval
        return f"ValueError: {e}"
    except json.JSONDecodeError as e:            # ❌ O'LIK KOD
        return f"JSONDecodeError: {e.msg}"
    except TypeError as e:
        return f"TypeError: {e}"


SINOVLAR = ['{"a": 1}', "{buzilgan}", "not json", 42, None]

print(f"  {'Kirish':<18} {'To`g`ri tartib':<30} {'Noto`g`ri'}")
print("  " + "─" * 76)
for m in SINOVLAR:
    r1 = togri_tartib(m) if isinstance(m, str) else "—"
    if not isinstance(m, str):
        try:
            r1 = togri_tartib(m)
        except TypeError as e:
            r1 = f"TypeError: {str(e)[:20]}"
    try:
        r2 = notogri_tartib(m)
    except Exception as e:
        r2 = f"❌ {type(e).__name__}"
    belgi = "⚠️" if r1.split(":")[0] != r2.split(":")[0] else ""
    print(f"  {str(m)[:16]:<18} {r1[:28]:<30} {r2[:24]} {belgi}")

print(f"""
  ⭐ JSONDecodeError MRO:
     {[c.__name__ for c in json.JSONDecodeError.__mro__[:4]]}

  ⚠️ ValueError avval bo'lsa — JSONDecodeError bloki
     HECH QACHON ishlamaydi (o'lik kod).

  ⭐ Linterlar buni topadi:
     ruff B014, pylint W0705
""")


print("=== ⭐ 2. Ierarxiya daraxti ===\n")


def daraxt(Sinf, chuqurlik=0, maks=3, korilgan=None):
    if korilgan is None:
        korilgan = set()
    if chuqurlik > maks or Sinf in korilgan:
        return []
    korilgan.add(Sinf)
    q = [f"{'    ' * chuqurlik}{'└── ' if chuqurlik else ''}"
         f"{Sinf.__name__}"]
    avlodlar = sorted(Sinf.__subclasses__(), key=lambda c: c.__name__)
    # Faqat o'rnatilgan istisnolar
    avlodlar = [c for c in avlodlar
                if c.__module__ in ("builtins", "exceptions")]
    for a in avlodlar[:8]:
        q.extend(daraxt(a, chuqurlik + 1, maks, korilgan))
    if len(avlodlar) > 8:
        q.append(f"{'    ' * (chuqurlik + 1)}└── ... yana "
                 f"{len(avlodlar) - 8}")
    return q


for q in daraxt(BaseException, maks=2):
    print(f"  {q}")

print(f"\n  OSError avlodlari:")
for q in daraxt(OSError, maks=1)[:14]:
    print(f"  {q}")


print("\n\n=== ⭐ 3. Guruh ushlash ===\n")

GURUHLAR = [
    (LookupError, [KeyError("k"), IndexError("i")]),
    (ArithmeticError, [ZeroDivisionError("z"), OverflowError("o"),
                       FloatingPointError("f")]),
    (OSError, [FileNotFoundError("f"), PermissionError("p"),
               ConnectionResetError("c"), TimeoutError("t")]),
    (ConnectionError, [BrokenPipeError("b"), ConnectionRefusedError("r"),
                       ConnectionResetError("s")]),
    (UnicodeError, [UnicodeDecodeError("utf-8", b"\xff", 0, 1, "r"),
                    UnicodeEncodeError("ascii", "Ў", 0, 1, "r")]),
    (ImportError, [ModuleNotFoundError("m")]),
    (RuntimeError, [RecursionError("r"), NotImplementedError("n")]),
]

for Ota, avlodlar in GURUHLAR:
    nomlar = ", ".join(type(a).__name__ for a in avlodlar)
    print(f"  except {Ota.__name__}:")
    print(f"    → {nomlar}")
    hammasi = all(isinstance(a, Ota) for a in avlodlar)
    print(f"    {'✅' if hammasi else '❌'} hammasi ushlanadi\n")

print(f"  ⚠️ Taxminlar TEKSHIRUVI:\n")
TAXMINLAR = [
    ("IOError is OSError",          IOError is OSError),
    ("EnvironmentError is OSError", EnvironmentError is OSError),
    ("WindowsError bormi",          hasattr(builtins, "WindowsError")),
    ("TimeoutError — OSError",      issubclass(TimeoutError, OSError)),
    ("StopIteration — Exception",   issubclass(StopIteration, Exception)),
    ("KeyboardInterrupt — Exception",
     issubclass(KeyboardInterrupt, Exception)),
    ("SystemExit — Exception",      issubclass(SystemExit, Exception)),
    ("bool — int",                  issubclass(bool, int)),
    ("UnicodeDecodeError — ValueError",
     issubclass(UnicodeDecodeError, ValueError)),
    ("ModuleNotFoundError — ImportError",
     issubclass(ModuleNotFoundError, ImportError)),
    ("RecursionError — RuntimeError",
     issubclass(RecursionError, RuntimeError)),
]

for tavsif, natija in TAXMINLAR:
    print(f"    {tavsif:<40} {natija}")

print(f"""
  ⚠️ MUHIM:
     KeyboardInterrupt va SystemExit — Exception EMAS
     → except Exception: ularni ushlamaydi ✅

     IOError == OSError (Python 3.3 dan birlashtirilgan)
""")


print("=== 4. Tuple bilan guruhlash ===\n")


def tuple_bilan(f):
    try:
        return f()
    except (ValueError, TypeError, KeyError) as e:
        return f"✅ {type(e).__name__}: {str(e)[:24]}"
    except Exception as e:
        return f"⚠️ ushlanmadi: {type(e).__name__}"


SINOVLAR2 = [
    ("int('abc')",      lambda: int("abc")),
    ("'a' + 1",         lambda: "a" + 1),
    ("{}['k']",         lambda: {}["k"]),
    ("[1][5]",          lambda: [1][5]),
    ("1/0",             lambda: 1 / 0),
]

for kod, f in SINOVLAR2:
    print(f"  {kod:<16} {tuple_bilan(f)}")

print(f"\n  ⭐ O'zgaruvchi bilan (qayta ishlatiladi):\n")

MALUMOT_XATOLARI = (ValueError, TypeError, KeyError, IndexError,
                    json.JSONDecodeError)
TARMOQ_XATOLARI = (ConnectionError, TimeoutError, BlockingIOError)
TIZIM_XATOLARI = (OSError, MemoryError, SystemError)


def toifali(f):
    try:
        return ("✅", f())
    except MALUMOT_XATOLARI as e:
        return ("MA'LUMOT", f"{type(e).__name__}")
    except TARMOQ_XATOLARI as e:
        return ("TARMOQ", f"{type(e).__name__}")
    except TIZIM_XATOLARI as e:
        return ("TIZIM", f"{type(e).__name__}")
    except Exception as e:
        return ("NOMA'LUM", f"{type(e).__name__}")


SINOVLAR3 = [
    ("int('abc')",              lambda: int("abc")),
    ("{}['k']",                 lambda: {}["k"]),
    ("json.loads('{')",         lambda: json.loads("{")),
    ("open('yoq_9999.txt')",    lambda: open("yoq_9999.txt")),
    ("raise ConnectionError",   lambda: (_ for _ in ()).throw(
        ConnectionError("tarmoq"))),
    ("raise TimeoutError",      lambda: (_ for _ in ()).throw(
        TimeoutError("vaqt"))),
    ("1/0",                     lambda: 1 / 0),
]

print(f"  {'Kod':<26} {'Toifa':<12} {'Tur'}")
print("  " + "─" * 56)
for kod, f in SINOVLAR3:
    toifa, tur = toifali(f)
    print(f"  {kod:<26} {toifa:<12} {tur}")

print(f"""
  ⚠️ E'TIBOR: TimeoutError — OSError avlodi!
     TARMOQ_XATOLARI da bo'lsa ham, agar TIZIM_XATOLARI
     avval yozilsa — u yerga tushardi.

  ⭐ Tartib: aniq guruhlar avval, umumiy keyin.
""")


print("=== ⭐ 5. Qaysi blok ishlaydi ===\n")


def kuzatuv(istisno):
    """Qaysi except bloki ishlashini aniqlaydi."""
    izlar = []
    try:
        raise istisno
    except KeyError:
        izlar.append("1: KeyError")
    except LookupError:
        izlar.append("2: LookupError")
    except (ValueError, TypeError):
        izlar.append("3: (ValueError, TypeError)")
    except OSError:
        izlar.append("4: OSError")
    except Exception:
        izlar.append("5: Exception")
    return izlar[0] if izlar else "ushlanmadi"


ISTISNOLAR = [
    KeyError("k"), IndexError("i"), ValueError("v"), TypeError("t"),
    FileNotFoundError("f"), ConnectionError("c"), ZeroDivisionError("z"),
    RuntimeError("r"), UnicodeDecodeError("utf-8", b"\xff", 0, 1, "r"),
    json.JSONDecodeError("m", "d", 0),
]

print(f"  Bloklar tartibi:")
print(f"    1: except KeyError")
print(f"    2: except LookupError")
print(f"    3: except (ValueError, TypeError)")
print(f"    4: except OSError")
print(f"    5: except Exception\n")

print(f"  {'Istisno':<26} {'Qaysi blok'}")
print("  " + "─" * 56)
for e in ISTISNOLAR:
    print(f"  {type(e).__name__:<26} {kuzatuv(e)}")

print(f"""
  ⭐ IndexError → 2 (LookupError), 1 emas
  ⭐ UnicodeDecodeError → 3 (ValueError avlodi)
  ⭐ JSONDecodeError → 3 (ValueError avlodi)
  ⭐ ConnectionError → 4 (OSError avlodi)
""")


print("=== 6. O'lik kodni topish ===\n")


def olik_kod_tekshir(bloklar: list[type]) -> list[tuple[int, str]]:
    """⭐ Qaysi except bloklari o'lik ekanini aniqlaydi."""
    muammolar = []
    for i, Joriy in enumerate(bloklar):
        for j, Oldingi in enumerate(bloklar[:i]):
            if issubclass(Joriy, Oldingi):
                muammolar.append((
                    i,
                    f"except {Joriy.__name__} (#{i+1}) — o'lik kod: "
                    f"{Oldingi.__name__} (#{j+1}) uni allaqachon ushlaydi"
                ))
                break
    return muammolar


TEKSHIRUVLAR = [
    ("✅ To'g'ri", [KeyError, LookupError, ValueError, Exception]),
    ("⚠️ 1", [ValueError, json.JSONDecodeError]),
    ("⚠️ 2", [Exception, ValueError, KeyError]),
    ("⚠️ 3", [LookupError, KeyError, IndexError]),
    ("⚠️ 4", [OSError, FileNotFoundError, ConnectionError]),
    ("✅ To'g'ri", [json.JSONDecodeError, ValueError, Exception]),
]

for nom, bloklar in TEKSHIRUVLAR:
    nomlar = " → ".join(b.__name__ for b in bloklar)
    muammolar = olik_kod_tekshir(bloklar)
    print(f"  {nom}: {nomlar}")
    if muammolar:
        for _, m in muammolar:
            print(f"    ❌ {m}")
    else:
        print(f"    ✅ o'lik kod yo'q")
    print()

Natijaning muhim qismi:

text
=== ⭐ 1. Tartib qoidasi ===

  Kirish             To`g`ri tartib                 Noto`g`ri
  ────────────────────────────────────────────────────────────────────────────
  {"a": 1}           ✅ {'a': 1}                    ✅ {'a': 1}
  {buzilgan}         JSONDecodeError: Expecting p   ValueError: Expecting pr ⚠️
  not json           JSONDecodeError: Expecting v   ValueError: Expecting va ⚠️

=== ⭐ 3. Guruh ushlash ===

  except LookupError:
    → KeyError, IndexError
    ✅ hammasi ushlanadi

  ⚠️ MUHIM:
     KeyboardInterrupt va SystemExit — Exception EMAS

=== ⭐ 5. Qaysi blok ishlaydi ===

  Istisno                    Qaysi blok
  ────────────────────────────────────────────────────────
  KeyError                   1: KeyError
  IndexError                 2: LookupError
  UnicodeDecodeError         3: (ValueError, TypeError)
  ConnectionError            4: OSError
  JSONDecodeError            3: (ValueError, TypeError)

=== 6. O'lik kodni topish ===

  ⚠️ 1: ValueError → JSONDecodeError
    ❌ except JSONDecodeError (#2) — o'lik kod: ValueError (#1) uni
       allaqachon ushlaydi

Nima ko'rsatdi: 2.1, 2.2, 2.3-bo'limlar.

Misol 2 — ExceptionGroup va except*

python
"""Bir vaqtda bir nechta istisno (3.11+)."""

from __future__ import annotations

import sys
import traceback

print(f"=== Python {sys.version_info.major}."
      f"{sys.version_info.minor} ===\n")

YANGI = sys.version_info >= (3, 11)

if not YANGI:
    print("  ⚠️ ExceptionGroup Python 3.11+ da mavjud")
    print("     Quyidagi misollar taqlid qilinadi\n")


print("=== ⚠️ 1. Muammo: faqat birinchi xato ===\n")


def ishlar_eski(malumot: list) -> list:
    """⚠️ Birinchi xatoda to'xtaydi."""
    natija = []
    for x in malumot:
        natija.append(int(x) * 2)
    return natija


def ishlar_yigish(malumot: list) -> tuple[list, list]:
    """⚠️ Xatolar yig'iladi, lekin faqat birinchisi ko'tariladi."""
    natija, xatolar = [], []
    for x in malumot:
        try:
            natija.append(int(x) * 2)
        except (ValueError, TypeError) as e:
            xatolar.append(e)
    if xatolar:
        raise xatolar[0]                        # ⚠️ qolganlari YO'QOLDI
    return natija, xatolar


MALUMOT = [1, "2", "uch", 4, None, "olti", 7, [8]]

print(f"  Kirish: {MALUMOT}\n")

try:
    ishlar_eski(MALUMOT)
except Exception as e:
    print(f"  Eski usul:  ❌ {type(e).__name__}: {e}")
    print(f"              ⚠️ Faqat BIRINCHI xato ko'rindi")

try:
    ishlar_yigish(MALUMOT)
except Exception as e:
    print(f"\n  Yig'ish:    ❌ {type(e).__name__}: {e}")
    print(f"              ⚠️ Yana faqat birinchisi ko'tarildi")


print("\n\n=== ⭐ 2. ExceptionGroup ===\n")

if YANGI:
    def ishlar_guruh(malumot: list) -> list:
        """⭐ BARCHA xatolarni bir guruhda."""
        natija, xatolar = [], []
        for i, x in enumerate(malumot):
            try:
                natija.append(int(x) * 2)
            except (ValueError, TypeError) as e:
                e.add_note(f"Indeks {i}: {x!r}")
                xatolar.append(e)
        if xatolar:
            raise ExceptionGroup(
                f"{len(xatolar)} elementda xato", xatolar
            )
        return natija

    try:
        ishlar_guruh(MALUMOT)
    except ExceptionGroup as eg:
        print(f"  ❌ {type(eg).__name__}: {eg.message}")
        print(f"     Ichida: {len(eg.exceptions)} istisno\n")
        for i, e in enumerate(eg.exceptions, 1):
            izoh = (f" [{e.__notes__[0]}]"
                    if hasattr(e, "__notes__") else "")
            print(f"     {i}. {type(e).__name__}: "
                  f"{str(e)[:40]}{izoh}")
else:
    print(f"  ⚠️ Python 3.11+ kerak")


print("\n\n=== ⭐ 3. except* ===\n")

if YANGI:
    def guruh_yasa():
        raise ExceptionGroup("Aralash xatolar", [
            ValueError("qiymat-1"),
            TypeError("tur-1"),
            ValueError("qiymat-2"),
            KeyError("kalit-1"),
            TypeError("tur-2"),
            ValueError("qiymat-3"),
        ])

    print(f"  Guruh: 3 ValueError, 2 TypeError, 1 KeyError\n")

    try:
        guruh_yasa()
    except* ValueError as eg:
        print(f"    ⭐ except* ValueError: {len(eg.exceptions)} ta")
        for e in eg.exceptions:
            print(f"       - {e}")
    except* TypeError as eg:
        print(f"    ⭐ except* TypeError: {len(eg.exceptions)} ta")
        for e in eg.exceptions:
            print(f"       - {e}")
    except* KeyError as eg:
        print(f"    ⭐ except* KeyError: {len(eg.exceptions)} ta")

    print(f"""
  ⭐ MUHIM FARQ:

     except   → FAQAT BITTA blok ishlaydi
     except*  → ⭐ BIR NECHTA blok ishlaydi

     Har blok o'z turidagi istisnolarni oladi.
""")

    print(f"  Bir qismi ushlanmasa:\n")
    try:
        try:
            guruh_yasa()
        except* ValueError as eg:
            print(f"    ValueError: {len(eg.exceptions)} ta ushlandi")
    except ExceptionGroup as eg:
        print(f"    ⭐ Qolgani UZATILDI: {len(eg.exceptions)} ta")
        for e in eg.exceptions:
            print(f"       - {type(e).__name__}: {e}")
else:
    print(f"  ⚠️ Python 3.11+ kerak")


print("\n\n=== 4. Ichma-ich guruhlar ===\n")

if YANGI:
    ichki = ExceptionGroup("Ichki xatolar", [
        ValueError("ichki-1"),
        TypeError("ichki-2"),
    ])

    tashqi = ExceptionGroup("Tashqi xatolar", [
        KeyError("tashqi-1"),
        ichki,                                  # ⭐ ichma-ich
        IndexError("tashqi-2"),
    ])

    def chiz(eg, chuqurlik=0):
        q = []
        belgi = "  " * chuqurlik
        if isinstance(eg, BaseExceptionGroup):
            q.append(f"{belgi}📦 {type(eg).__name__}: {eg.message} "
                     f"({len(eg.exceptions)} ta)")
            for e in eg.exceptions:
                q.extend(chiz(e, chuqurlik + 1))
        else:
            q.append(f"{belgi}└─ {type(eg).__name__}: {eg}")
        return q

    print(f"  Tuzilma:")
    for q in chiz(tashqi):
        print(f"    {q}")

    print(f"\n  ⭐ except* ichma-ich guruhlarni ham topadi:\n")
    try:
        raise tashqi
    except* ValueError as eg:
        print(f"    ValueError: {len(eg.exceptions)} ta guruh")
        for e in eg.exceptions:
            print(f"      {chiz(e)[0].strip()}")
    except* (KeyError, IndexError) as eg:
        print(f"    KeyError/IndexError: {len(eg.exceptions)} ta")
    except* TypeError as eg:
        print(f"    TypeError: {len(eg.exceptions)} ta")

    print(f"""
  ⭐ except* ichma-ich guruhlarni AVTOMATIK yassilaydi
     va faqat mos turlarni oladi.

  ⭐ subgroup / split metodlari:
""")

    faqat_value = tashqi.subgroup(ValueError)
    print(f"    tashqi.subgroup(ValueError):")
    for q in chiz(faqat_value):
        print(f"      {q}")

    mos, qolgan = tashqi.split(ValueError)
    print(f"\n    tashqi.split(ValueError):")
    print(f"      mos:    {len(mos.exceptions) if mos else 0} ta")
    print(f"      qolgan: {len(qolgan.exceptions) if qolgan else 0} ta")
else:
    print(f"  ⚠️ Python 3.11+ kerak")


print("\n\n=== ⚠️ 5. Cheklovlar ===\n")

print("""  ⚠️ 1. except va except* ni ARALASHTIRIB bo'lmaydi:

     try: ...
     except* ValueError: ...
     except TypeError: ...           ❌ SyntaxError


  ⚠️ 2. except* da break/continue/return YO'Q:

     for x in ...:
         try: ...
         except* ValueError:
             break                   ❌ SyntaxError


  ⚠️ 3. ExceptionGroup BaseException ni qabul qilmaydi:

     ExceptionGroup("x", [KeyboardInterrupt()])   ❌ TypeError
     BaseExceptionGroup("x", [KeyboardInterrupt()])  ✅


  ⚠️ 4. Bo'sh guruh mumkin emas:

     ExceptionGroup("x", [])         ❌ ValueError


  ⭐ 5. except* BITTA istisnoni ham guruh qiladi:

     try:
         raise ValueError("bitta")
     except* ValueError as eg:
         type(eg)                    ExceptionGroup ⭐
         len(eg.exceptions)          1
""")

if YANGI:
    print(f"  Tekshiruv:\n")
    for tavsif, f in [
        ("ExceptionGroup('x', [])",
         lambda: ExceptionGroup("x", [])),
        ("ExceptionGroup('x', [KeyboardInterrupt()])",
         lambda: ExceptionGroup("x", [KeyboardInterrupt()])),
        ("BaseExceptionGroup('x', [KeyboardInterrupt()])",
         lambda: BaseExceptionGroup("x", [KeyboardInterrupt()])),
        ("ExceptionGroup('x', [ValueError()])",
         lambda: ExceptionGroup("x", [ValueError()])),
    ]:
        try:
            n = f"✅ {type(f()).__name__}"
        except (TypeError, ValueError) as e:
            n = f"❌ {type(e).__name__}: {str(e)[:34]}"
        print(f"    {tavsif:<48} {n}")

    print(f"\n  Bitta istisno except* bilan:")
    try:
        raise ValueError("bitta")
    except* ValueError as eg:
        print(f"    type(eg) = {type(eg).__name__}")
        print(f"    len(eg.exceptions) = {len(eg.exceptions)}")
        print(f"    eg.exceptions[0] = {eg.exceptions[0]!r}")


print("\n\n=== ⭐ 6. Amaliy: parallel ishlar ===\n")

import concurrent.futures
import time


def ish(n: int) -> str:
    """Ba'zilari xato beradi."""
    time.sleep(0.001)
    if n % 4 == 0:
        raise ValueError(f"Ish {n}: qiymat xatosi")
    if n % 5 == 0:
        raise TypeError(f"Ish {n}: tur xatosi")
    if n % 7 == 0:
        raise ConnectionError(f"Ish {n}: tarmoq xatosi")
    return f"✅ Ish {n}"


def parallel_eski(ishlar: list[int]):
    """⚠️ Birinchi xato butun ishni to'xtatadi."""
    natijalar = []
    with concurrent.futures.ThreadPoolExecutor(4) as ex:
        for n in ex.map(ish, ishlar):
            natijalar.append(n)
    return natijalar


def parallel_guruh(ishlar: list[int]):
    """⭐ Barcha xatolar yig'iladi."""
    natijalar, xatolar = [], []
    with concurrent.futures.ThreadPoolExecutor(4) as ex:
        kelajaklar = {ex.submit(ish, n): n for n in ishlar}
        for k in concurrent.futures.as_completed(kelajaklar):
            n = kelajaklar[k]
            try:
                natijalar.append(k.result())
            except Exception as e:
                if YANGI:
                    e.add_note(f"Ish raqami: {n}")
                xatolar.append(e)
    if xatolar and YANGI:
        raise ExceptionGroup(
            f"{len(xatolar)}/{len(ishlar)} ish bajarilmadi", xatolar
        )
    elif xatolar:
        raise xatolar[0]
    return natijalar


ISHLAR = list(range(1, 16))

print(f"  {len(ISHLAR)} ta parallel ish\n")

try:
    parallel_eski(ISHLAR)
except Exception as e:
    print(f"  Eski usul: ❌ {type(e).__name__}: {e}")
    print(f"             ⚠️ Qolgan xatolar ko'rinmadi\n")

try:
    n = parallel_guruh(ISHLAR)
    print(f"  ✅ Hammasi bajarildi: {len(n)}")
except Exception as e:
    if YANGI and isinstance(e, ExceptionGroup):
        print(f"  ⭐ ExceptionGroup: {e.message}\n")
        try:
            raise e
        except* ValueError as eg:
            print(f"    ValueError ({len(eg.exceptions)}):")
            for x in eg.exceptions:
                print(f"      - {x}")
        except* TypeError as eg:
            print(f"    TypeError ({len(eg.exceptions)}):")
            for x in eg.exceptions:
                print(f"      - {x}")
        except* ConnectionError as eg:
            print(f"    ConnectionError ({len(eg.exceptions)}):")
            for x in eg.exceptions:
                print(f"      - {x}")
    else:
        print(f"  ❌ {type(e).__name__}: {e}")

print(f"""
  ⭐ ExceptionGroup NIMA UCHUN KERAK:

     • asyncio.TaskGroup (3.11+)
     • concurrent.futures — parallel ishlar
     • Ma'lumot validatsiyasi (barcha xatolar)
     • Quvurlar — har bosqichdagi xatolar
     • Test to'plamlari

  ⭐ Foydalanuvchi BARCHA muammolarni bir marta ko'radi.
""")

Natijaning muhim qismi:

text
=== ⚠️ 1. Muammo: faqat birinchi xato ===

  Eski usul:  ❌ ValueError: invalid literal for int() with base 10: 'uch'
              ⚠️ Faqat BIRINCHI xato ko'rindi

=== ⭐ 2. ExceptionGroup ===

  ❌ ExceptionGroup: 4 elementda xato
     Ichida: 4 istisno

     1. ValueError: invalid literal... [Indeks 2: 'uch']
     2. TypeError: int() argument must... [Indeks 4: None]
     3. ValueError: invalid literal... [Indeks 5: 'olti']
     4. TypeError: int() argument must... [Indeks 7: [8]]

=== ⭐ 3. except* ===

    ⭐ except* ValueError: 3 ta
    ⭐ except* TypeError: 2 ta
    ⭐ except* KeyError: 1 ta

  ⭐ MUHIM FARQ:
     except   → FAQAT BITTA blok ishlaydi
     except*  → ⭐ BIR NECHTA blok ishlaydi

Nima ko'rsatdi: 2.5, 2.6-bo'limlar.

Misol 3 — Toifalarga ajratish strategiyasi

python
"""Xatolarni toifalab, mos strategiya tanlash."""

from __future__ import annotations

import io
import json
import logging
import random
import time
from dataclasses import dataclass, field
from enum import Enum
from typing import Any, Callable

print("=== XATOLARNI TOIFALASH ===\n")

jurnal = logging.getLogger("toifa")
jurnal.setLevel(logging.DEBUG)
_b = io.StringIO()
_h = logging.StreamHandler(_b)
_h.setFormatter(logging.Formatter("%(levelname)-8s %(message)s"))
jurnal.addHandler(_h)
jurnal.propagate = False


class Toifa(str, Enum):
    """⭐ Xato toifalari — har biri o'z strategiyasi bilan."""

    VAQTINCHALIK = "vaqtinchalik"    # qayta urin
    MALUMOT = "malumot"              # foydalanuvchi tuzatadi
    RUXSAT = "ruxsat"                # autentifikatsiya
    RESURS = "resurs"                # tizim resursi
    DASTUR = "dastur"                # dasturchi xatosi
    NOMALUM = "nomalum"

    def __str__(self): return self.value


@dataclass
class Strategiya:
    """Toifa uchun ish tartibi."""

    qayta_urin: bool = False
    urinishlar: int = 1
    jurnal_darajasi: int = logging.ERROR
    http_kodi: int = 500
    foydalanuvchiga: str = "Ichki xato"
    xabar_yubor: bool = False


STRATEGIYALAR: dict[Toifa, Strategiya] = {
    Toifa.VAQTINCHALIK: Strategiya(
        qayta_urin=True, urinishlar=3,
        jurnal_darajasi=logging.WARNING, http_kodi=503,
        foydalanuvchiga="Xizmat vaqtincha mavjud emas",
    ),
    Toifa.MALUMOT: Strategiya(
        jurnal_darajasi=logging.INFO, http_kodi=400,
        foydalanuvchiga="Yuborilgan ma'lumot noto'g'ri",
    ),
    Toifa.RUXSAT: Strategiya(
        jurnal_darajasi=logging.WARNING, http_kodi=403,
        foydalanuvchiga="Ruxsat yo'q",
    ),
    Toifa.RESURS: Strategiya(
        jurnal_darajasi=logging.ERROR, http_kodi=507,
        foydalanuvchiga="Tizim resursi yetarli emas",
        xabar_yubor=True,
    ),
    Toifa.DASTUR: Strategiya(
        jurnal_darajasi=logging.CRITICAL, http_kodi=500,
        foydalanuvchiga="Ichki xato",
        xabar_yubor=True,
    ),
    Toifa.NOMALUM: Strategiya(
        jurnal_darajasi=logging.ERROR, http_kodi=500,
        foydalanuvchiga="Kutilmagan xato",
        xabar_yubor=True,
    ),
}


# ⭐ Tartib MUHIM — aniqdan umumiyga
TOIFA_XARITASI: list[tuple[tuple[type, ...], Toifa]] = [
    # 1. Eng aniq — tarmoq
    ((ConnectionError, TimeoutError, BlockingIOError,
      InterruptedError), Toifa.VAQTINCHALIK),

    # 2. Ruxsat
    ((PermissionError,), Toifa.RUXSAT),

    # 3. Resurs
    ((MemoryError, RecursionError, BufferError), Toifa.RESURS),

    # 4. Ma'lumot
    ((ValueError, TypeError, KeyError, IndexError,
      json.JSONDecodeError, UnicodeError, ArithmeticError),
     Toifa.MALUMOT),

    # 5. Dastur xatolari
    ((AttributeError, NameError, ImportError, AssertionError,
      NotImplementedError), Toifa.DASTUR),

    # 6. Qolgan OS xatolari
    ((OSError,), Toifa.RESURS),
]


def toifa_aniqla(e: BaseException) -> Toifa:
    """⭐ Istisno turiga qarab toifa."""
    for turlar, toifa in TOIFA_XARITASI:
        if isinstance(e, turlar):
            return toifa
    return Toifa.NOMALUM


print("1. Toifalash jadvali:\n")

SINOV_XATOLARI = [
    ConnectionResetError("ulanish uzildi"),
    TimeoutError("vaqt tugadi"),
    PermissionError("ruxsat yo'q"),
    MemoryError("xotira tugadi"),
    ValueError("noto'g'ri qiymat"),
    KeyError("kalit"),
    json.JSONDecodeError("Expecting value", "{", 0),
    ZeroDivisionError("nolga bo'lish"),
    UnicodeDecodeError("utf-8", b"\xff", 0, 1, "invalid"),
    AttributeError("atribut yo'q"),
    NameError("nom yo'q"),
    FileNotFoundError("fayl yo'q"),
    IsADirectoryError("papka"),
    RuntimeError("noma'lum"),
]

print(f"  {'Istisno':<26} {'Toifa':<16} {'HTTP':<7} {'Qayta urin'}")
print("  " + "─" * 62)
for e in SINOV_XATOLARI:
    t = toifa_aniqla(e)
    s = STRATEGIYALAR[t]
    print(f"  {type(e).__name__:<26} {str(t):<16} {s.http_kodi:<7} "
          f"{'✅ ' + str(s.urinishlar) if s.qayta_urin else '❌'}")


print("\n\n2. ⭐ Strategiya qo'llash:\n")


@dataclass
class Javob:
    """HTTP javobi."""
    kod: int
    tana: dict[str, Any]
    urinishlar: int = 1

    def __str__(self):
        return f"HTTP {self.kod}: {self.tana}"


class Ishlovchi:
    """⭐ Toifaga qarab strategiya qo'llaydi."""

    def __init__(self):
        self.xabarlar: list[str] = []
        self.statistika: dict[str, int] = {}

    def bajar(self, f: Callable, *args, **kwargs) -> Javob:
        toifa = None
        oxirgi_xato = None
        urinish = 0

        while True:
            urinish += 1
            try:
                natija = f(*args, **kwargs)
            except BaseException as e:
                if isinstance(e, (KeyboardInterrupt, SystemExit)):
                    raise                       # ⭐ hech qachon yutmaymiz
                oxirgi_xato = e
                toifa = toifa_aniqla(e)
                s = STRATEGIYALAR[toifa]

                self.statistika[str(toifa)] = \
                    self.statistika.get(str(toifa), 0) + 1

                jurnal.log(
                    s.jurnal_darajasi,
                    "[%s] %s: %s (urinish %d/%d)",
                    toifa, type(e).__name__, e, urinish, s.urinishlar,
                )

                if s.qayta_urin and urinish < s.urinishlar:
                    time.sleep(0.001 * (2 ** (urinish - 1)))
                    continue

                if s.xabar_yubor:
                    self.xabarlar.append(
                        f"⚠️ {toifa}: {type(e).__name__}: {e}"
                    )

                return Javob(
                    kod=s.http_kodi,
                    tana={"xato": s.foydalanuvchiga,
                          "toifa": str(toifa)},
                    urinishlar=urinish,
                )
            else:
                return Javob(200, {"natija": natija},
                             urinishlar=urinish)


# ═══ SINOV FUNKSIYALARI ═══

HOLAT = {"urinish": 0}


def vaqtinchalik_xato():
    """3-urinishda ishlaydi."""
    HOLAT["urinish"] += 1
    if HOLAT["urinish"] < 3:
        raise ConnectionResetError("ulanish uzildi")
    return "✅ ishladi"


def doimiy_vaqtinchalik():
    raise TimeoutError("har doim vaqt tugaydi")


def malumot_xatosi():
    return json.loads("{buzilgan}")


def ruxsat_xatosi():
    raise PermissionError("faylga yozib bo'lmaydi")


def dastur_xatosi():
    return None.mavjud_emas


def resurs_xatosi():
    raise MemoryError("xotira tugadi")


def muvaffaqiyat():
    return {"malumot": [1, 2, 3]}


ish = Ishlovchi()

FUNKSIYALAR = [
    ("Muvaffaqiyat",            muvaffaqiyat),
    ("Vaqtinchalik (tuzaladi)", vaqtinchalik_xato),
    ("Vaqtinchalik (doimiy)",   doimiy_vaqtinchalik),
    ("Ma'lumot xatosi",         malumot_xatosi),
    ("Ruxsat xatosi",           ruxsat_xatosi),
    ("Dastur xatosi",           dastur_xatosi),
    ("Resurs xatosi",           resurs_xatosi),
]

print(f"  {'Holat':<26} {'Javob':<44} {'Urinish'}")
print("  " + "─" * 78)
for nom, f in FUNKSIYALAR:
    HOLAT["urinish"] = 0
    j = ish.bajar(f)
    tana = str(j.tana)[:40]
    print(f"  {nom:<26} HTTP {j.kod}: {tana:<36} {j.urinishlar}")


print("\n\n3. Statistika:\n")

print(f"  {'Toifa':<18} {'Soni':<8} {'Diagramma'}")
print("  " + "─" * 42)
for toifa, n in sorted(ish.statistika.items(), key=lambda x: -x[1]):
    print(f"  {toifa:<18} {n:<8} {'█' * n}")

print(f"\n  Xabar yuborilgan xatolar ({len(ish.xabarlar)}):")
for x in ish.xabarlar:
    print(f"    {x}")


print("\n\n4. ⭐ Toifalar bo'yicha except:\n")


def toifali_ishlov(f: Callable) -> str:
    """⭐ Toifalar bo'yicha ANIQ except bloklari."""
    try:
        return f"✅ {f()}"

    # ⭐ 1. VAQTINCHALIK — qayta urin
    except (ConnectionError, TimeoutError, BlockingIOError) as e:
        return f"🔄 Qayta urinish kerak: {type(e).__name__}"

    # ⭐ 2. RUXSAT — autentifikatsiya
    except PermissionError as e:
        return f"🔒 Ruxsat yo'q: {e}"

    # ⭐ 3. RESURS — tizim
    except (MemoryError, RecursionError) as e:
        return f"💾 Resurs tugadi: {type(e).__name__}"

    # ⭐ 4. MA'LUMOT — foydalanuvchi
    except json.JSONDecodeError as e:
        return f"📄 JSON xatosi: {e.msg} (qator {e.lineno})"
    except (ValueError, TypeError, KeyError, IndexError) as e:
        return f"📝 Ma'lumot xatosi: {type(e).__name__}"
    except UnicodeError as e:
        return f"🔤 Kodlash xatosi: {type(e).__name__}"
    except ArithmeticError as e:
        return f"🔢 Hisoblash xatosi: {type(e).__name__}"

    # ⭐ 5. DASTUR — bizning xatomiz
    except (AttributeError, NameError, ImportError) as e:
        jurnal.critical("Dastur xatosi: %s", e)
        return f"🐛 Dastur xatosi: {type(e).__name__}"

    # ⭐ 6. Qolgan OS xatolari
    except OSError as e:
        return f"💿 Tizim xatosi: {type(e).__name__} (errno={e.errno})"

    # ⭐ 7. Boshqa hammasi
    except Exception as e:
        jurnal.exception("Kutilmagan xato")
        return f"❓ Noma'lum: {type(e).__name__}"


SINOVLAR = [
    ("muvaffaqiyat",        muvaffaqiyat),
    ("ConnectionError",     lambda: doimiy_vaqtinchalik()),
    ("PermissionError",     ruxsat_xatosi),
    ("MemoryError",         resurs_xatosi),
    ("JSONDecodeError",     malumot_xatosi),
    ("ValueError",          lambda: int("abc")),
    ("UnicodeDecodeError",  lambda: b"\xff".decode("utf-8")),
    ("ZeroDivisionError",   lambda: 1 / 0),
    ("AttributeError",      dastur_xatosi),
    ("FileNotFoundError",   lambda: open("yoq_99999.txt")),
    ("RuntimeError",        lambda: (_ for _ in ()).throw(
        RuntimeError("noma'lum"))),
]

for nom, f in SINOVLAR:
    print(f"  {nom:<22} {toifali_ishlov(f)}")


print("\n\n5. ⭐ Toifa xaritasini tekshirish:\n")


def xarita_tekshir() -> list[str]:
    """⭐ O'lik kod va yetishmayotgan turlarni topadi."""
    muammolar = []
    korilgan: list[type] = []

    for i, (turlar, toifa) in enumerate(TOIFA_XARITASI):
        for T in turlar:
            for K in korilgan:
                if issubclass(T, K):
                    muammolar.append(
                        f"⚠️ {T.__name__} ({toifa}) — o'lik: "
                        f"{K.__name__} uni allaqachon ushlaydi"
                    )
                    break
            korilgan.append(T)

    # Qamrab olinmagan muhim turlar
    MUHIM = [
        StopIteration, GeneratorExit, SystemError, ReferenceError,
        EOFError, KeyboardInterrupt, SystemExit,
    ]
    for T in MUHIM:
        qamragan = any(
            issubclass(T, K) for turlar, _ in TOIFA_XARITASI
            for K in turlar
        )
        if not qamragan:
            xavfsiz = issubclass(T, (KeyboardInterrupt, SystemExit,
                                     GeneratorExit))
            muammolar.append(
                f"{'ℹ️' if xavfsiz else '⚠️'} {T.__name__} "
                f"qamrab olinmagan → NOMALUM"
                f"{' (ataylab ✅)' if xavfsiz else ''}"
            )
    return muammolar


for m in xarita_tekshir():
    print(f"  {m}")

print(f"""
  ⭐ TOIFALASH STRATEGIYASI AFZALLIKLARI:

     1. Har toifa uchun ANIQ ish tartibi
     2. Yangi istisno turi → xaritaga bir qator
     3. HTTP kodlari markazlashgan
     4. Jurnal darajasi mos (INFO/WARNING/CRITICAL)
     5. Qayta urinish faqat kerakli joyda
     6. Xabar yuborish faqat jiddiy xatolarda

  ⚠️ TARTIB MUHIM: aniqdan umumiyga
     TimeoutError OSError avlodi — agar OSError avval
     bo'lsa, u VAQTINCHALIK emas RESURS bo'lardi.
""")


print("\n6. Jurnal:\n")
for q in _b.getvalue().rstrip().split("\n")[:10]:
    print(f"  {q}")

Natijaning muhim qismi:

text
1. Toifalash jadvali:

  Istisno                    Toifa            HTTP    Qayta urin
  ──────────────────────────────────────────────────────────────
  ConnectionResetError       vaqtinchalik     503     ✅ 3
  TimeoutError               vaqtinchalik     503     ✅ 3
  PermissionError            ruxsat           403     ❌
  MemoryError                resurs           507     ❌
  JSONDecodeError            malumot          400     ❌
  AttributeError             dastur           500     ❌
  FileNotFoundError          resurs           507     ❌

2. ⭐ Strategiya qo'llash:

  Holat                      Javob                              Urinish
  ──────────────────────────────────────────────────────────────────────
  Vaqtinchalik (tuzaladi)    HTTP 200: {'natija': '✅ ishladi'}        3
  Vaqtinchalik (doimiy)      HTTP 503: {'xato': 'Xizmat vaqtincha mavjud emas', 3
  Ma'lumot xatosi            HTTP 400: {'xato': "Yuborilgan ma'lumot noto'g'ri" 1

5. ⭐ Toifa xaritasini tekshirish:

  ℹ️ KeyboardInterrupt qamrab olinmagan → NOMALUM (ataylab ✅)
  ℹ️ SystemExit qamrab olinmagan → NOMALUM (ataylab ✅)

Nima ko'rsatdi: 2.3, 2.4-bo'limlar.

Misol 4 — Amaliy: validatsiya tizimi

python
"""Barcha xatolarni yig'ib, bir marta ko'rsatish."""

from __future__ import annotations

import re
import sys
from contextlib import contextmanager
from dataclasses import dataclass, field
from datetime import date, datetime
from decimal import Decimal, InvalidOperation
from typing import Any, Callable, Iterator

print("=== VALIDATSIYA TIZIMI ===\n")

YANGI = sys.version_info >= (3, 11)


class ValidatsiyaXatosi(Exception):
    """Bitta maydon xatosi."""

    def __init__(self, xabar: str, yol: str = "", qiymat: Any = None):
        self.yol = yol
        self.qiymat = qiymat
        super().__init__(f"{yol}: {xabar}" if yol else xabar)


class MajburiyXato(ValidatsiyaXatosi):
    """Maydon yo'q."""


class TurXatosi(ValidatsiyaXatosi):
    """Tur mos emas."""


class QiymatXatosi(ValidatsiyaXatosi):
    """Qiymat yaroqsiz."""


class FormatXatosi(QiymatXatosi):
    """Format mos emas."""


# ═══ XATOLARNI YIG'ISH ═══

class Yiguvchi:
    """⭐ Xatolarni yig'ib, oxirida birga ko'taradi."""

    def __init__(self, yol: str = "", maks: int | None = None):
        self.yol = yol
        self.maks = maks
        self.xatolar: list[ValidatsiyaXatosi] = []

    @contextmanager
    def ushla(self, *turlar: type) -> Iterator[None]:
        """⭐ Bitta amalning xatosini yig'adi."""
        turlar = turlar or (ValidatsiyaXatosi,)
        try:
            yield
        except turlar as e:
            self.qosh(e)

    def qosh(self, e: ValidatsiyaXatosi) -> None:
        if self.maks and len(self.xatolar) >= self.maks:
            return
        self.xatolar.append(e)

    def ichki(self, yol: str) -> "Yiguvchi":
        """⭐ Ichma-ich obyekt uchun."""
        toliq = f"{self.yol}.{yol}" if self.yol else yol
        return Yiguvchi(toliq, self.maks)

    def birlashtir(self, boshqa: "Yiguvchi") -> None:
        self.xatolar.extend(boshqa.xatolar)

    def __bool__(self) -> bool:
        return bool(self.xatolar)

    def __len__(self) -> int:
        return len(self.xatolar)

    def kotar(self, xabar: str = "Validatsiya xatolari") -> None:
        """⭐ ExceptionGroup yoki birinchi xato."""
        if not self.xatolar:
            return
        if YANGI:
            raise ExceptionGroup(
                f"{xabar} ({len(self.xatolar)} ta)", self.xatolar
            )
        raise self.xatolar[0]

    def hisobot(self) -> str:
        if not self.xatolar:
            return "✅ Xato yo'q"
        q = [f"❌ {len(self.xatolar)} ta xato:"]
        toifalar: dict[str, list[ValidatsiyaXatosi]] = {}
        for e in self.xatolar:
            toifalar.setdefault(type(e).__name__, []).append(e)
        for tur, ro in sorted(toifalar.items()):
            q.append(f"  {tur} ({len(ro)}):")
            for e in ro:
                qiymat = (f" (qiymat: {e.qiymat!r})"
                          if e.qiymat is not None else "")
                q.append(f"    • {e}{qiymat}")
        return "\n".join(q)


# ═══ TEKSHIRUVCHILAR ═══

def tekshir_majburiy(m: dict, kalit: str, yol: str) -> Any:
    if kalit not in m or m[kalit] in (None, ""):
        raise MajburiyXato("majburiy maydon", f"{yol}.{kalit}"
                           if yol else kalit)
    return m[kalit]


def tekshir_satr(q: Any, yol: str, *, eng_kam: int = 0,
                 eng_kop: int | None = None,
                 naqsh: str = "") -> str:
    if not isinstance(q, str):
        raise TurXatosi(f"satr kutildi, {type(q).__name__} keldi",
                        yol, q)
    q = q.strip()
    if len(q) < eng_kam:
        raise QiymatXatosi(f"kamida {eng_kam} belgi ({len(q)} keldi)",
                           yol, q)
    if eng_kop and len(q) > eng_kop:
        raise QiymatXatosi(f"ko'pi bilan {eng_kop} belgi", yol, q)
    if naqsh and not re.fullmatch(naqsh, q):
        raise FormatXatosi("format mos emas", yol, q)
    return q


def tekshir_butun(q: Any, yol: str, *, eng_kam: int | None = None,
                  eng_kop: int | None = None) -> int:
    if isinstance(q, bool) or not isinstance(q, (int, str)):
        raise TurXatosi(f"butun son kutildi, {type(q).__name__} keldi",
                        yol, q)
    try:
        n = int(q)
    except ValueError as e:
        raise TurXatosi(f"butun songa o'girib bo'lmaydi", yol, q) from e
    if eng_kam is not None and n < eng_kam:
        raise QiymatXatosi(f">= {eng_kam} bo'lsin", yol, n)
    if eng_kop is not None and n > eng_kop:
        raise QiymatXatosi(f"<= {eng_kop} bo'lsin", yol, n)
    return n


def tekshir_pul(q: Any, yol: str, *, eng_kam: str = "0") -> Decimal:
    try:
        d = Decimal(str(q).replace(" ", "").replace(",", ""))
    except (InvalidOperation, TypeError) as e:
        raise TurXatosi("summa emas", yol, q) from e
    if d < Decimal(eng_kam):
        raise QiymatXatosi(f">= {eng_kam} bo'lsin", yol, d)
    return d


def tekshir_sana(q: Any, yol: str, *, kelajak: bool = True) -> date:
    if isinstance(q, date):
        d = q
    else:
        for fmt in ("%Y-%m-%d", "%d.%m.%Y"):
            try:
                d = datetime.strptime(str(q).strip(), fmt).date()
                break
            except ValueError:
                continue
        else:
            raise FormatXatosi("sana formati YYYY-MM-DD yoki DD.MM.YYYY",
                               yol, q)
    if not kelajak and d > date.today():
        raise QiymatXatosi("kelajakda bo'lmasin", yol, d)
    return d


def tekshir_email(q: Any, yol: str) -> str:
    s = tekshir_satr(q, yol, eng_kam=5, eng_kop=254)
    if not re.fullmatch(r"[^@\s]+@[^@\s]+\.[a-zA-Z]{2,}", s):
        raise FormatXatosi("email formati noto'g'ri", yol, s)
    return s.lower()


# ═══ MODEL VALIDATSIYASI ═══

def xodim_tekshir(m: dict, y: Yiguvchi) -> dict | None:
    """⭐ HAR maydon alohida — barcha xatolar yig'iladi."""
    natija: dict[str, Any] = {}

    with y.ushla():
        natija["id"] = tekshir_butun(
            tekshir_majburiy(m, "id", y.yol), f"{y.yol}.id", eng_kam=1
        )

    with y.ushla():
        natija["ism"] = tekshir_satr(
            tekshir_majburiy(m, "ism", y.yol), f"{y.yol}.ism",
            eng_kam=2, eng_kop=100,
        )

    with y.ushla():
        natija["email"] = tekshir_email(
            tekshir_majburiy(m, "email", y.yol), f"{y.yol}.email"
        )

    with y.ushla():
        natija["maosh"] = tekshir_pul(
            tekshir_majburiy(m, "maosh", y.yol), f"{y.yol}.maosh",
            eng_kam="500000",
        )

    if "yosh" in m:
        with y.ushla():
            natija["yosh"] = tekshir_butun(
                m["yosh"], f"{y.yol}.yosh", eng_kam=16, eng_kop=100
            )

    if "ishga_kirgan" in m:
        with y.ushla():
            natija["ishga_kirgan"] = tekshir_sana(
                m["ishga_kirgan"], f"{y.yol}.ishga_kirgan",
                kelajak=False,
            )

    # ⭐ Ichma-ich obyekt
    if "manzil" in m:
        ichki = y.ichki("manzil")
        manzil = manzil_tekshir(m["manzil"], ichki)
        y.birlashtir(ichki)
        if manzil:
            natija["manzil"] = manzil

    return natija if not y else None


def manzil_tekshir(m: Any, y: Yiguvchi) -> dict | None:
    if not isinstance(m, dict):
        y.qosh(TurXatosi(f"obyekt kutildi, {type(m).__name__} keldi",
                         y.yol, m))
        return None
    natija = {}
    with y.ushla():
        natija["shahar"] = tekshir_satr(
            tekshir_majburiy(m, "shahar", y.yol), f"{y.yol}.shahar",
            eng_kam=2,
        )
    with y.ushla():
        natija["kocha"] = tekshir_satr(
            tekshir_majburiy(m, "kocha", y.yol), f"{y.yol}.kocha",
            eng_kam=3,
        )
    if "indeks" in m:
        with y.ushla():
            natija["indeks"] = tekshir_satr(
                m["indeks"], f"{y.yol}.indeks", naqsh=r"\d{6}"
            )
    return natija if not y else None


# ═══ SINOVLAR ═══

XODIMLAR = [
    # ✅ To'liq to'g'ri
    {
        "id": 1, "ism": "Aziz Karimov", "email": "aziz@wisar.uz",
        "maosh": "8500000", "yosh": 30, "ishga_kirgan": "2023-03-15",
        "manzil": {"shahar": "Toshkent", "kocha": "Amir Temur 12",
                   "indeks": "100000"},
    },
    # ⚠️ Ko'p xato
    {
        "id": "xato", "ism": "A", "email": "notogri",
        "maosh": "kam", "yosh": 200, "ishga_kirgan": "2099-01-01",
        "manzil": {"shahar": "T", "indeks": "abc"},
    },
    # ⚠️ Majburiy maydonlar yo'q
    {"yosh": 25},
    # ⚠️ Manzil noto'g'ri tur
    {
        "id": 3, "ism": "Bobur Aliyev", "email": "bobur@wisar.uz",
        "maosh": 9500000, "manzil": "Toshkent",
    },
]


print("1. Har xodim alohida:\n")

for i, xodim in enumerate(XODIMLAR, 1):
    y = Yiguvchi(yol=f"xodim[{i}]")
    natija = xodim_tekshir(xodim, y)
    print(f"  ┌─ Xodim {i}")
    if natija:
        print(f"  │ ✅ Muvaffaqiyatli")
        for k, v in natija.items():
            if k != "manzil":
                print(f"  │   {k:<14} {v!r}")
            else:
                print(f"  │   {k:<14} {v}")
    else:
        for q in y.hisobot().split("\n"):
            print(f"  │ {q}")
    print(f"  └{'─' * 60}\n")


print("\n2. ⭐ Barcha xodimlarni birga:\n")

umumiy = Yiguvchi()
natijalar = []

for i, xodim in enumerate(XODIMLAR, 1):
    y = Yiguvchi(yol=f"xodim[{i}]")
    n = xodim_tekshir(xodim, y)
    if n:
        natijalar.append(n)
    umumiy.birlashtir(y)

print(f"  ✅ Yuklandi: {len(natijalar)}/{len(XODIMLAR)}")
print(f"  ❌ Xatolar:  {len(umumiy)}\n")

print(f"  Xatolar toifalari:")
toifalar: dict[str, int] = {}
for e in umumiy.xatolar:
    toifalar[type(e).__name__] = toifalar.get(type(e).__name__, 0) + 1
for tur, n in sorted(toifalar.items(), key=lambda x: -x[1]):
    print(f"    {tur:<20} {n:>3}  {'█' * n}")


print("\n\n3. ⭐ ExceptionGroup bilan ko'tarish:\n")

if YANGI:
    try:
        umumiy.kotar("Xodimlar validatsiyasi")
    except* MajburiyXato as eg:
        print(f"  📋 Majburiy maydonlar ({len(eg.exceptions)}):")
        for e in eg.exceptions[:4]:
            print(f"     • {e}")
    except* TurXatosi as eg:
        print(f"\n  🔤 Tur xatolari ({len(eg.exceptions)}):")
        for e in eg.exceptions[:4]:
            print(f"     • {e}")
    except* FormatXatosi as eg:
        print(f"\n  📐 Format xatolari ({len(eg.exceptions)}):")
        for e in eg.exceptions:
            print(f"     • {e}")
    except* QiymatXatosi as eg:
        print(f"\n  📊 Qiymat xatolari ({len(eg.exceptions)}):")
        for e in eg.exceptions:
            print(f"     • {e}")
else:
    print(f"  ⚠️ Python 3.11+ kerak — birinchi xato:")
    try:
        umumiy.kotar()
    except ValidatsiyaXatosi as e:
        print(f"    {type(e).__name__}: {e}")


print("\n\n4. Xato chegarasi:\n")

for maks in [None, 5, 3, 1]:
    y = Yiguvchi(yol="xodim", maks=maks)
    xodim_tekshir(XODIMLAR[1], y)
    print(f"  maks={str(maks):<6} → {len(y)} xato yig'ildi")


print("\n\n5. ⭐ Yo'l (path) bilan xatolar:\n")

y = Yiguvchi(yol="sorov")
xodim_tekshir(XODIMLAR[1], y)

print(f"  Ichma-ich yo'llar:")
for e in y.xatolar:
    print(f"    {e.yol:<32} {type(e).__name__}")

print(f"""
  ⭐ YO'L (path) FOYDASI:
     • Foydalanuvchi ANIQ qayerda xato ekanini biladi
     • Frontend maydonni belgilashi mumkin
     • Ichma-ich obyektlarda ham ishlaydi

  ⭐ pydantic, marshmallow, cerberus — hammasi shunday
""")


print("\n6. Xulosa:\n")

print(f"""  ⭐ BU TIZIMDA:

     ISTISNO IERARXIYASI:
       ValidatsiyaXatosi
        ├── MajburiyXato
        ├── TurXatosi
        └── QiymatXatosi
             └── FormatXatosi

     ⭐ Ierarxiya tufayli:
        except QiymatXatosi:  → FormatXatosi ham
        except ValidatsiyaXatosi:  → hammasi

     XATOLARNI YIG'ISH:
       Yiguvchi.ushla() — kontekst menejeri
       Har maydon alohida → barcha xatolar ko'rinadi

     ICHMA-ICH:
       y.ichki("manzil") → "xodim.manzil.shahar"

     EXCEPTIONGROUP:
       except* bilan toifalar bo'yicha ajratish
       Foydalanuvchi barcha muammoni BIR MARTA ko'radi

  ⚠️ ANTIPATTERN (birinchi xatoda to'xtash):
     for maydon in maydonlar:
         tekshir(maydon)        ⚠️ birinchi xatoda to'xtaydi
                                ⚠️ foydalanuvchi 5 marta yuboradi
""")

Natijaning muhim qismi:

text
1. Har xodim alohida:

  ┌─ Xodim 2
  │ ❌ 9 ta xato:
  │   FormatXatosi (2):
  │     • xodim[2].manzil.indeks: format mos emas (qiymat: 'abc')
  │     • xodim[2].manzil.indeks: format mos emas (qiymat: 'abc')
  │   MajburiyXato (1):
  │     • xodim[2].manzil.kocha: majburiy maydon
  │   QiymatXatosi (4):
  │     • xodim[2].ism: kamida 2 belgi (1 keldi)
  │     • xodim[2].yosh: <= 100 bo'lsin (qiymat: 200)
  │   TurXatosi (2):
  │     • xodim[2].id: butun songa o'girib bo'lmaydi

3. ⭐ ExceptionGroup bilan ko'tarish:

  📋 Majburiy maydonlar (5):
     • xodim[3].id: majburiy maydon
     • xodim[3].ism: majburiy maydon

  🔤 Tur xatolari (3):
     • xodim[2].id: butun songa o'girib bo'lmaydi

Nima ko'rsatdi: 2.3, 2.5, 2.6-bo'limlar.


5. To'g'ri va noto'g'ri tushunishlar

Noto'g'ri fikr To'g'risi
"Bir necha except — hammasi ishlaydi" Faqat bitta
"Tartib ahamiyatsiz" Avlod avval bo'lishi kerak
"except A, B: ishlaydi" SyntaxError; qavs kerak
"IOError va OSError — turli" Bir xil (3.3 dan)
"TimeoutError — tarmoq xatosi" OSError avlodi
"except* — except ning sinonimi" Bir nechta blok ishlaydi
"ExceptionGroup — oddiy ro'yxat" Ierarxik, split/subgroup bor
"Birinchi xatoda to'xtash kerak" Validatsiyada — hammasini yig'ing

6. Keng tarqalgan xatolar va yechimlari

1. Noto'g'ri tartib

python
except ValueError: ...
except JSONDecodeError: ...         # ❌ o'lik kod
                                    # ✅ teskari

2. Qavssiz tuple

python
except ValueError, TypeError:       # ❌ SyntaxError
except (ValueError, TypeError):     # ✅

3. Juda keng birinchi blok

python
except Exception: ...               # ⚠️ birinchi
except ValueError: ...              # ❌ o'lik

4. OSError avlodlarini unutish

python
except (ConnectionError, OSError):  # ⚠️ ConnectionError ortiqcha
except OSError:                     # ✅ yoki teskari tartib

5. Birinchi xatoda to'xtash

python
for x in malumot:
    tekshir(x)                      # ⚠️ birinchi xatoda to'xtaydi
                                    # ✅ xatolarni yig'ing

6. except* va except aralashmasi

python
except* ValueError: ...
except TypeError: ...               # ❌ SyntaxError

7. ExceptionGroup da BaseException

python
ExceptionGroup("x", [KeyboardInterrupt()])      # ❌
BaseExceptionGroup("x", [KeyboardInterrupt()])  # ✅

8. Toifalarda tartib xatosi

python
except OSError: ...                 # ⚠️ TimeoutError ham
except TimeoutError: ...            # ❌ o'lik

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 9.2-9.3-darslar (o'tilgan): try/except/finally
  • 9.6-9.7: o'z istisnolaringiz, ierarxiya
  • 14-qism: asyncio.TaskGroup — ExceptionGroup
  • concurrent.futures: parallel ishlar
  • pydantic, marshmallow: validatsiya xatolarini yig'ish
  • PEP 654: Exception Groups and except*
  • PEP 3151: OS xatolari ierarxiyasini qayta qurish

8. Eng yaxshi amaliyotlar

  1. Aniqdan umumiyga. Avlod turi avval yozilsin.

  2. Guruh turlarini biling. LookupError, OSError, ArithmeticError.

  3. Toifalarni o'zgaruvchiga chiqaring. VAQTINCHALIK = (...).

  4. Har toifaga o'z strategiyasi. Qayta urinish, HTTP kodi, jurnal darajasi.

  5. Validatsiyada barcha xatolarni yig'ing. Foydalanuvchi bir marta ko'rsin.

  6. ExceptionGroup — parallel ishlar uchun. 3.11+.

  7. Xato yo'lini (path) ko'rsating. xodim[2].manzil.shahar.

  8. Linter ishlating. ruff B014 o'lik except bloklarini topadi.


9. Amaliy topshiriq

Vazifa 1: Qaysi blok ishlaydi?

python
try:
    raise X()
except KeyError: print(1)
except LookupError: print(2)
except (ValueError, TypeError): print(3)
except OSError: print(4)
except Exception: print(5)

X = ?

1.  KeyError
2.  IndexError
3.  ValueError
4.  UnicodeDecodeError
5.  FileNotFoundError
6.  ConnectionResetError
7.  TimeoutError
8.  ZeroDivisionError
9.  json.JSONDecodeError
10. RecursionError
11. ModuleNotFoundError
12. KeyboardInterrupt
Javoblar
  1. 1 — KeyError
  2. 2 — LookupError
  3. 3
  4. 3 — ValueError avlodi
  5. 4 — OSError avlodi
  6. 4 — ConnectionError → OSError
  7. 4 — TimeoutError ham OSError
  8. 5 — ArithmeticError → Exception
  9. 3 — ValueError avlodi
  10. 5 — RuntimeError → Exception
  11. 5 — ImportError → Exception
  12. Ushlanmaydi — BaseException

Vazifa 2: Xatolarni tuzating

python
1.  except ValueError: ...
    except json.JSONDecodeError: ...
2.  except ValueError, TypeError: ...
3.  except Exception: ...
    except ValueError: ...
4.  except OSError: ...
    except TimeoutError: ...
5.  except* ValueError: ...
    except TypeError: ...
6.  raise ExceptionGroup("x", [])
7.  for x in m: tekshir(x)      # barcha xatolar kerak
8.  except (ConnectionError, OSError): ...
Javoblar
python
1.  JSONDecodeError ni AVVAL
2.  except (ValueError, TypeError):
3.  ValueError ni AVVAL
4.  TimeoutError ni AVVAL (OSError avlodi)
5.  Ikkalasini ham except* qiling
6.  Bo'sh guruh mumkin emas — shart qo'ying
7.  Xatolarni yig'ing (Yiguvchi yoki ro'yxat)
8.  OSError yetadi (ConnectionError uning avlodi)

Vazifa 3: Xato toifalovchi

Yozing:

  1. Toifa enum: vaqtinchalik, ma'lumot, ruxsat, resurs, dastur
  2. Tur → toifa xaritasi (tartib bilan)
  3. Har toifa uchun strategiya (qayta urinish, HTTP, jurnal)
  4. O'lik kod tekshiruvi
  5. Qamrab olinmagan turlarni topish
  6. Statistika va hisobot

Vazifa 4: except tartibi tekshiruvchisi

Vosita yozing (AST bilan):

  1. Har try dagi except turlarini olsin
  2. O'lik bloklarni topsin (issubclass)
  3. Tuple ichidagi ortiqcha turlarni
  4. except: va except Exception: larni
  5. Tuzatilgan tartibni taklif qilsin
  6. ruff B014 bilan solishtiring

Vazifa 5: ExceptionGroup yig'uvchi

  1. Kontekst menejeri: with yigish() as y:
  2. y.ushla() — bitta amal xatosini yig'sin
  3. Ichma-ich yig'uvchilar (yo'l bilan)
  4. Chegara va qat'iy rejim
  5. except* bilan toifalash
  6. Python 3.10 uchun zaxira variant

Vazifa 6: Parallel ishlar

  1. ThreadPoolExecutor bilan N ish
  2. Barcha xatolarni yig'sin
  3. ExceptionGroup bilan ko'tarsin
  4. except* bilan toifalarga ajratsin
  5. Qayta urinish faqat vaqtinchalik xatolarda
  6. Statistika: muvaffaqiyat/xato/qayta urinish

Vazifa 7: O'ylash

Nega Python ExceptionGroup ni faqat 3.11 da qo'shdi — bu ehtiyoj ancha oldin bo'lgan-ku?

Javob

Chunki asyncio.TaskGroup uni majburiy qildi — undan oldin muammo "chetlab o'tiladigan" edi.

1. Muammo ancha oldin ma'lum edi

python
# 2010-yillar: har kutubxona o'z yechimini yasadi

# concurrent.futures
for k in futures:
    try:
        k.result()
    except Exception as e:
        xatolar.append(e)           # ⚠️ qo'lda

# multiprocessing
hovuz.map(f, malumot)               # ⚠️ birinchi xato

# pytest
class ExceptionInfo: ...            # ⚠️ o'z tuzilmasi

# trio (2017)
class MultiError(BaseException):    # ⭐ ExceptionGroup ning bobosi
    def __init__(self, exceptions): ...

Trio kutubxonasi (Nathaniel Smith, 2017) MultiError ni yaratdi — bu ExceptionGroup ning to'g'ridan-to'g'ri ilhomlantiruvchisi.

2. Nega bunchalik kech

a) Sintaksis o'zgarishi kerak edi

python
except* ValueError as eg:           # ⭐ YANGI kalit so'z

Python juda kamdan-kam yangi sintaksis qo'shadi. Oxirgi 10 yilda:

Versiya Yangi sintaksis
3.5 async/await, @ (matmul)
3.6 f-string, := yo'q hali
3.8 := (walrus), / (pozitsion)
3.10 match/case
3.11 except*
3.12 tur parametrlari def f[T]()

Har biri yillar muhokama qilinadi.

b) Semantika murakkab

Savollar, javob talab qilgan:

python
# 1. Bitta istisno ham guruh bo'ladimi?
try:
    raise ValueError()
except* ValueError as eg:
    type(eg)                        # ExceptionGroup ⭐ ha

# 2. Ichma-ich guruhlar qanday?
ExceptionGroup("a", [ExceptionGroup("b", [ValueError()])])
# → except* ValueError uni topadimi?  ⭐ ha, avtomatik yassilanadi

# 3. Traceback qanday ko'rsatiladi?
# → daraxt shaklida ⭐

# 4. Bir necha blok ishlagach nima bo'ladi?
# → ushlanmaganlar guruhda uzatiladi ⭐

# 5. except va except* aralashsa?
# → ❌ SyntaxError

# 6. break/continue/return except* da?
# → ❌ SyntaxError (murakkab semantika)

Har savolga javob o'ylab topilishi kerak edi.

c) Orqaga moslik

python
# Mavjud kod buzilmasligi kerak:
try:
    ish()
except Exception:                   # ⚠️ ExceptionGroup ni ushlaydimi?
    ...

Yechim: ExceptionGroup — Exception avlodi. Ya'ni eski kod uni ushlaydi, lekin ichidagilarni ajrata olmaydi.

3. Nima majburladi: asyncio.TaskGroup

Python 3.11 da asyncio.TaskGroup qo'shildi (trio dan ilhom):

python
async with asyncio.TaskGroup() as tg:
    tg.create_task(ish1())
    tg.create_task(ish2())
    tg.create_task(ish3())

Savol: uchtasi ham xato bersa — nima ko'tariladi?

Variantlar:

Variant Muammo
Birinchisi Qolgan ikkitasi yo'qoladi
Ro'yxat raise ro'yxatni qabul qilmaydi
Maxsus istisno except bilan ajratib bo'lmaydi
ExceptionGroup To'g'ri yechim

Ya'ni TaskGroup ExceptionGroup siz mumkin emas edi.

4. PEP 654 tarixi

Sana Voqea
2017 Trio MultiError ni yaratadi
2019 Trio MultiError v2 muhokamasi
2021-02 PEP 654 taklif qilinadi (Irit Katriel, Yury Selivanov, Guido)
2021-09 PEP qabul qilinadi
2022-10 Python 3.11 chiqadi

Taklif → chiqish: 20 oy. Muhokama esa 4 yil.

5. Nima uchun MultiError emas

Trio dagi MultiError muammolari:

python
# ⚠️ 1. except bilan ajratib bo'lmaydi
try:
    ish()
except MultiError as me:
    for e in me.exceptions:         # ⚠️ qo'lda
        if isinstance(e, ValueError):
            ...

# ⚠️ 2. Ichma-ich guruhlar qo'lda yassilanadi
# ⚠️ 3. Traceback chalkash
# ⚠️ 4. Standart emas — har kutubxona o'ziniki

except* — til darajasidagi yechim:

python
except* ValueError as eg:           # ✅ avtomatik

6. Boshqa tillar

Til Ko'p istisno
Python 3.11+ ExceptionGroup + except*
Java Throwable.addSuppressed()
C# AggregateException
JavaScript AggregateError (ES2021)
Go errors.Join() (1.20, 2023)
Rust Qo'lda (Vec<Error>)

C# — eng yaqin:

csharp
try {
    Task.WaitAll(tasks);
} catch (AggregateException ae) {
    ae.Handle(e => {                // ⚠️ qo'lda filtrlash
        if (e is ArgumentException) return true;
        return false;
    });
}

C# da catch* yo'q — filtrlash qo'lda.

Go 1.20 (2023):

go
err := errors.Join(err1, err2, err3)
errors.Is(err, target)              // ⭐ ichidagilarni tekshiradi

Python dan 8 yil keyin, lekin except* kabi sintaksis yo'q.

7. Amalda kim ishlatadi

python
# 1. asyncio.TaskGroup (3.11+)
async with asyncio.TaskGroup() as tg: ...

# 2. Validatsiya kutubxonalari
# pydantic v2 hali ExceptionGroup ishlatmaydi (orqaga moslik)

# 3. Test frameworklari
# pytest — muhokama qilinmoqda

# 4. Ma'lumot quvurlari
raise ExceptionGroup("Yuklashda xatolar", xatolar)

# 5. Parallel so'rovlar

Sekin qabul qilinmoqda, chunki:

  • 3.11+ talab qiladi (2022)
  • Ko'p loyihalar 3.9/3.10 da
  • Kutubxonalar orqaga moslikni saqlaydi

8. Zaxira variant (3.10 va oldin)

python
try:
    from builtins import ExceptionGroup
except ImportError:
    # ⭐ exceptiongroup kutubxonasi
    from exceptiongroup import ExceptionGroup

exceptiongroup — PyPI paketi, except* ni ham taqlid qiladi (catch() kontekst menejeri bilan).

9. Nima o'rgandik

Python ExceptionGroup ni kech qo'shdi, chunki:

  1. Yangi sintaksis (except*) — Python juda ehtiyotkor
  2. Semantika murakkab — 6+ savol javob talab qilgan
  3. Orqaga moslik — mavjud kod buzilmasligi kerak
  4. "Chetlab o'tish" mumkin edi — har kutubxona o'ziniki yasagan
  5. asyncio.TaskGroup majburladi — usiz mumkin emas edi

Umumiy naqsh: Python yangi xususiyatni haqiqiy ehtiyoj paydo bo'lgandagina qo'shadi:

Xususiyat Nima majburladi
async/await Tarmoq dasturlari
dataclass attrs ning muvaffaqiyati
match/case Strukturaviy ma'lumot
except* asyncio.TaskGroup
:= Comprehension da takroriy hisoblash

Va har biri yillar muhokama qilinadi — bu Pythonning barqarorligi sababi.

Nimani mustahkamlaydi: 2.5, 2.6-bo'limlar.


Xulosa

Bu darsda bir nechta istisno bilan ishlashni o'rgandik.

Eng muhim uch fikr:

  1. except bloklari tartib bilan tekshiriladi va faqat bittasi ishlaydi. Avlod turi ota-sinfdan avval yozilishi kerak — aks holda u o'lik kod bo'ladi. Bu ayniqsa JSONDecodeError/ValueError, TimeoutError/OSError, KeyError/LookupError juftliklarida uchraydi.

  2. Ierarxiyani biling va undan foydalaning. LookupError — KeyError va IndexError; OSError — barcha fayl va tarmoq xatolari; ConnectionError — to'rtta ulanish xatosi. Amaliy strategiya — xatolarni toifalarga ajratish (vaqtinchalik / ma'lumot / ruxsat / resurs / dastur) va har toifaga o'z ish tartibini berish: qayta urinish, HTTP kodi, jurnal darajasi.

  3. ExceptionGroup va except* (3.11+) — bir vaqtda bir nechta xato uchun. except* da, oddiy except dan farqli, bir nechta blok ishlashi mumkin va har biri o'z turidagi istisnolarni oladi. Bu parallel ishlar (asyncio.TaskGroup) va validatsiya uchun juda muhim: foydalanuvchi barcha muammoni bir marta ko'radi, birinchisini tuzatib qayta yuborishga majbur bo'lmaydi.

Keyingi darsda raise ni chuqur ko'ramiz: istisno ko'tarish, qayta ko'tarish, from bilan zanjirlash va istisnolarni to'g'ri o'rash.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
9.4-dars: Bir nechta istisno — IlmHamroh