IlmHamroh
Python kursi/Xatolar7/8-dars39 daqiqa
Mundarija (21)

9.7-dars: Istisnolar ierarxiyasi

9-QISM — XATOLAR VA ISTISNOLAR · 7-dars


1. Kirish va motivatsiya

Bu ikki kutubxona bir xil ishni qiladi. Farqni ko'ring:

Kutubxona A:

python
try:
    a.sorov("/xodimlar")
except A.UlanmadiXato: ...
except A.VaqtTugadi: ...
except A.HTTPXato: ...
except A.JSONXato: ...
except A.SertifikatXato: ...            # ⚠️ va yana 12 ta

Yangi versiyada yangi istisno qo'shilsa — kodingiz buziladi.

Kutubxona B:

python
try:
    b.sorov("/xodimlar")
except b.SorovXatosi:                   # ⭐ HAMMASI
    ...

Farq — ierarxiya. B da barcha istisnolar bitta asosdan meros oladi, shuning uchun:

  • Yangi istisno qo'shilsa — mavjud kod ishlashda davom etadi
  • Kerak bo'lganda aniqroq ushlash mumkin
  • Toifalar orqali guruh ushlash mumkin

Bu darsda:

  • Pythonning o'z ierarxiyasi — to'liq daraxt
  • Ierarxiya loyihalash tamoyillari
  • Chuqurlik vs kenglik
  • Mashhur kutubxonalar tahlili
  • Migratsiya va orqaga moslik
  • Ierarxiyani sinovdan o'tkazish

2. Nazariya — chuqur tushuntirish

2.1. Pythonning o'z ierarxiyasi

BaseException                       ⭐ hamma narsaning asosi
 ├── BaseExceptionGroup             (3.11+)
 ├── GeneratorExit                  ⚠️ oqim boshqaruvi
 ├── KeyboardInterrupt              ⚠️ Ctrl+C
 ├── SystemExit                     ⚠️ sys.exit()
 └── Exception                      ⭐ ODATIY xatolar
      ├── ArithmeticError
      │    ├── FloatingPointError
      │    ├── OverflowError
      │    └── ZeroDivisionError
      ├── AssertionError
      ├── AttributeError
      ├── BufferError
      ├── EOFError
      ├── ExceptionGroup            (3.11+)
      ├── ImportError
      │    └── ModuleNotFoundError
      ├── LookupError               ⭐ guruh
      │    ├── IndexError
      │    └── KeyError
      ├── MemoryError
      ├── NameError
      │    └── UnboundLocalError
      ├── OSError                   ⭐ eng katta guruh
      │    ├── BlockingIOError
      │    ├── ChildProcessError
      │    ├── ConnectionError      ⭐ ichki guruh
      │    │    ├── BrokenPipeError
      │    │    ├── ConnectionAbortedError
      │    │    ├── ConnectionRefusedError
      │    │    └── ConnectionResetError
      │    ├── FileExistsError
      │    ├── FileNotFoundError
      │    ├── InterruptedError
      │    ├── IsADirectoryError
      │    ├── NotADirectoryError
      │    ├── PermissionError
      │    ├── ProcessLookupError
      │    └── TimeoutError
      ├── ReferenceError
      ├── RuntimeError
      │    ├── NotImplementedError
      │    ├── RecursionError
      │    └── PythonFinalizationError    (3.13+)
      ├── StopAsyncIteration
      ├── StopIteration
      ├── SyntaxError
      │    └── IndentationError
      │         └── TabError
      ├── SystemError
      ├── TypeError
      ├── ValueError
      │    └── UnicodeError
      │         ├── UnicodeDecodeError
      │         ├── UnicodeEncodeError
      │         └── UnicodeTranslateError
      └── Warning                   ⭐ warnings moduli
           ├── DeprecationWarning
           ├── UserWarning
           └── ... (11 ta)

Dizayn xususiyatlari:

Xususiyat Misol
Guruhlash LookupError = IndexError + KeyError
Sayoz Ko'pchilik 2-3 daraja
Toifalar OSError, ArithmeticError
Maxsus Warning alohida shoxda

2.2. Loyihalash tamoyillari

1. Bitta asos sinf — MAJBURIY

python
class KutubxonaXatosi(Exception):
    """Barcha xatolar shundan meros."""

Foydalanuvchi bitta blok bilan hammasini ushlaydi.

2. Toifalar — foydalanuvchi HAR XIL ish qilsa

python
class TarmoqXatosi(KutubxonaXatosi): ...    # ⭐ qayta urin
class MalumotXatosi(KutubxonaXatosi): ...   # ⭐ tuzat

Agar bir xil ish qilinsa — toifa kerak emas.

3. Sayoz — 3 darajadan oshmasin

KutubxonaXatosi              1
 └── TarmoqXatosi            2
      └── VaqtTugadi         3      ⭐ yetadi
           └── DNSVaqt       4      ⚠️ ortiqcha

4. Kenglik — chuqurlikdan yaxshi

python
# ⚠️ CHUQUR
Xato → Tarmoq → HTTP → Mijoz → NotFound

# ✅ SAYOZ
Xato → TarmoqXatosi
     → HTTPXatosi (kod atributi bilan)

5. Atributlar — sinflardan yaxshi

python
# ⚠️ 20 ta sinf
class HTTP400(...), HTTP401(...), HTTP403(...), ...

# ✅ 1 ta sinf + atribut
class HTTPXatosi(Exception):
    def __init__(self, kod: int, ...): self.kod = kod

Qoida: foydalanuvchi alohida ushlaydimi? Ha → sinf. Yo'q → atribut.

2.3. Ierarxiya antipatternlari

python
# ❌ 1. Tekis — asos yo'q
class XatoA(Exception): ...
class XatoB(Exception): ...

# ❌ 2. Juda chuqur
class A(Exception): ...
class B(A): ...
class C(B): ...
class D(C): ...
class E(D): ...                     # ⚠️ 5 daraja

# ❌ 3. Sinf portlashi
class Xato400(...), Xato401(...)    # ⚠️ har kod uchun

# ❌ 4. Mantiqsiz meros
class VaqtTugadi(ValidatsiyaXatosi): ...    # ⚠️ aloqasi yo'q

# ❌ 5. Ko'p meros chalkashligi
class X(TarmoqXatosi, MalumotXatosi): ...   # ⚠️ qaysi biri?

# ❌ 6. Bo'sh oraliq
class OraliqXato(AsosXato): ...     # ⚠️ hech kim ishlatmaydi
class Haqiqiy(OraliqXato): ...

2.4. OSError — namunali dizayn

Python 3.3 gacha (PEP 3151):

python
try:
    open("yoq.txt")
except IOError as e:
    if e.errno == errno.ENOENT:     # ⚠️ errno tekshirish
        ...
    elif e.errno == errno.EACCES:
        ...

Muammolar:

  • IOError, OSError, WindowsError, EnvironmentError, socket.error, select.error — 6 ta sinf
  • Har safar errno tekshirish
  • Platformaga bog'liq

Python 3.3 dan keyin:

python
try:
    open("yoq.txt")
except FileNotFoundError:           # ⭐ aniq
    ...
except PermissionError:             # ⭐ aniq
    ...

Barcha eski nomlar OSError ga taxallus:

python
IOError is OSError                  # True
EnvironmentError is OSError         # True
socket.error is OSError             # True

OSError.__new__ sehri:

python
type(OSError(2, "yo'q"))            # FileNotFoundError  ⭐
type(OSError(13, "ruxsat"))         # PermissionError    ⭐

errno ga qarab avtomatik to'g'ri avlod tanlanadi.

2.5. Mashhur kutubxonalar

requests:

RequestException(IOError)           ⭐ IOError dan!
 ├── ConnectionError
 │    ├── ConnectTimeout(+Timeout)  ⭐ ko'p meros
 │    ├── ProxyError
 │    └── SSLError
 ├── HTTPError
 ├── Timeout
 │    └── ReadTimeout
 ├── TooManyRedirects
 ├── URLRequired
 └── ... (JSONDecodeError, MissingSchema, ...)

ConnectTimeout(ConnectionError, Timeout) — ikkalasi bilan ham ushlanadi.

sqlalchemy:

SQLAlchemyError
 ├── ArgumentError
 ├── DBAPIError                     ⭐ drayver xatosini o'raydi
 │    ├── InterfaceError
 │    ├── DatabaseError
 │    │    ├── DataError
 │    │    ├── OperationalError
 │    │    ├── IntegrityError       ⭐ UNIQUE, FOREIGN KEY
 │    │    ├── InternalError
 │    │    ├── ProgrammingError
 │    │    └── NotSupportedError
 │    └── StatementError
 └── NoResultFound / MultipleResultsFound

DB-API 2.0 (PEP 249) standarti — barcha drayverlar bir xil.

httpx:

HTTPError
 ├── RequestError
 │    ├── TransportError
 │    │    ├── TimeoutException
 │    │    │    ├── ConnectTimeout
 │    │    │    ├── ReadTimeout
 │    │    │    ├── WriteTimeout
 │    │    │    └── PoolTimeout
 │    │    ├── NetworkError
 │    │    ├── ProtocolError
 │    │    └── ProxyError
 │    └── InvalidURL / CookieConflict
 └── HTTPStatusError

4 daraja — chuqurroq, lekin mantiqiy.

2.6. Migratsiya strategiyalari

Muammo: eski kod except ValueError: yozgan, siz MeningXatom ga o'tmoqchisiz.

Yechim 1 — ko'p meros:

python
class MeningXatom(KutubxonaXatosi, ValueError):
    """⭐ Ikkalasi ham ishlaydi."""

Yechim 2 — bosqichma-bosqich:

python
# v2.0: yangi qo'shildi, eski ishlaydi
class YangiXato(EskiXato): ...      # ⭐ eskidan meros

# v3.0: eskisi deprecated
class EskiXato(YangiXato):          # ⭐ teskari
    def __init__(self, *a, **kw):
        warnings.warn("EskiXato eskirgan", DeprecationWarning)
        super().__init__(*a, **kw)

# v4.0: eskisi olib tashlanadi

Yechim 3 — taxallus:

python
EskiNom = YangiNom                  # ⭐ oddiy

Ierarxiyani O'ZGARTIRISH — buzuvchi o'zgarish:

python
# v1.0
class VaqtTugadi(KutubxonaXatosi): ...

# v2.0
class VaqtTugadi(TarmoqXatosi): ...     # ⭐ QO'SHISH — xavfsiz
                                        #   (TarmoqXatosi ⊂ KutubxonaXatosi)

# ❌ XAVFLI:
class VaqtTugadi(Exception): ...        # ⚠️ KutubxonaXatosi'dan chiqdi

