فصل ۸: پروژه و حرفه‌ای شدن — پروژه‌ی پایانی، API، تست، دیباگ و کلینیک خطا

دیباگ حرفه‌ای: breakpoint()، فرمان‌های pdb و دیباگر VS Code

به‌جای حدس زدن، برنامه را وسط اجرا نگه دارید

بیشتر تازه‌کارها با print دیباگ می‌کنند: چند خط چاپ اضافه، اجرا، حدس، پاک کردن و دوباره. دیباگر یعنی برنامه را در یک خط مشخص متوقف کنید، همه‌ی متغیرها را همان لحظه ببینید، قدم‌به‌قدم جلو بروید و حتی عبارت دلخواه اجرا کنید. این کد گزارش ماهانه را ببینید که عدد غلط می‌دهد:

orders = [
    {"created": "2025-04-02", "price": 57_000_000},
    {"created": "2025-04-19", "price": 31_500_000},
    {"created": "2025-05-03", "price": 12_000_000},
]


def monthly_total(orders):
    totals = {}
    for o in orders:
        month = o["created"][:7]
        breakpoint()                     # اجرا این‌جا می‌ایستد
        totals[month] = o["price"]       # باگ: جایگزین می‌کند، جمع نمی‌زند
    return totals


print(monthly_total(orders))   # جمع 2025-04 باید 88,500,000 باشد

با اجرای فایل، پایتون در خط breakpoint() می‌ایستد و اعلان (Pdb) ظاهر می‌شود:

> report.py(13)monthly_total()
-> totals[month] = o["price"]       # باگ: جایگزین می‌کند، جمع نمی‌زند
(Pdb) p month, totals
('2025-04', {})
(Pdb) c
> report.py(13)monthly_total()
-> totals[month] = o["price"]       # باگ: جایگزین می‌کند، جمع نمی‌زند
(Pdb) p totals
{'2025-04': 57000000}
(Pdb) n
> report.py(10)monthly_total()
-> for o in orders:
(Pdb) p totals
{'2025-04': 31500000}          # همین‌جا باگ پیدا شد
(Pdb) q

درمان: totals[month] = totals.get(month, 0) + o["price"]، یا همان Counter درس پروژه.

فرمان‌های پرکاربرد pdb

فرمانکار
n (next)اجرای خط فعلی و رفتن به خط بعد، بدون ورود به تابع‌ها
s (step)ورود به داخل تابعی که در خط فعلی صدا زده می‌شود
c (continue)ادامه تا breakpoint بعدی
p / ppچاپ مقدار / چاپ مرتب dict و لیست‌های بزرگ
l / llنمایش کد اطراف / کل تابع فعلی
w، u، dپشته‌ی فراخوانی؛ بالا و پایین رفتن بین تابع‌ها
b 13گذاشتن breakpoint در خط ۱۳
qخروج

دیباگر VS Code

با افزونه‌ی Python، روی شماره‌ی خط کلیک کنید (یا F9) تا نقطه‌ی قرمز بنشیند و با F5 اجرا کنید. پنل Variables همه‌ی متغیرها را نشان می‌دهد، Watch عبارت دلخواه را دنبال می‌کند و در Debug Console هر کد پایتونی را در همان لحظه اجرا می‌کنید. F10 معادل n و F11 معادل s است. برای برنامه‌ی خط فرمان، آرگومان‌ها را در .vscode/launch.json بدهید:

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "orders report",
      "type": "debugpy",
      "request": "launch",
      "program": "${workspaceFolder}/cli.py",
      "args": ["report"],
      "console": "integratedTerminal",
      "justMyCode": true,
      "env": {"PYTHONUTF8": "1"}
    }
  ]
}

با راست‌کلیک روی نقطه‌ی قرمز و Edit Breakpoint، شرط بگذارید (مثلاً o["price"] > 50_000_000) تا فقط سفارش‌های مشکوک متوقف شوند؛ یا Logpoint بسازید که بدون توقف و بدون تغییر کد، پیامی مثل {month} چاپ کند.

نکته‌هایی که کمتر کسی می‌داند

  • متغیر محیطی PYTHONBREAKPOINT=0 همه‌ی breakpoint() ها را بی‌اثر می‌کند؛ ولی بهتر است اصلاً در کد نمانند. قاعده‌ی T100 در ruff، breakpoint جامانده را پیش از commit پیدا می‌کند.
  • اگر متغیری به نام n، c یا l دارید، تایپ نامش در pdb فرمان را اجرا می‌کند نه چاپ را؛ بنویسید p n یا !n.
  • python -m pdb -c continue cli.py report برنامه را عادی اجرا می‌کند و فقط وقتی استثنا رخ دهد، دیباگر را در همان نقطه باز می‌کند (post-mortem). در REPL هم بعد از خطا import pdb; pdb.pm() همین کار را می‌کند.
  • فرمان interact در pdb یک REPL کامل با همه‌ی متغیرهای محلی باز می‌کند؛ برای امتحان چند خط درمان پیش از تغییر فایل عالی است.
  • اگر VS Code نقطه‌ی قرمز را خاکستری نشان می‌دهد و نمی‌ایستد، معمولاً مفسر انتخاب‌شده (Python: Select Interpreter) همان .venv پروژه نیست، یا فایل در کتابخانه‌ای است که justMyCode آن را رد می‌کند.

برای ذخیره‌ی پیشرفت و شرکت در آزمون، وارد شوید — رایگان است.