Qoida: ierarxiyani faqat kengaytiring (avlod qo'shing), torayting emas.

2.7. Ierarxiyani sinash

python
def ierarxiya_tekshir(Asos: type) -> list[str]:
    """⭐ Loyihalash muammolarini topadi."""
    muammolar = []

    def yur(S, chuqurlik=0, yol=()):
        avlodlar = S.__subclasses__()

        if chuqurlik > 3:
            muammolar.append(f"Juda chuqur: {'.'.join(yol)}")
        if len(avlodlar) > 12:
            muammolar.append(f"Juda ko'p avlod: {S.__name__}")
        if not S.__doc__:
            muammolar.append(f"Docstring yo'q: {S.__name__}")
        if len(avlodlar) == 1:
            muammolar.append(f"Bitta avlod: {S.__name__}")

        for A in avlodlar:
            yur(A, chuqurlik + 1, yol + (A.__name__,))

    yur(Asos)
    return muammolar

3. Tez ma'lumotnoma

Python ierarxiyasi (asosiy)

BaseException
 ├── SystemExit / KeyboardInterrupt / GeneratorExit
 └── Exception
      ├── LookupError → IndexError, KeyError
      ├── OSError → FileNotFoundError, PermissionError,
      │             ConnectionError → ConnectionResetError, ...
      ├── ArithmeticError → ZeroDivisionError, OverflowError
      ├── ValueError → UnicodeError → UnicodeDecodeError
      ├── RuntimeError → NotImplementedError, RecursionError
      ├── NameError → UnboundLocalError
      ├── ImportError → ModuleNotFoundError
      ├── SyntaxError → IndentationError → TabError
      └── Warning → DeprecationWarning, ...

Loyihalash qoidalari

1. Bitta asos sinf                  MAJBURIY
2. Toifalar — har xil ish qilinsa   2-6 ta
3. Chuqurlik ≤ 3 daraja
4. Kenglik > chuqurlik
5. Atribut > yangi sinf             ⭐ eng muhim
6. Har sinfda docstring

Antipatternlar

Tekis (asos yo'q)                   ❌
5+ daraja chuqurlik                 ❌
Har HTTP kod uchun sinf             ❌
Mantiqsiz meros                     ❌
Bo'sh oraliq sinf                   ❌

Migratsiya

Kengaytirish (avlod qo'shish)       ✅ xavfsiz
Ko'p meros (eski + yangi)           ✅
Taxallus (EskiNom = YangiNom)       ✅
DeprecationWarning bilan            ✅
Asosni o'zgartirish (torayish)      ❌ buzadi

4. Batafsil misollar

Misol 1 — Pythonning o'z ierarxiyasi

python
"""O'rnatilgan istisnolar daraxtini tahlil qilish."""

import builtins
import sys

print("=== 1. To'liq daraxt ===\n")


def daraxt(Sinf: type, chuqurlik: int = 0, oxirgi: bool = True,
           prefiks: str = "", maks: int = 3) -> list[str]:
    """⭐ Istisno daraxtini chizadi."""
    if chuqurlik == 0:
        qatorlar = [Sinf.__name__]
    else:
        qatorlar = [f"{prefiks}{'└── ' if oxirgi else '├── '}"
                    f"{Sinf.__name__}"]

    if chuqurlik >= maks:
        return qatorlar

    avlodlar = sorted(
        [c for c in Sinf.__subclasses__()
         if c.__module__ == "builtins"],
        key=lambda c: c.__name__,
    )
    yangi = prefiks + ("" if chuqurlik == 0
                       else ("    " if oxirgi else "│   "))
    for i, A in enumerate(avlodlar):
        qatorlar.extend(
            daraxt(A, chuqurlik + 1, i == len(avlodlar) - 1,
                   yangi, maks)
        )
    return qatorlar


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


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


def barcha(Sinf: type) -> set[type]:
    natija = {Sinf}
    for A in Sinf.__subclasses__():
        if A.__module__ == "builtins":
            natija |= barcha(A)
    return natija


HAMMASI = barcha(BaseException)
print(f"  Jami o'rnatilgan istisno: {len(HAMMASI)}")


def chuqurlik(Sinf: type) -> int:
    d = 0
    for K in Sinf.__mro__:
        if K is BaseException:
            break
        d += 1
    return d


darajalar: dict[int, list[str]] = {}
for S in HAMMASI:
    darajalar.setdefault(chuqurlik(S), []).append(S.__name__)

print(f"\n  {'Daraja':<9} {'Soni':<7} {'Misollar'}")
print("  " + "─" * 66)
for d in sorted(darajalar):
    nomlar = sorted(darajalar[d])
    print(f"  {d:<9} {len(nomlar):<7} "
          f"{', '.join(nomlar[:5])}"
          f"{' ...' if len(nomlar) > 5 else ''}")

print(f"""
  ⭐ XULOSA: ko'pchilik istisno 1-3 daraja chuqurlikda.
     Eng chuqur: {max(darajalar)} daraja.
""")


print("=== 3. ⭐ Eng ko'p avlodli sinflar ===\n")


def avlodlar_soni(Sinf: type) -> int:
    return len(barcha(Sinf)) - 1


ro = sorted(
    ((S, avlodlar_soni(S)) for S in HAMMASI),
    key=lambda p: -p[1],
)

print(f"  {'Sinf':<22} {'Avlodlar':<11} {'Bevosita'}")
print("  " + "─" * 50)
for S, n in ro[:10]:
    bevosita = len([c for c in S.__subclasses__()
                    if c.__module__ == "builtins"])
    print(f"  {S.__name__:<22} {n:<11} {bevosita}")


print("\n\n=== 4. ⭐ OSError oilasi ===\n")

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

print(f"\n  ⭐ Taxalluslar (PEP 3151 dan keyin):\n")

import socket
import select

TAXALLUSLAR = [
    ("IOError", IOError),
    ("EnvironmentError", EnvironmentError),
    ("socket.error", socket.error),
    ("select.error", select.error),
]
for nom, Sinf in TAXALLUSLAR:
    print(f"    {nom:<20} is OSError → {Sinf is OSError}")

print(f"\n  ⭐ OSError.__new__ sehri (errno → avlod):\n")

import errno

KODLAR = [
    (errno.ENOENT, "ENOENT"),
    (errno.EACCES, "EACCES"),
    (errno.EEXIST, "EEXIST"),
    (errno.EISDIR, "EISDIR"),
    (errno.ENOTDIR, "ENOTDIR"),
    (errno.ECONNREFUSED, "ECONNREFUSED"),
    (errno.ECONNRESET, "ECONNRESET"),
    (errno.EPIPE, "EPIPE"),
    (errno.ETIMEDOUT, "ETIMEDOUT"),
    (errno.EINTR, "EINTR"),
    (errno.ECHILD, "ECHILD"),
    (errno.ESRCH, "ESRCH"),
    (999, "(noma'lum)"),
]

print(f"  {'errno':<8} {'Nomi':<16} {'Natija turi'}")
print("  " + "─" * 52)
for kod, nom in KODLAR:
    e = OSError(kod, "xabar")
    print(f"  {kod:<8} {nom:<16} {type(e).__name__}")

print(f"""
  ⭐ MEXANIZM: OSError.__new__ errno ga qarab TO'G'RI
     avlodni qaytaradi. Bu C darajasida amalga oshirilgan.

  ⭐ NATIJA: kutubxonalar OSError(errno, msg) ko'tarsa —
     foydalanuvchi FileNotFoundError ni ushlaydi.
""")


print("=== 5. ⭐ Guruh ushlash imkoniyatlari ===\n")

GURUHLAR = [
    (LookupError, "Qidiruv xatolari"),
    (ArithmeticError, "Arifmetik xatolar"),
    (OSError, "Tizim xatolari"),
    (ConnectionError, "Ulanish xatolari"),
    (ValueError, "Qiymat xatolari"),
    (RuntimeError, "Ish vaqti xatolari"),
    (NameError, "Nom xatolari"),
    (ImportError, "Import xatolari"),
    (SyntaxError, "Sintaksis xatolari"),
    (UnicodeError, "Unicode xatolari"),
    (Warning, "Ogohlantirishlar"),
]

for Guruh, tavsif in GURUHLAR:
    a = sorted(S.__name__ for S in barcha(Guruh) if S is not Guruh)
    print(f"  except {Guruh.__name__:<18} — {tavsif}")
    if a:
        print(f"    → {', '.join(a[:6])}"
              f"{f' (+{len(a) - 6})' if len(a) > 6 else ''}")
    print()


print("=== 6. ⚠️ Kutilmagan meroslar ===\n")

TEKSHIRUVLAR = [
    ("ModuleNotFoundError", ModuleNotFoundError, ImportError),
    ("UnboundLocalError", UnboundLocalError, NameError),
    ("RecursionError", RecursionError, RuntimeError),
    ("NotImplementedError", NotImplementedError, RuntimeError),
    ("UnicodeDecodeError", UnicodeDecodeError, ValueError),
    ("IndentationError", IndentationError, SyntaxError),
    ("TabError", TabError, IndentationError),
    ("TimeoutError", TimeoutError, OSError),
    ("StopIteration", StopIteration, Exception),
    ("KeyboardInterrupt", KeyboardInterrupt, Exception),
    ("SystemExit", SystemExit, Exception),
    ("MemoryError", MemoryError, Exception),
    ("Warning", Warning, Exception),
]

print(f"  {'Istisno':<22} {'Kutilgan asos':<20} {'Haqiqat'}")
print("  " + "─" * 56)
for nom, Sinf, Kutilgan in TEKSHIRUVLAR:
    natija = issubclass(Sinf, Kutilgan)
    belgi = "✅" if natija else "❌"
    print(f"  {nom:<22} {Kutilgan.__name__:<20} {belgi}")

print(f"""
  ⚠️ ❌ BELGILANGANLAR:

     KeyboardInterrupt, SystemExit — Exception EMAS!
     → BaseException avlodlari (9.6-dars, Vazifa 7)

  ⭐ QIZIQ FAKTLAR:

     • NotImplementedError — RuntimeError avlodi
       (⚠️ "TypeError kabi" tuyuladi, lekin yo'q)
     • TimeoutError — OSError avlodi (3.3 dan)
       (⚠️ asyncio.TimeoutError esa 3.11 dan shu bilan bir xil)
     • RecursionError — 3.5 dan alohida (oldin RuntimeError)
     • UnicodeDecodeError — ValueError avlodi
     • MemoryError — Exception (Java da esa Error!)
""")


print("=== 7. ⭐ Ierarxiya bo'yicha qidiruv ===\n")


def qayerda(Sinf: type) -> str:
    """⭐ MRO yo'lini ko'rsatadi."""
    yol = [K.__name__ for K in reversed(Sinf.__mro__[:-1])]
    return " → ".join(yol)


SINOVLAR = [FileNotFoundError, ConnectionResetError, TabError,
            UnicodeDecodeError, ModuleNotFoundError,
            RecursionError, KeyError, TimeoutError]

for S in SINOVLAR:
    print(f"  {S.__name__}:")
    print(f"    {qayerda(S)}")
    print()

print(f"  ⭐ Bir istisnoni ushlash usullari (FileNotFoundError):\n")
for K in FileNotFoundError.__mro__[:-1]:
    print(f"    except {K.__name__}:")

Natijaning muhim qismi:

text
=== 2. ⭐ Statistika ===

  Jami o'rnatilgan istisno: 69

  Daraja    Soni    Misollar
  ──────────────────────────────────────────────────────────────────
  0         1       BaseException
  1         5       BaseExceptionGroup, Exception, GeneratorExit, KeyboardInterrupt, SystemExit
  2         19      ArithmeticError, AssertionError, AttributeError, BufferError, EOFError ...
  3         36      BlockingIOError, BytesWarning, ChildProcessError, ConnectionError, DeprecationWarning ...
  4         8       BrokenPipeError, ConnectionAbortedError, ConnectionRefusedError, ConnectionResetError, TabError ...

  ⭐ XULOSA: ko'pchilik istisno 1-3 daraja chuqurlikda.
     Eng chuqur: 4 daraja.

=== 4. ⭐ OSError oilasi ===

  errno    Nomi             Natija turi
  ────────────────────────────────────────────────────
  2        ENOENT           FileNotFoundError
  13       EACCES           PermissionError
  111      ECONNREFUSED     ConnectionRefusedError
  999      (noma'lum)       OSError

=== 6. ⚠️ Kutilmagan meroslar ===

  KeyboardInterrupt      Exception            ❌
  SystemExit             Exception            ❌
  NotImplementedError    RuntimeError         ✅

Natija Python 3.14 da olingan. Istisnolar soni versiyaga qarab farq qiladi — masalan, PythonFinalizationError 3.13 da qo'shilgan. errno raqamlari esa platformaga bog'liq: yuqoridagi 111 (ECONNREFUSED) Linux qiymati, Windows'da xuddi shu konstanta 10061 ga teng. Shuning uchun kodda hech qachon raqam yozmang — errno.ECONNREFUSED ishlating.

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

Misol 2 — Ierarxiya loyihalash

python
"""Bir xil domen — uch xil ierarxiya. Qaysi biri yaxshi?"""

from __future__ import annotations

print("=== FAYL SAQLASH XIZMATI — UCH LOYIHA ===\n")


# ═══════════════════════════════════════════════
# ❌ LOYIHA A — TEKIS
# ═══════════════════════════════════════════════

class A_FaylTopilmadi(Exception): ...
class A_RuxsatYoq(Exception): ...
class A_JoyYoq(Exception): ...
class A_NomNotogri(Exception): ...
class A_HajmKatta(Exception): ...
class A_TurNotogri(Exception): ...
class A_UlanmadiXato(Exception): ...
class A_VaqtTugadi(Exception): ...
class A_KvotaOshdi(Exception): ...


# ═══════════════════════════════════════════════
# ❌ LOYIHA B — JUDA CHUQUR
# ═══════════════════════════════════════════════

class B_Xato(Exception): ...
class B_Kirish(B_Xato): ...
class B_KirishFayl(B_Kirish): ...
class B_KirishFaylOqish(B_KirishFayl): ...
class B_FaylTopilmadi(B_KirishFaylOqish): ...           # 5 daraja
class B_KirishFaylYozish(B_KirishFayl): ...
class B_JoyYoq(B_KirishFaylYozish): ...                 # 5 daraja
class B_KirishRuxsat(B_Kirish): ...
class B_RuxsatYoq(B_KirishRuxsat): ...                  # 4 daraja
class B_Tekshirish(B_Xato): ...
class B_TekshirishNom(B_Tekshirish): ...
class B_NomNotogri(B_TekshirishNom): ...                # 4 daraja


# ═══════════════════════════════════════════════
# ✅ LOYIHA C — MUVOZANATLI
# ═══════════════════════════════════════════════

class SaqlashXatosi(Exception):
    """Barcha saqlash xatolarining asosi."""
    QAYTA_URINISH = False
    HTTP_KODI = 500


class FaylXatosi(SaqlashXatosi):
    """Fayl bilan bog'liq (topilmadi, ruxsat, tur)."""
    HTTP_KODI = 400


class ResursXatosi(SaqlashXatosi):
    """Resurs chegaralari (joy, kvota, hajm)."""
    HTTP_KODI = 507


class TarmoqXatosi(SaqlashXatosi):
    """Tarmoq va tashqi xizmat."""
    QAYTA_URINISH = True
    HTTP_KODI = 503


class FaylTopilmadi(FaylXatosi):
    """So'ralgan fayl mavjud emas."""
    HTTP_KODI = 404

    def __init__(self, yol: str):
        self.yol = yol
        super().__init__(f"Fayl topilmadi: {yol}")


class RuxsatYoq(FaylXatosi):
    """Faylga kirish taqiqlangan."""
    HTTP_KODI = 403

    def __init__(self, yol: str, amal: str = "o'qish"):
        self.yol, self.amal = yol, amal
        super().__init__(f"Ruxsat yo'q: {amal} — {yol}")


class FaylNotogri(FaylXatosi):
    """Fayl nomi yoki turi yaroqsiz.

    ⭐ ATRIBUT bilan — uch sinf o'rniga bittasi.
    """

    def __init__(self, yol: str, sabab: str, tafsilot: str = ""):
        self.yol, self.sabab, self.tafsilot = yol, sabab, tafsilot
        super().__init__(
            f"Fayl yaroqsiz ({sabab}): {yol}"
            + (f" — {tafsilot}" if tafsilot else "")
        )


class ChegaraOshdi(ResursXatosi):
    """Biror chegara oshdi.

    ⭐ ATRIBUT bilan: joy / kvota / hajm / fayllar soni.
    """

    def __init__(self, tur: str, chegara: float, joriy: float,
                 birlik: str = ""):
        self.tur, self.chegara, self.joriy = tur, chegara, joriy
        self.birlik = birlik
        self.oshgan = joriy - chegara
        super().__init__(
            f"{tur} chegarasi oshdi: {joriy:,.0f}/"
            f"{chegara:,.0f} {birlik}".strip()
        )


class UlanmadiXato(TarmoqXatosi):
    """Saqlash xizmatiga ulanib bo'lmadi."""

    def __init__(self, xizmat: str, urinish: int = 1):
        self.xizmat, self.urinish = xizmat, urinish
        super().__init__(f"Ulanib bo'lmadi: {xizmat}")


class VaqtTugadi(TarmoqXatosi):
    """Amal belgilangan vaqtda bajarilmadi."""
    HTTP_KODI = 504

    def __init__(self, amal: str, sekund: float):
        self.amal, self.sekund = amal, sekund
        super().__init__(f"Vaqt tugadi: {amal} ({sekund}s)")


# ═══════════════════════════════════════════════
# TAHLIL
# ═══════════════════════════════════════════════

def yigish(Asos: type) -> list[type]:
    natija = [Asos]
    for A in Asos.__subclasses__():
        natija.extend(yigish(A))
    return natija


def maks_chuqurlik(Asos: type) -> int:
    a = Asos.__subclasses__()
    return 1 + max((maks_chuqurlik(A) for A in a), default=0)


def ierarxiya_chiz(Asos: type, chuqurlik: int = 0) -> list[str]:
    q = [f"{'  ' * chuqurlik}{'└─ ' if chuqurlik else ''}"
         f"{Asos.__name__}"]
    for A in sorted(Asos.__subclasses__(), key=lambda c: c.__name__):
        q.extend(ierarxiya_chiz(A, chuqurlik + 1))
    return q


print("1. Loyiha C ierarxiyasi:\n")
for q in ierarxiya_chiz(SaqlashXatosi):
    print(f"  {q}")


print("\n\n2. ⭐ Uch loyiha taqqoslash:\n")

A_SINFLAR = [A_FaylTopilmadi, A_RuxsatYoq, A_JoyYoq, A_NomNotogri,
             A_HajmKatta, A_TurNotogri, A_UlanmadiXato,
             A_VaqtTugadi, A_KvotaOshdi]

TAQQOS = [
    ("A — TEKIS", len(A_SINFLAR), 1, 0,
     "❌ Guruh ushlash MUMKIN EMAS"),
    ("B — CHUQUR", len(yigish(B_Xato)), maks_chuqurlik(B_Xato), 4,
     "❌ Chalkash, bo'sh oraliq sinflar"),
    ("C — MUVOZANATLI", len(yigish(SaqlashXatosi)),
     maks_chuqurlik(SaqlashXatosi), 3,
     "✅ Toifalar aniq, atributlar bilan"),
]

print(f"  {'Loyiha':<18} {'Sinflar':<10} {'Chuqurlik':<11} "
      f"{'Toifalar':<11} {'Baho'}")
print("  " + "─" * 78)
for nom, s, c, t, baho in TAQQOS:
    print(f"  {nom:<18} {s:<10} {c:<11} {t:<11} {baho}")


print("\n\n3. ⚠️ LOYIHA A muammosi:\n")

print("""  Foydalanuvchi kodi:

    try:
        saqla(fayl)
    except A_FaylTopilmadi: ...
    except A_RuxsatYoq: ...
    except A_JoyYoq: ...
    except A_NomNotogri: ...
    except A_HajmKatta: ...
    except A_TurNotogri: ...
    except A_UlanmadiXato: ...
    except A_VaqtTugadi: ...
    except A_KvotaOshdi: ...            ⚠️ 9 ta blok!

  ⚠️ MUAMMOLAR:
     • Yangi istisno qo'shilsa — kod BUZILADI
     • "Hammasini ushlash" mumkin emas
     • except Exception: — juda keng
""")


print("4. ⚠️ LOYIHA B muammosi:\n")

print(f"  B_FaylTopilmadi MRO:")
for K in B_FaylTopilmadi.__mro__[:-1]:
    print(f"    {K.__name__}")

print("""
  ⚠️ MUAMMOLAR:
     • B_KirishFaylOqish — hech kim ushlamaydi (bo'sh oraliq)
     • "Kirish" toifasi juda keng (fayl + ruxsat)
     • Yangi istisnoni QAYERGA qo'yish noaniq
     • Nomlar uzun va chalkash
""")


print("5. ✅ LOYIHA C — foydalanuvchi tanlaydi:\n")

XATOLAR = [
    FaylTopilmadi("/hujjatlar/hisobot.pdf"),
    RuxsatYoq("/tizim/config.json", "yozish"),
    FaylNotogri("rasm.exe", "tur", "faqat jpg, png, pdf"),
    FaylNotogri("a" * 300 + ".txt", "nom", "255 belgidan uzun"),
    ChegaraOshdi("Disk", 10_000, 10_240, "MB"),
    ChegaraOshdi("Kvota", 1_000, 1_050, "fayl"),
    ChegaraOshdi("Hajm", 50, 120, "MB"),
    UlanmadiXato("s3.wisar.uz", urinish=2),
    VaqtTugadi("yuklash", 30.0),
]

print(f"  Uch daraja aniqlik:\n")
for Tur, tavsif in [
    (SaqlashXatosi, "HAMMASI"),
    (FaylXatosi, "Fayl toifasi"),
    (ResursXatosi, "Resurs toifasi"),
    (TarmoqXatosi, "Tarmoq toifasi"),
    (FaylTopilmadi, "Aniq xato"),
]:
    mos = [e for e in XATOLAR if isinstance(e, Tur)]
    print(f"    except {Tur.__name__:<16} ({tavsif:<14}) "
          f"→ {len(mos)} xato")


print("\n\n6. ⭐ Atribut vs sinf qarori:\n")

print("""  LOYIHA A da uch alohida sinf edi:
    A_NomNotogri, A_TurNotogri, A_HajmKatta

  LOYIHA C da:
    FaylNotogri(sabab="nom" | "tur")     ⭐ 1 sinf
    ChegaraOshdi(tur="Hajm")             ⭐ 1 sinf

  ⭐ QAROR MEZONI:

     "Foydalanuvchi bularni ALOHIDA ushlaydimi?"

     • Nom noto'g'ri → foydalanuvchi tuzatadi
     • Tur noto'g'ri → foydalanuvchi tuzatadi
       ⭐ BIR XIL ish → BITTA sinf, sabab atributi

     • Fayl topilmadi → 404
     • Ruxsat yo'q → 403
       ⭐ HAR XIL ish → ALOHIDA sinf
""")

print(f"  Amalda:\n")
for e in XATOLAR:
    ish = ""
    if isinstance(e, FaylNotogri):
        ish = f"Foydalanuvchiga: '{e.sabab}' ni tuzating"
    elif isinstance(e, ChegaraOshdi):
        ish = f"{e.tur}: {e.oshgan:,.0f} {e.birlik} ortiqcha"
    elif isinstance(e, FaylTopilmadi):
        ish = "404 qaytarish"
    elif isinstance(e, RuxsatYoq):
        ish = "403 qaytarish"
    elif isinstance(e, TarmoqXatosi):
        ish = "Qayta urinish"
    print(f"    HTTP {e.HTTP_KODI}  {type(e).__name__:<16} {ish}")


print("\n\n7. ⭐ Ierarxiya tekshiruvchisi:\n")


def ierarxiya_tekshir(Asos: type) -> list[tuple[str, str]]:
    """⭐ Loyihalash muammolarini topadi."""
    muammolar: list[tuple[str, str]] = []

    def yur(S: type, chuqurlik: int = 0):
        avlodlar = S.__subclasses__()

        if chuqurlik > 3:
            muammolar.append(
                ("CHUQUR", f"{S.__name__} — {chuqurlik} daraja")
            )
        if len(avlodlar) > 12:
            muammolar.append(
                ("KENG", f"{S.__name__} — {len(avlodlar)} avlod")
            )
        if len(avlodlar) == 1:
            muammolar.append(
                ("BITTA", f"{S.__name__} — bitta avlod "
                          f"({avlodlar[0].__name__}), birlashtiring?")
            )
        if not (S.__doc__ or "").strip():
            muammolar.append(("HUJJAT", f"{S.__name__} — docstring yo'q"))

        for A in avlodlar:
            yur(A, chuqurlik + 1)

    yur(Asos)
    return muammolar


for nom, Asos in [("LOYIHA B", B_Xato), ("LOYIHA C", SaqlashXatosi)]:
    m = ierarxiya_tekshir(Asos)
    print(f"  {nom}: {len(m)} muammo")
    for tur, matn in m[:8]:
        print(f"    [{tur:<7}] {matn}")
    if len(m) > 8:
        print(f"    ... va yana {len(m) - 8} ta")
    print()

Natijaning muhim qismi:

text
1. Loyiha C ierarxiyasi:

  SaqlashXatosi
    └─ FaylXatosi
      └─ FaylNotogri
      └─ FaylTopilmadi
      └─ RuxsatYoq
    └─ ResursXatosi
      └─ ChegaraOshdi
    └─ TarmoqXatosi
      └─ UlanmadiXato
      └─ VaqtTugadi

2. ⭐ Uch loyiha taqqoslash:

  Loyiha             Sinflar    Chuqurlik   Toifalar    Baho
  ──────────────────────────────────────────────────────────────────────────
  A — TEKIS          9          1           0           ❌ Guruh ushlash MUMKIN EMAS
  B — CHUQUR         12         5           4           ❌ Chalkash, bo'sh oraliq sinflar
  C — MUVOZANATLI    10         3           3           ✅ Toifalar aniq, atributlar bilan

5. ✅ LOYIHA C — foydalanuvchi tanlaydi:

    except SaqlashXatosi   (HAMMASI       ) → 9 xato
    except FaylXatosi      (Fayl toifasi  ) → 4 xato
    except ResursXatosi    (Resurs toifasi) → 3 xato
    except FaylTopilmadi   (Aniq xato     ) → 1 xato

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

Misol 3 — Migratsiya va orqaga moslik

python
"""Ierarxiyani versiyadan versiyaga o'zgartirish."""

from __future__ import annotations

import warnings

warnings.simplefilter("always", DeprecationWarning)

print("=== 1. ⭐ v1.0 — boshlang'ich ===\n")


class V1_KutubxonaXatosi(Exception):
    """Asos."""


class V1_UlanmadiXato(V1_KutubxonaXatosi):
    """Ulanib bo'lmadi."""


class V1_VaqtTugadi(V1_KutubxonaXatosi):
    """Vaqt tugadi."""


class V1_NotogriMalumot(V1_KutubxonaXatosi):
    """Ma'lumot yaroqsiz."""


print("""  V1_KutubxonaXatosi
    ├── V1_UlanmadiXato
    ├── V1_VaqtTugadi
    └── V1_NotogriMalumot

  ⚠️ MUAMMO: tarmoq xatolarini birga ushlash mumkin emas.
""")


print("=== 2. ✅ v2.0 — TOIFA QO'SHISH (xavfsiz) ===\n")


class V2_KutubxonaXatosi(Exception):
    """Asos — o'zgarmadi."""


class V2_TarmoqXatosi(V2_KutubxonaXatosi):
    """⭐ YANGI oraliq toifa."""


class V2_UlanmadiXato(V2_TarmoqXatosi):
    """⭐ Asosi O'ZGARDI, lekin KENGAYDI."""


class V2_VaqtTugadi(V2_TarmoqXatosi):
    """⭐ Asosi O'ZGARDI, lekin KENGAYDI."""


class V2_NotogriMalumot(V2_KutubxonaXatosi):
    """O'zgarmadi."""


e = V2_UlanmadiXato("xato")

print(f"  Eski kod:  except V2_KutubxonaXatosi:")
print(f"    Ishlaydimi? {isinstance(e, V2_KutubxonaXatosi)}  ✅")
print(f"\n  Yangi kod: except V2_TarmoqXatosi:")
print(f"    Ishlaydimi? {isinstance(e, V2_TarmoqXatosi)}  ✅")

print(f"""
  ⭐ NEGA XAVFSIZ:

     V1: UlanmadiXato → KutubxonaXatosi
     V2: UlanmadiXato → TarmoqXatosi → KutubxonaXatosi
                        ⭐ QO'SHILDI

     MRO KENGAYDI — eski asos hamon MRO da.
     Eski except bloklar ishlashda davom etadi.

  ⭐ QOIDA: oraliq sinf QO'SHISH — har doim xavfsiz.
""")


print("=== 3. ❌ v3.0 — ASOSNI ALMASHTIRISH (buzadi) ===\n")


class V3_TarmoqXatosi(Exception):
    """⚠️ KutubxonaXatosi'dan CHIQDI."""


class V3_UlanmadiXato(V3_TarmoqXatosi):
    """⚠️ Endi V2_KutubxonaXatosi emas."""


e3 = V3_UlanmadiXato("xato")

print(f"  Eski kod:  except V2_KutubxonaXatosi:")
print(f"    Ishlaydimi? {isinstance(e3, V2_KutubxonaXatosi)}  ❌ BUZILDI")

print(f"""
  ❌ BU BUZUVCHI O'ZGARISH:

     Foydalanuvchining ISHLAYOTGAN kodi:
       try:
           kutubxona.sorov()
       except KutubxonaXatosi:      ⚠️ ENDI USHLAMAYDI
           qayta_urin()

     → Istisno DASTURNI TO'XTATADI

  ⭐ QOIDA: MRO dan sinf OLIB TASHLAMANG.
""")


print("=== 4. ⭐ Ko'p meros bilan migratsiya ===\n")


class V4_KutubxonaXatosi(Exception):
    """Yangi asos."""


class V4_NotogriMalumot(V4_KutubxonaXatosi, ValueError):
    """⭐ Eski kod ValueError ni ushlagan edi.

    Ko'p meros — IKKALASI ham ishlaydi.
    """


e4 = V4_NotogriMalumot("yaroqsiz")

print(f"  MRO: {[K.__name__ for K in type(e4).__mro__[:4]]}\n")
print(f"  {'Ushlash usuli':<32} {'Ishlaydimi'}")
print("  " + "─" * 48)
for Tur in (V4_NotogriMalumot, V4_KutubxonaXatosi, ValueError,
            Exception):
    print(f"  except {Tur.__name__:<24} {isinstance(e4, Tur)}")

print(f"""
  ⭐ STRATEGIYA:

     v1.0: raise ValueError("yaroqsiz")
     v2.0: raise NotogriMalumot(...)         ⭐ ValueError dan meros
     v3.0: (2 yil keyin) ValueError olib tashlanadi

  ⚠️ EHTIYOT: ko'p meros MRO ni murakkablashtiradi.
     __str__ va __init__ ni tekshiring.
""")


print("=== 5. ⭐ DeprecationWarning bilan ===\n")


class YangiXato(Exception):
    """Yangi nom."""


class EskiXato(YangiXato):
    """⚠️ Eskirgan — YangiXato ishlating."""

    def __init__(self, *args, **kwargs):
        warnings.warn(
            "EskiXato eskirgan, YangiXato ishlating "
            "(v4.0 da olib tashlanadi)",
            DeprecationWarning,
            stacklevel=2,
        )
        super().__init__(*args, **kwargs)


print(f"  Eski sinfdan foydalanish:")
with warnings.catch_warnings(record=True) as ogohlar:
    warnings.simplefilter("always")
    e5 = EskiXato("xabar")
    for o in ogohlar:
        print(f"    ⚠️ {o.category.__name__}: {o.message}")

print(f"\n  Ushlash hamon ishlaydi:")
try:
    raise EskiXato("test")
except YangiXato as e:
    print(f"    except YangiXato → ushlandi ✅")

print(f"""
  ⚠️ MUHIM: ogohlantirish YARATILGANDA chiqadi, USHLANGANDA emas.

     except EskiXato:               ⚠️ ogohlantirish YO'Q
     raise EskiXato(...)            ⭐ ogohlantirish BOR

  ⭐ Ushlashni ham ogohlantirish uchun — metaklass kerak
     (__instancecheck__ ni ushlash), lekin bu ODATDA ORTIQCHA.
""")


print("=== 6. ⭐ Taxallus va __init__ eksporti ===\n")

# ⭐ Eng oddiy migratsiya
SorovXatosi = YangiXato                 # taxallus

print(f"  SorovXatosi is YangiXato → {SorovXatosi is YangiXato}")
print(f"  ⭐ Bir XIL obyekt — hech qanday farq yo'q\n")

print("""  Modul tuzilishi:

    # xatolar.py
    class KutubxonaXatosi(Exception): ...
    class TarmoqXatosi(KutubxonaXatosi): ...
    class VaqtTugadi(TarmoqXatosi): ...

    # ⚠️ Eskirgan taxalluslar
    SorovXatosi = KutubxonaXatosi
    UlanishXatosi = TarmoqXatosi

    __all__ = [
        "KutubxonaXatosi", "TarmoqXatosi", "VaqtTugadi",
        # eskirganlar __all__ da EMAS
    ]

    # __init__.py
    from .xatolar import *
    from .xatolar import SorovXatosi, UlanishXatosi   ⭐ aniq
""")


print("=== 7. ⭐ Migratsiya rejasi ===\n")

REJA = [
    ("v1.5", "Yangi ierarxiya QO'SHILADI",
     "Eski sinflar yangilardan meros oladi", "✅ Buzmaydi"),
    ("v2.0", "Hujjatda yangi nomlar",
     "Eskilar hamon ishlaydi, ogohlantirishsiz", "✅ Buzmaydi"),
    ("v2.5", "DeprecationWarning qo'shiladi",
     "Foydalanuvchi ogohlantiriladi", "⚠️ Ogohlantirish"),
    ("v3.0", "CHANGELOG da katta e'lon",
     "Eskilar 1 yil ishlaydi", "⚠️ Ogohlantirish"),
    ("v4.0", "Eski nomlar OLIB TASHLANADI",
     "Major versiya — buzuvchi o'zgarish", "❌ Buzadi"),
]

print(f"  {'Versiya':<9} {'Qadam':<34} {'Ta`sir'}")
print("  " + "─" * 66)
for v, qadam, izoh, tasir in REJA:
    print(f"  {v:<9} {qadam:<34} {tasir}")
    print(f"  {'':<9} {izoh}")
    print()

print("""  ⭐ SEMVER (semver.org) QOIDASI:

     PATCH (1.0.1)  — ierarxiya O'ZGARMAYDI
     MINOR (1.1.0)  — sinf QO'SHISH, oraliq toifa QO'SHISH
     MAJOR (2.0.0)  — sinf OLIB TASHLASH, asosni TORAYTIRISH

  ⭐ AMALIY MASLAHAT:

     • Kutubxona chiqarishdan OLDIN ierarxiyani o'ylang
     • Bitta asos sinfdan boshlang
     • Toifalarni KEYIN qo'shish oson
     • Toifani OLIB TASHLASH qiyin
""")


print("=== 8. ⭐ Ierarxiya testi ===\n")


class KutubxonaXatosi(Exception):
    """Asos."""


class TarmoqXatosi(KutubxonaXatosi):
    """Tarmoq."""


class VaqtTugadi(TarmoqXatosi):
    """Vaqt."""


class MalumotXatosi(KutubxonaXatosi):
    """Ma'lumot."""


def test_ierarxiya():
    """⭐ Bu testlar ierarxiyani MUZLATADI.

    Kelajakdagi o'zgarishlar buzsa — test qulaydi.
    """
    natijalar = []

    # ⭐ 1. Barcha istisnolar asosdan meros
    for S in (TarmoqXatosi, VaqtTugadi, MalumotXatosi):
        ok = issubclass(S, KutubxonaXatosi)
        natijalar.append((f"{S.__name__} ⊂ KutubxonaXatosi", ok))

    # ⭐ 2. Asos — Exception, BaseException emas
    ok = (issubclass(KutubxonaXatosi, Exception)
          and KutubxonaXatosi is not BaseException)
    natijalar.append(("Asos — Exception avlodi", ok))

    # ⭐ 3. Toifalar to'g'ri
    natijalar.append((
        "VaqtTugadi ⊂ TarmoqXatosi",
        issubclass(VaqtTugadi, TarmoqXatosi),
    ))
    natijalar.append((
        "MalumotXatosi ⊄ TarmoqXatosi",
        not issubclass(MalumotXatosi, TarmoqXatosi),
    ))

    # ⭐ 4. Chuqurlik chegarasi
    def chuq(S):
        return len([K for K in S.__mro__
                    if issubclass(K, KutubxonaXatosi)])
    eng = max(chuq(S) for S in (TarmoqXatosi, VaqtTugadi,
                                MalumotXatosi))
    natijalar.append((f"Chuqurlik ≤ 3 (joriy: {eng})", eng <= 3))

    # ⭐ 5. Har sinfda docstring
    hammasi = all(
        (S.__doc__ or "").strip()
        for S in (KutubxonaXatosi, TarmoqXatosi, VaqtTugadi,
                  MalumotXatosi)
    )
    natijalar.append(("Har sinfda docstring", hammasi))

    # ⭐ 6. str(e) bo'sh emas
    bosh_emas = all(
        str(S("xabar")) == "xabar"
        for S in (TarmoqXatosi, VaqtTugadi, MalumotXatosi)
    )
    natijalar.append(("str(e) to'g'ri ishlaydi", bosh_emas))

    return natijalar


print(f"  {'Test':<42} {'Natija'}")
print("  " + "─" * 54)
for nom, ok in test_ierarxiya():
    print(f"  {nom:<42} {'✅ PASS' if ok else '❌ FAIL'}")

print(f"""
  ⭐ NEGA IERARXIYA TESTLARI KERAK:

     • Refaktoring paytida tasodifan buzilishi mumkin
     • Yangi dasturchi noto'g'ri joyga qo'shishi mumkin
     • Buzuvchi o'zgarishlar TESTDA ko'rinadi
     • Hujjat vazifasini bajaradi

  ⚠️ pytest da:

     @pytest.mark.parametrize("Sinf", [TarmoqXatosi, VaqtTugadi])
     def test_asos(Sinf):
         assert issubclass(Sinf, KutubxonaXatosi)
""")

Natijaning muhim qismi:

text
=== 2. ✅ v2.0 — TOIFA QO'SHISH (xavfsiz) ===

  Eski kod:  except V2_KutubxonaXatosi:
    Ishlaydimi? True  ✅

  Yangi kod: except V2_TarmoqXatosi:
    Ishlaydimi? True  ✅

=== 3. ❌ v3.0 — ASOSNI ALMASHTIRISH (buzadi) ===

  Eski kod:  except V2_KutubxonaXatosi:
    Ishlaydimi? False  ❌ BUZILDI

=== 5. ⭐ DeprecationWarning bilan ===

  Eski sinfdan foydalanish:
    ⚠️ DeprecationWarning: EskiXato eskirgan, YangiXato ishlating
       (v4.0 da olib tashlanadi)

=== 8. ⭐ Ierarxiya testi ===

  Test                                       Natija
  ──────────────────────────────────────────────────────
  TarmoqXatosi ⊂ KutubxonaXatosi             ✅ PASS
  Asos — Exception avlodi                    ✅ PASS
  Chuqurlik ≤ 3 (joriy: 3)                   ✅ PASS

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

Misol 4 — Amaliy: IlmHamroh platformasi ierarxiyasi

python
"""To'liq platforma uchun istisno ierarxiyasi."""

from __future__ import annotations

import json
from dataclasses import dataclass
from datetime import datetime
from enum import Enum
from typing import Any, ClassVar

print("=== WISAR PLATFORMASI — ISTISNO IERARXIYASI ===\n")


class Qatlam(str, Enum):
    """⭐ Xato qaysi qatlamda yuzaga keldi."""
    TAQDIMOT = "taqdimot"       # HTTP, CLI
    DOMEN = "domen"             # biznes mantiq
    INFRA = "infra"             # DB, tarmoq, fayl

    def __str__(self): return self.value


# ═══════════════════════════════════════════════
# 1-DARAJA: ASOS
# ═══════════════════════════════════════════════

class WisarXatosi(Exception):
    """⭐ Platformaning BARCHA xatolari.

    Foydalanuvchi bitta blok bilan hammasini ushlaydi:
        except WisarXatosi: ...
    """

    QATLAM: ClassVar[Qatlam] = Qatlam.DOMEN
    HTTP_KODI: ClassVar[int] = 500
    QAYTA_URINISH: ClassVar[bool] = False
    OMMAVIY: ClassVar[bool] = False     # foydalanuvchiga ko'rsatiladimi
    XABAR: ClassVar[str] = "Xato yuz berdi"

    def __init__(self, xabar: str = "", **kontekst: Any):
        self.kontekst = kontekst
        self.vaqt = datetime.now()
        super().__init__(xabar or self.XABAR)

    def __str__(self) -> str:
        asos = self.args[0] if self.args else self.XABAR
        if not self.kontekst:
            return asos
        k = ", ".join(f"{a}={b!r}" for a, b in self.kontekst.items())
        return f"{asos} ({k})"

    def javob(self) -> tuple[int, dict]:
        return self.HTTP_KODI, {
            "xato": type(self).__name__,
            "xabar": (str(self) if self.OMMAVIY else self.XABAR),
            "qatlam": str(self.QATLAM),
        }


# ═══════════════════════════════════════════════
# 2-DARAJA: TOIFALAR (6 ta)
# ═══════════════════════════════════════════════

class SorovXatosi(WisarXatosi):
    """Kiruvchi so'rov bilan bog'liq — mijoz aybi."""
    QATLAM = Qatlam.TAQDIMOT
    HTTP_KODI = 400
    OMMAVIY = True
    XABAR = "So'rov noto'g'ri"


class RuxsatXatosi(WisarXatosi):
    """Autentifikatsiya va avtorizatsiya."""
    QATLAM = Qatlam.TAQDIMOT
    HTTP_KODI = 403
    OMMAVIY = True
    XABAR = "Ruxsat yo'q"


class BiznesXatosi(WisarXatosi):
    """Biznes qoidasi buzildi — mijoz tuzatishi mumkin."""
    QATLAM = Qatlam.DOMEN
    HTTP_KODI = 409
    OMMAVIY = True
    XABAR = "Amal bajarib bo'lmadi"


class MalumotXatosi(WisarXatosi):
    """Ma'lumotlar bazasi bilan bog'liq."""
    QATLAM = Qatlam.INFRA
    HTTP_KODI = 500
    XABAR = "Ma'lumotlar bazasi xatosi"


class TashqiXizmatXatosi(WisarXatosi):
    """Tashqi API va xizmatlar."""
    QATLAM = Qatlam.INFRA
    HTTP_KODI = 502
    QAYTA_URINISH = True
    XABAR = "Tashqi xizmat javob bermadi"


class SozlamaXatosi(WisarXatosi):
    """Noto'g'ri konfiguratsiya — dasturchi aybi."""
    QATLAM = Qatlam.INFRA
    HTTP_KODI = 500
    XABAR = "Tizim sozlamasi xato"


# ═══════════════════════════════════════════════
# 3-DARAJA: ANIQ XATOLAR
# ═══════════════════════════════════════════════

# ── So'rov ──

class ValidatsiyaXatosi(SorovXatosi):
    """Kiruvchi ma'lumot yaroqsiz."""
    HTTP_KODI = 422

    def __init__(self, maydon: str, sabab: str):
        self.maydon, self.sabab = maydon, sabab
        super().__init__(f"{maydon}: {sabab}", maydon=maydon)


class TopilmadiXatosi(SorovXatosi):
    """So'ralgan resurs mavjud emas."""
    HTTP_KODI = 404

    def __init__(self, tur: str, id_: Any):
        self.tur, self.id = tur, id_
        super().__init__(f"{tur} topilmadi", tur=tur, id=id_)


class TezlikChegarasi(SorovXatosi):
    """So'rovlar chegarasi oshdi."""
    HTTP_KODI = 429

    def __init__(self, chegara: int, kutish: float = 60.0):
        self.chegara, self.kutish = chegara, kutish
        super().__init__(f"Chegara: {chegara} so'rov/daqiqa",
                         kutish=kutish)


# ── Ruxsat ──

class AutentifikatsiyaXatosi(RuxsatXatosi):
    """Kim ekanligi tasdiqlanmadi."""
    HTTP_KODI = 401
    XABAR = "Tizimga kiring"


class HuquqYetarliEmas(RuxsatXatosi):
    """Kim ekanligi ma'lum, lekin huquq yo'q."""

    def __init__(self, kerak: str):
        self.kerak = kerak
        super().__init__(f"Huquq kerak: {kerak}", kerak=kerak)


# ── Biznes ──

class TakrorYozuv(BiznesXatosi):
    """Bunday yozuv allaqachon mavjud."""

    def __init__(self, tur: str, maydon: str, qiymat: Any):
        self.tur, self.maydon = tur, maydon
        super().__init__(f"{tur} mavjud: {maydon}={qiymat!r}",
                         tur=tur, maydon=maydon)


class HolatNotogri(BiznesXatosi):
    """Obyekt bu amal uchun noto'g'ri holatda."""

    def __init__(self, obyekt: str, joriy: str, kerak: str):
        self.obyekt, self.joriy, self.kerak = obyekt, joriy, kerak
        super().__init__(
            f"{obyekt} holati '{joriy}', '{kerak}' bo'lishi kerak",
            obyekt=obyekt,
        )


class BogliqlikXatosi(BiznesXatosi):
    """Boshqa yozuvlar bunga bog'liq."""

    def __init__(self, tur: str, bogliq_soni: int):
        self.tur, self.bogliq_soni = tur, bogliq_soni
        super().__init__(
            f"{tur} o'chirib bo'lmaydi: {bogliq_soni} ta bog'liq yozuv",
            tur=tur,
        )


# ── Ma'lumot ──

class UlanmadiXato(MalumotXatosi):
    """Bazaga ulanib bo'lmadi."""
    QAYTA_URINISH = True
    HTTP_KODI = 503

    def __init__(self, baza: str, urinish: int = 1):
        self.baza, self.urinish = baza, urinish
        super().__init__(f"Bazaga ulanib bo'lmadi: {baza}",
                         urinish=urinish)


class TranzaksiyaXatosi(MalumotXatosi):
    """Tranzaksiya bekor qilindi."""
    QAYTA_URINISH = True

    def __init__(self, sabab: str, qaytarildi: bool = True):
        self.sabab, self.qaytarildi = sabab, qaytarildi
        super().__init__(f"Tranzaksiya bekor: {sabab}",
                         qaytarildi=qaytarildi)


class SorovVaqtTugadi(MalumotXatosi):
    """Baza so'rovi juda uzoq davom etdi."""
    HTTP_KODI = 504
    QAYTA_URINISH = True

    def __init__(self, sekund: float, sorov: str = ""):
        self.sekund = sekund
        super().__init__(f"So'rov vaqti tugadi ({sekund}s)",
                         sekund=sekund)


# ── Tashqi xizmat ──

class XizmatMavjudEmas(TashqiXizmatXatosi):
    """Tashqi xizmat ishlamayapti."""
    HTTP_KODI = 503

    def __init__(self, xizmat: str, kod: int = 0):
        self.xizmat, self.kod = xizmat, kod
        super().__init__(f"{xizmat} mavjud emas", xizmat=xizmat)


class XizmatVaqtTugadi(TashqiXizmatXatosi):
    """Tashqi xizmat vaqtida javob bermadi."""
    HTTP_KODI = 504

    def __init__(self, xizmat: str, sekund: float):
        self.xizmat, self.sekund = xizmat, sekund
        super().__init__(f"{xizmat} vaqt tugadi ({sekund}s)",
                         xizmat=xizmat)


# ── Sozlama ──

class SozlamaYoq(SozlamaXatosi):
    """Majburiy sozlama berilmagan."""

    def __init__(self, kalit: str):
        self.kalit = kalit
        super().__init__(f"Sozlama yo'q: {kalit}", kalit=kalit)


class SozlamaNotogri(SozlamaXatosi):
    """Sozlama qiymati yaroqsiz."""

    def __init__(self, kalit: str, qiymat: Any, kutilgan: str):
        self.kalit, self.qiymat = kalit, qiymat
        super().__init__(
            f"Sozlama noto'g'ri: {kalit}={qiymat!r}, "
            f"kutilgan: {kutilgan}",
            kalit=kalit,
        )


# ═══════════════════════════════════════════════
# TAHLIL
# ═══════════════════════════════════════════════

print("1. Ierarxiya:\n")


def chiz(S: type, chuqurlik: int = 0) -> list[str]:
    belgi = {"taqdimot": "🌐", "domen": "🏢", "infra": "⚙️"}[
        str(S.QATLAM)
    ]
    nom = f"{'  ' * chuqurlik}{'└─ ' if chuqurlik else ''}{S.__name__}"
    q = [f"  {nom:<40} {belgi} {S.HTTP_KODI}"
         f"{'  🔄' if S.QAYTA_URINISH else ''}"]
    for A in sorted(S.__subclasses__(), key=lambda c: c.__name__):
        q.extend(chiz(A, chuqurlik + 1))
    return q


for q in chiz(WisarXatosi):
    print(q)


print("\n\n2. ⭐ O'lchamlar:\n")


def barchasi(S):
    n = [S]
    for A in S.__subclasses__():
        n.extend(barchasi(A))
    return n


HAMMASI = barchasi(WisarXatosi)
TOIFALAR = WisarXatosi.__subclasses__()


def chuqurlik(S):
    return len([K for K in S.__mro__ if issubclass(K, WisarXatosi)])


print(f"  Jami sinf:            {len(HAMMASI)}")
print(f"  Toifalar:             {len(TOIFALAR)}")
print(f"  Aniq xatolar:         {len(HAMMASI) - len(TOIFALAR) - 1}")
print(f"  Maks chuqurlik:       {max(chuqurlik(S) for S in HAMMASI)}")
print(f"  Qayta urinish mumkin: "
      f"{sum(1 for S in HAMMASI if S.QAYTA_URINISH)}")
print(f"  Ommaviy xabar:        "
      f"{sum(1 for S in HAMMASI if S.OMMAVIY)}")


print("\n\n3. ⭐ Qatlamlar bo'yicha:\n")

for q in Qatlam:
    mos = [S for S in HAMMASI if S.QATLAM is q and S.__subclasses__() == []]
    belgi = {"taqdimot": "🌐", "domen": "🏢", "infra": "⚙️"}[str(q)]
    print(f"  {belgi} {str(q).upper():<12} ({len(mos)}):")
    for S in sorted(mos, key=lambda c: c.__name__):
        print(f"      {S.__name__:<26} HTTP {S.HTTP_KODI}")
    print()


print("4. ⭐ Ushlash strategiyalari:\n")

XATOLAR = [
    ValidatsiyaXatosi("email", "format noto'g'ri"),
    TopilmadiXatosi("Xodim", 999),
    TezlikChegarasi(100, 45.0),
    AutentifikatsiyaXatosi(),
    HuquqYetarliEmas("admin"),
    TakrorYozuv("Xodim", "email", "aziz@wisar.uz"),
    HolatNotogri("Buyurtma", "yakunlangan", "kutilmoqda"),
    BogliqlikXatosi("Bo'lim", 12),
    UlanmadiXato("wisar-db", 2),
    TranzaksiyaXatosi("deadlock"),
    SorovVaqtTugadi(30.0),
    XizmatMavjudEmas("payme-api", 503),
    XizmatVaqtTugadi("eskiz-sms", 10.0),
    SozlamaYoq("DATABASE_URL"),
    SozlamaNotogri("PORT", "abc", "butun son"),
]

STRATEGIYALAR = [
    ("HTTP handler", [
        (SorovXatosi, "4xx qaytarish — mijozga xabar"),
        (RuxsatXatosi, "401/403 — qayta kirish"),
        (BiznesXatosi, "409 — biznes xabari"),
        (WisarXatosi, "500 — umumiy xabar + jurnal"),
    ]),
    ("Fon vazifasi", [
        (TashqiXizmatXatosi, "Navbatga qaytarish"),
        (MalumotXatosi, "Qayta urinish"),
        (SozlamaXatosi, "Vazifani to'xtatish + ogohlantirish"),
    ]),
]

for nom, bloklar in STRATEGIYALAR:
    print(f"  {nom}:\n")
    ushlangan: set = set()
    for Tur, ish in bloklar:
        mos = [e for e in XATOLAR
               if isinstance(e, Tur) and id(e) not in ushlangan]
        ushlangan |= {id(e) for e in mos}
        print(f"    except {Tur.__name__:<22} → {ish}")
        print(f"      {len(mos)} xato: "
              f"{', '.join(type(e).__name__ for e in mos[:3])}"
              f"{' ...' if len(mos) > 3 else ''}")
    print()


print("5. ⭐ HTTP javoblar:\n")

print(f"  {'Istisno':<24} {'HTTP':<6} {'Ommaviy':<9} {'Xabar'}")
print("  " + "─" * 78)
for e in XATOLAR:
    kod, tana = e.javob()
    print(f"  {type(e).__name__:<24} {kod:<6} "
          f"{'✅' if e.OMMAVIY else '🔒':<9} {tana['xabar'][:32]}")

print(f"""
  ⭐ OMMAVIY=False — texnik tafsilot YASHIRILADI:

     Ichki:  "Bazaga ulanib bo'lmadi: wisar-db (urinish=2)"
     Tashqi: "Ma'lumotlar bazasi xatosi"

  ⚠️ NEGA: xato xabari SIR OSHKOR qilishi mumkin
     (baza nomi, ichki IP, fayl yo'llari, SQL so'rovlar).
""")


print("6. ⭐ Qayta urinish qarori:\n")

print(f"  {'Istisno':<24} {'Qayta urinish':<16} {'Sabab'}")
print("  " + "─" * 70)
for e in XATOLAR:
    if e.QAYTA_URINISH:
        sabab = "Vaqtinchalik — o'tib ketishi mumkin"
        belgi = "🔄 HA"
    elif isinstance(e, (SorovXatosi, RuxsatXatosi, BiznesXatosi)):
        sabab = "Mijoz tuzatishi kerak"
        belgi = "❌ YO'Q"
    elif isinstance(e, SozlamaXatosi):
        sabab = "Dasturchi tuzatishi kerak"
        belgi = "❌ YO'Q"
    else:
        sabab = "Noma'lum — jurnalga"
        belgi = "❌ YO'Q"
    print(f"  {type(e).__name__:<24} {belgi:<16} {sabab}")


print("\n\n7. ⭐ Ierarxiya testlari:\n")


def testlar() -> list[tuple[str, bool]]:
    n = []

    n.append(("Barcha sinflar WisarXatosi avlodi",
              all(issubclass(S, WisarXatosi) for S in HAMMASI)))
    n.append(("Asos — Exception (BaseException emas)",
              issubclass(WisarXatosi, Exception)))
    n.append((f"Chuqurlik ≤ 3 "
              f"(joriy: {max(chuqurlik(S) for S in HAMMASI)})",
              max(chuqurlik(S) for S in HAMMASI) <= 3))
    n.append((f"Toifalar 2-8 orasida (joriy: {len(TOIFALAR)})",
              2 <= len(TOIFALAR) <= 8))
    n.append(("Har sinfda docstring",
              all((S.__doc__ or "").strip() for S in HAMMASI)))
    n.append(("Har sinfda HTTP_KODI 100-599",
              all(100 <= S.HTTP_KODI <= 599 for S in HAMMASI)))
    n.append(("Bo'sh oraliq sinf yo'q",
              all(len(S.__subclasses__()) != 1 for S in HAMMASI)))
    n.append(("4xx xatolar OMMAVIY",
              all(S.OMMAVIY for S in HAMMASI
                  if 400 <= S.HTTP_KODI < 500)))
    n.append(("5xx xatolar QAYTA_URINISH yoki SozlamaXatosi",
              all(S.QAYTA_URINISH or issubclass(S, SozlamaXatosi)
                  or S in (WisarXatosi, MalumotXatosi)
                  for S in HAMMASI if S.HTTP_KODI >= 500)))
    n.append(("str(e) hech qachon bo'sh emas",
              all(str(S("test")) for S in (ValidatsiyaXatosi.__bases__
                                           + (WisarXatosi,)))))
    return n


for nom, ok in testlar():
    print(f"  {'✅' if ok else '❌'} {nom}")

print(f"""

  ⭐ BU IERARXIYADA:

     1-DARAJA: WisarXatosi
       → foydalanuvchi hammasini ushlashi mumkin

     2-DARAJA: 6 toifa
       So'rov / Ruxsat / Biznes / Ma'lumot / Tashqi / Sozlama
       → har biri HAR XIL ishlov talab qiladi

     3-DARAJA: 15 aniq xato
       → atributlar bilan boy kontekst

  ⭐ QARORLAR:

     • Chuqurlik 3 — ko'proq emas
     • ClassVar sozlamalar merosda uzatiladi
     • OMMAVIY — sirlarni himoya qiladi
     • QAYTA_URINISH — avtomatik qaror
     • QATLAM — monitoring va guruhlash uchun

  ⭐ KENGAYTIRISH:

     Yangi xato → mos TOIFAGA qo'shiladi
     Yangi toifa → WisarXatosi ostiga (kamdan-kam)
     ⚠️ Toifani OLIB TASHLASH — major versiya
""")

Natijaning muhim qismi:

text
1. Ierarxiya:

  WisarXatosi                              🏢 500
    └─ BiznesXatosi                        🏢 409
      └─ BogliqlikXatosi                   🏢 409
      └─ HolatNotogri                      🏢 409
      └─ TakrorYozuv                       🏢 409
    └─ MalumotXatosi                       ⚙️ 500
      └─ SorovVaqtTugadi                   ⚙️ 504  🔄
      └─ TranzaksiyaXatosi                 ⚙️ 500  🔄
      └─ UlanmadiXato                      ⚙️ 503  🔄

2. ⭐ O'lchamlar:

  Jami sinf:            22
  Toifalar:             6
  Aniq xatolar:         15
  Maks chuqurlik:       3

5. ⭐ HTTP javoblar:

  Istisno                  HTTP   Ommaviy   Xabar
  ──────────────────────────────────────────────────────────────────────────
  ValidatsiyaXatosi        422    ✅        email: format noto'g'ri
  UlanmadiXato             503    🔒        Ma'lumotlar bazasi xatosi

Nima ko'rsatdi: 2.2, 2.5, 2.7-bo'limlar.


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

Noto'g'ri fikr To'g'risi
"Chuqur ierarxiya — professional" 3 daraja yetadi
"Har xato uchun alohida sinf" Atribut ko'pincha yaxshiroq
"Ierarxiyani keyin tuzataman" Buzuvchi o'zgarish bo'ladi
"IOError va OSError — boshqa" Bir xil (PEP 3151)
"Oraliq sinf qo'shish buzadi" Xavfsiz — MRO kengayadi
"Ko'p meros — yomon" Migratsiyada foydali
"Ierarxiyani test qilish ortiqcha" Buzilishni tutadi
"NotImplementedError — TypeError avlodi" RuntimeError avlodi

6. Keng tarqalgan xatolar va yechimlari

1. Asos sinf yo'q

python
class XatoA(Exception): ...             # ⚠️ tekis
class KutubxonaXatosi(Exception): ...   # ✅

2. Juda chuqur

python
A → B → C → D → E                       # ⚠️ 5 daraja
Asos → Toifa → Aniq                     # ✅ 3 daraja

3. Sinf portlashi

python
class Xato400, Xato401, Xato403 ...     # ⚠️
class HTTPXatosi(kod=403)               # ✅ atribut

4. Bo'sh oraliq sinf

python
class Oraliq(Asos): ...                 # ⚠️ 1 avlod
class Haqiqiy(Oraliq): ...              # ✅ birlashtiring

5. Asosni toraytirish

python
class X(TarmoqXatosi): ...              # v1
class X(Exception): ...                 # ❌ v2 — buzadi

6. Mantiqsiz meros

python
class VaqtTugadi(ValidatsiyaXatosi)     # ⚠️ aloqasi yo'q
class VaqtTugadi(TarmoqXatosi)          # ✅

7. Docstring yo'q

python
class TarmoqXatosi(Asos): pass          # ⚠️ nima uchun?
    """Tarmoq va tashqi xizmat."""      # ✅

8. Ierarxiya testsiz

python
def test_asos():
    assert issubclass(VaqtTugadi, KutubxonaXatosi)   # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 9.6-dars (o'tilgan): o'z istisno sinflaringiz
  • 9.8-dars: amaliy xatolar bilan ishlash
  • 8-qism: meros, MRO, ClassVar
  • 17-qism: ierarxiya testlari (pytest)
  • 20-qism: HTTP xato kodlari
  • requests, httpx, sqlalchemy — namunali ierarxiyalar

8. Eng yaxshi amaliyotlar

  1. Bitta asos sinfdan boshlang. Kengaytirish oson, torayish — buzuvchi.

  2. Chuqurlikni 3 daraja bilan cheklang. Asos → Toifa → Aniq.

  3. Toifa — har xil ishlov talab qilsa. Bir xil bo'lsa — kerak emas.

  4. Atribut sinfdan ustun. "Alohida ushlanadimi?" — mezon.

  5. Har sinfda docstring. Qachon ko'tariladi, nima qilish kerak.

  6. Ierarxiyani testlar bilan muzlating. issubclass tekshiruvlari.

  7. Faqat kengaytiring. Oraliq toifa qo'shish — xavfsiz.

  8. Migratsiyada ko'p meros. Eski + yangi asos birga.


9. Amaliy topshiriq

Vazifa 1: Natijani bashorat qiling

python
1.  print(issubclass(KeyError, LookupError))
2.  print(issubclass(TimeoutError, OSError))
3.  print(IOError is OSError, EnvironmentError is OSError)
4.  print(type(OSError(2, "x")).__name__)
5.  print(issubclass(NotImplementedError, RuntimeError))
6.  print(issubclass(UnicodeDecodeError, ValueError))
7.  print(issubclass(TabError, SyntaxError))
8.  print(issubclass(KeyboardInterrupt, Exception))
9.  print(issubclass(ModuleNotFoundError, ImportError))
10. print(len(FileNotFoundError.__mro__))
11. print(issubclass(ConnectionResetError, ConnectionError),
          issubclass(ConnectionError, OSError))
12. print(issubclass(RecursionError, RuntimeError),
          issubclass(MemoryError, Exception))
Javoblar
  1. True
  2. True — 3.3 dan
  3. True True — PEP 3151
  4. FileNotFoundError — errno sehri
  5. True — RuntimeError avlodi
  6. True — ValueError → UnicodeError → UnicodeDecodeError
  7. True — SyntaxError → IndentationError → TabError
  8. False — BaseException avlodi
  9. True
  10. 5 — FileNotFoundError, OSError, Exception, BaseException, object
  11. True True
  12. True True

Vazifa 2: Ierarxiyani tuzating

python
1.  class XatoA(Exception): pass
    class XatoB(Exception): pass
2.  class A(Exception): pass
    class B(A): pass
    class C(B): pass
    class D(C): pass
    class E(D): pass
3.  class Xato400(Asos), Xato401(Asos), ..., Xato503(Asos)
4.  class Oraliq(Asos): pass
    class Haqiqiy(Oraliq): pass
5.  class VaqtTugadi(ValidatsiyaXatosi): pass
6.  # v2.0 da:
    class X(Exception): pass    # oldin: class X(KutubxonaXatosi)
7.  class TarmoqXatosi(Asos): pass      # docstring yo'q
8.  class X(TarmoqXatosi, MalumotXatosi): pass
Javoblar
python
1.  Umumiy asos: class KutubxonaXatosi(Exception)
2.  3 darajaga qisqartiring: A → B → C
3.  class HTTPXatosi(Asos) + kod atributi
4.  Oraliq ni olib tashlang: class Haqiqiy(Asos)
5.  class VaqtTugadi(TarmoqXatosi)
6.  ❌ Buzuvchi — KutubxonaXatosi ni qaytaring
7.  """Tarmoq va tashqi xizmat xatolari."""
8.  Bitta toifa tanlang yoki yangi toifa yarating

Vazifa 3: Ierarxiya loyihalang

Onlayn do'kon uchun:

  1. Bitta asos sinf
  2. 4-6 toifa (buyurtma, to'lov, yetkazish, ombor, ...)
  3. Har toifada 2-4 aniq xato
  4. ClassVar sozlamalar (HTTP, qayta urinish, ommaviy)
  5. Chuqurlik ≤ 3
  6. Har sinfda docstring

Vazifa 4: Ierarxiya tahlilchisi

Vosita yozing:

  1. Daraxtni chizsin (__subclasses__)
  2. Chuqurlik va kenglikni o'lchasin
  3. Bo'sh oraliq sinflarni topsin
  4. Docstringsizlarni topsin
  5. Mantiqsiz meroslarni bashorat qilsin
  6. Markdown hisobot chiqarsin

Vazifa 5: Migratsiya

Tekis ierarxiyani muvozanatliga aylantiring:

  1. Toifalarni aniqlang
  2. Oraliq sinflar qo'shing (buzmasdan)
  3. Eski nomlar uchun taxalluslar
  4. DeprecationWarning qo'shing
  5. __all__ ni yangilang
  6. CHANGELOG yozing

Vazifa 6: Ierarxiya testlari

pytest testlari:

  1. Barcha sinflar asosdan meros
  2. Chuqurlik chegarasi
  3. Docstring mavjudligi
  4. ClassVar qiymatlari to'g'ri
  5. Bo'sh oraliq yo'qligi
  6. str(e) bo'sh emasligi

Vazifa 7: O'ylash

Nega Python OSError oilasini errno tekshirishdan alohida sinflarga o'tkazdi (PEP 3151) — bu 10 yil kutildi?

Javob

Chunki errno bilan ishlash noqulay, platformaga bog'liq va xato bo'lishi oson edi — lekin tuzatish orqaga moslikni buzardi.

1. Eski holat — 6 ta sinf, bitta ma'no

Python 3.3 gacha:

python
IOError                     # fayl amallar
OSError                     # tizim chaqiruvlari
WindowsError                # ⚠️ faqat Windows
EnvironmentError            # ⚠️ IOError + OSError asosi
socket.error                # ⚠️ tarmoq
select.error                # ⚠️ select moduli
mmap.error                  # ⚠️ mmap

Muammo: ular bir xil narsani anglatardi, lekin dasturchi qaysi birini ushlashni bilmasdi:

python
try:
    f = open("fayl.txt")
except IOError:                 # ⚠️ yoki OSError?
    ...

try:
    s.connect(...)
except socket.error:            # ⚠️ IOError emas!
    ...

Chegaralar tasodifiy edi:

python
open("yoq.txt")                 # IOError
os.remove("yoq.txt")            # OSError    ⚠️ nega boshqa?
os.stat("yoq.txt")              # OSError

2. errno tekshirish — asosiy og'riq

python
import errno

try:
    with open(yol) as f:
        ...
except IOError as e:
    if e.errno == errno.ENOENT:
        print("Fayl yo'q")
    elif e.errno == errno.EACCES:
        print("Ruxsat yo'q")
    elif e.errno == errno.EISDIR:
        print("Bu papka")
    else:
        raise

Muammolar:

  • Batafsil — 3 qatorlik ish 8 qator
  • import errno kerak
  • Konstanta nomlari — ENOENT? ENOTDIR? Eslash qiyin
  • else: raise unutiladi → xatolar yutiladi
  • Platformaga bog'liq — Windows da boshqa kodlar

3. Windows muammosi

python
# Unix
e.errno == errno.ENOENT         # 2

# Windows
e.errno == errno.ENOENT         # ba'zan
e.winerror == 2                 # ⚠️ ba'zan boshqa kod

WindowsError — faqat Windows da mavjud:

python
try:
    ...
except WindowsError:            # ❌ Linux da NameError!
    ...

Shuning uchun kross-platforma kod yozish juda qiyin edi.

4. PEP 3151 (2011) — Antoine Pitrou

Taklif:

OSError                         ⭐ YAGONA asos
 ├── BlockingIOError            EAGAIN, EALREADY, EWOULDBLOCK, EINPROGRESS
 ├── ChildProcessError          ECHILD
 ├── ConnectionError            ⭐ ichki guruh
 │    ├── BrokenPipeError       EPIPE, ESHUTDOWN
 │    ├── ConnectionAbortedError  ECONNABORTED
 │    ├── ConnectionRefusedError  ECONNREFUSED
 │    └── ConnectionResetError    ECONNRESET
 ├── FileExistsError            EEXIST
 ├── FileNotFoundError          ENOENT
 ├── InterruptedError           EINTR
 ├── IsADirectoryError          EISDIR
 ├── NotADirectoryError         ENOTDIR
 ├── PermissionError            EACCES, EPERM
 ├── ProcessLookupError         ESRCH
 └── TimeoutError               ETIMEDOUT

Barcha eski nomlar taxallusga aylandi:

python
IOError = OSError
EnvironmentError = OSError
WindowsError = OSError          # (faqat Windows da)
socket.error = OSError
select.error = OSError
mmap.error = OSError

5. __new__ sehri — orqaga moslik kaliti

Eng aqlli qism:

python
class OSError(Exception):
    def __new__(cls, *args):
        if cls is OSError and len(args) >= 2:
            errno_ = args[0]
            yangi = _ERRNO_XARITA.get(errno_)
            if yangi is not None:
                cls = yangi              # ⭐ AVLODNI ALMASHTIRADI
        return super().__new__(cls, *args)

Natija:

python
OSError(2, "Fayl yo'q")         # → FileNotFoundError
OSError(13, "Ruxsat")           # → PermissionError

Nega bu muhim:

Minglab kutubxonalar OSError(errno, msg) yozgan edi. Ular hech narsa o'zgartirmasdan yangi avlodlarni ko'tara boshladi.

python
# Eski kutubxona kodi (o'zgarmagan):
raise OSError(errno.ENOENT, "Konfiguratsiya topilmadi")

# Yangi foydalanuvchi kodi:
except FileNotFoundError:       # ⭐ ISHLAYDI!

6. Nega 10 yil kutildi

a) Orqaga moslik qo'rquvi

python
# ⚠️ Bu kod buziladimi?
except IOError:
    ...

Javob: yo'q — IOError OSError ga taxallus, va yangi sinflar uning avlodlari.

b) Python 2 → 3 o'tish davri

2008-2011 da hamma Python 3 ga o'tish bilan band edi. Yana bir buzuvchi o'zgarish — juda ko'p.

c) Qaysi errno qaysi sinfga

Munozara uzoq davom etdi:

EACCES va EPERM — bitta sinfmi?         → ha, PermissionError
EAGAIN, EWOULDBLOCK — bir xilmi?        → ha, BlockingIOError
ENOTDIR — alohida sinf kerakmi?         → ha
EROFS (read-only fs) — kerakmi?         → yo'q, kam ishlatiladi

Mezon: "bu xatoni alohida ushlash kerakmi?" — 9.6-darsdagi qoida.

d) Sinflar sonini cheklash

errno da 130+ konstanta bor. Hammasiga sinf yaratish — antipattern (sinf portlashi).

Tanlangan: eng ko'p ishlatiladigan 15 tasi. Qolganlar — oddiy OSError + errno.

7. Natija — kod qanchalik soddalashdi

Oldin:

python
import errno

try:
    ish()
except IOError as e:
    if e.errno in (errno.EACCES, errno.EPERM):
        ruxsat_sora()
    elif e.errno == errno.ENOENT:
        yarat()
    elif e.errno == errno.EEXIST:
        pass
    else:
        raise
except OSError as e:                # ⚠️ takror!
    if e.errno == errno.ENOENT:
        ...
    else:
        raise

Keyin:

python
try:
    ish()
except PermissionError:
    ruxsat_sora()
except FileNotFoundError:
    yarat()
except FileExistsError:
    pass

17 qator → 7 qator, va import errno kerak emas.

8. Bundan o'rganish

Dars Tushuntirish
Atribut tekshirish — antipattern if e.errno == X → alohida sinflar
Cheklangan sinflar 130 dan 15 tasi tanlandi
Guruhlash ConnectionError — 4 ta ostida
Taxallus bilan migratsiya IOError = OSError
__new__ bilan avtomatik Eski kod yangi sinflarni ko'taradi
Kengaytirish, toraytirmaslik Barcha eski except ishlaydi

Sizning kutubxonangiz uchun:

python
# ⚠️ Agar shunday yozgan bo'lsangiz:
class APIXatosi(Exception):
    def __init__(self, kod): self.kod = kod

except APIXatosi as e:
    if e.kod == 404: ...            # ⚠️ errno naqshi!
    elif e.kod == 403: ...

# ✅ Ehtimol shunday bo'lishi kerak:
class APIXatosi(Exception): ...
class TopilmadiXatosi(APIXatosi): ...
class RuxsatYoqXatosi(APIXatosi): ...

# ⭐ Va __new__ bilan orqaga moslik:
class APIXatosi(Exception):
    def __new__(cls, kod=0, *args):
        if cls is APIXatosi:
            cls = _XARITA.get(kod, cls)
        return super().__new__(cls, kod, *args)

9. Nima yakunlanmadi

python
issubclass(TimeoutError, OSError)               # True

# ⚠️ Lekin 3.10 gacha:
import asyncio, socket, concurrent.futures
asyncio.TimeoutError is not TimeoutError        # ⚠️ boshqa!
socket.timeout is not TimeoutError              # ⚠️ boshqa!

3.11 da tuzatildi:

python
socket.timeout is TimeoutError                  # True
asyncio.TimeoutError is TimeoutError            # True
concurrent.futures.TimeoutError is TimeoutError # True

Ya'ni OSError birlashtirilganidan 8 yil keyin TimeoutError birlashtirildi. Ierarxiya tozalash — sekin jarayon.

10. Xulosa

  1. errno tekshirish — batafsil, xatoga moyil, platformaga bog'liq
  2. PEP 3151 (3.3, 2012) — 15 ta aniq sinf, 6 ta eski nom taxallusga
  3. OSError.__new__ errno ga qarab avlodni tanlaydi — orqaga moslik
  4. 10 yil kutildi — orqaga moslik qo'rquvi va Python 3 o'tish davri
  5. Dars: atributga qarab shoxlanish (if e.kod == X) — ierarxiya kerakligi belgisi

Amaliy qoida:

python
if e.kod == X: ...              # ⚠️ 3+ marta bo'lsa — sinf yarating
except AniqXato: ...            # ✅

Nimani mustahkamlaydi: 2.1, 2.2, 2.4, 2.6-bo'limlar.


Xulosa

Bu darsda istisnolar ierarxiyasini loyihalashni o'rgandik.

Eng muhim uch fikr:

  1. Uch daraja yetadi: Asos → Toifa → Aniq xato. Bitta asos sinf — foydalanuvchi hammasini ushlashi uchun; 2-6 toifa — har biri har xil ishlov talab qilsa; aniq xatolar — atributlar bilan. Chuqurroq ierarxiya (httpx dagi 4 daraja) — istisno, qoida emas.

  2. Atribut sinfdan ustun — mezon "alohida ushlanadimi?". 20 ta Xato400, Xato401 o'rniga bitta HTTPXatosi(kod=...). Aksincha ham to'g'ri: if e.errno == X naqshi uch marta takrorlansa — bu sinf kerakligi belgisi (PEP 3151 aynan shuni tuzatdi).

  3. Ierarxiyani faqat kengaytiring. Oraliq toifa qo'shish xavfsiz — MRO kengayadi, eski except bloklar ishlashda davom etadi. Asosni toraytirish (class X(TarmoqXatosi) → class X(Exception)) — buzuvchi o'zgarish, major versiya kerak. Migratsiyada ko'p meros va DeprecationWarning yordam beradi, issubclass testlari esa ierarxiyani muzlatadi.

Keyingi darsda 9-qismni yakunlaymiz: xatolar bilan ishlashning amaliy jihatlari — jurnal, monitoring, foydalanuvchi xabarlari va butun tizim darajasidagi strategiya.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
9.7-dars: Istisnolar ierarxiyasi — IlmHamroh