Faragiri

همهٔ زبان‌ها

آموزش بریک‌پوینتاشکال‌زدایی

در فایلی با چند هزار سطر، توقف روی هر سطر بی‌فایده است. یک شرط قبل از breakpoint، برنامه را دقیقا روی همان سطری نگه می‌دارد که مسئله دارد.

۴بخش
۳دقیقه خواندن
۴عمق

یک توقف، چهار حالت

چهار حالتی که یک جلسه اشکال‌زدایی از آن‌ها می‌گذرد

هر قاب یک حالت برنامه است و نه یک کار شما؛ در هیچ‌کدام سطری اجرا نشده مگر آنکه خودتان گفته باشید.

  1. ۰۱ایستاده روی سطر شرطمقدار خراب همین حالا در دسترس است و هیچ سطری از دست نرفته
  2. ۰۲نام‌های همین قابچاپ چند نام با هم، و دیدن اینکه کدام خراب است
  3. ۰۳یک قاب بالاترجایی که مقدار ساخته شد، با متغیرهای خودش
  4. ۰۴ادامه تا توقف بعدیسطر خراب بعدی، و نه سطر بعدی فایل

این چهار قاب یک توقف را می‌کشند. بدون شرط، همین چهار قاب برای هر سطر فایل تکرار می‌شوند و همین است که بریک‌پوینت بی‌شرط را بی‌فایده می‌کند.

یک بریک‌پوینت بی‌شرط روی فایلی با چند هزار سطر یعنی چند هزار بار زدن کلید ادامه. جوابش یک سطر است که کل تفاوت را می‌سازد:

  • if mm < -900: breakpoint()

حالا برنامه فقط وقتی می‌ایستد که مقدار خراب باشد و شما مستقیم داخل همان تکرار هستید. breakpoint() از پایتون 3.7 در خود زبان است و لازم نیست چیزی ایمپورت شود؛ اگر متغیر محیطی PYTHONBREAKPOINT=0 باشد، همان سطر بدون هیچ کاری رد می‌شود، که یعنی می‌شود یک بریک‌پوینت شرطی را در کد نگه داشت و در اجرای عادی خاموش کرد.

پنج دستوری که نود درصد کار را می‌کنند

  • p nam, mm مقدار چند نام را چاپ می‌کند و جواب یک چندتایی است مثل ('Astara', -999.0)
  • n سطر بعدی همین تابع، بدون رفتن داخل فراخوانی‌ها
  • s رفتن داخل فراخوانی همان سطر
  • u یک قاب بالاتر، یعنی به تابعی که این را صدا زده
  • c ادامه تا بریک‌پوینت بعدی

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

وقتی برنامه ایستاده، هنوز یک مفسر کامل دارید

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

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

جایی که کار گیر می‌کند، اضافه کردن print است

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

ابزار، و سالی که این حرف درست است

در سال ۲۰۲۶، breakpoint() در خود زبان است و رایگان، و جای import pdb; pdb.set_trace() را گرفته که پیش از پایتون 3.7 تنها راه بود و دو چیز اضافه در سطر داشت. برای همان کار با رابط گرافیکی، اشکال‌زدای VS Code روی همان مکانیزم می‌نشیند و بریک‌پوینت شرطی را به‌جای کد، در خود ویرایشگر می‌گیرد؛ رایگان است و برای پایتون افزونه رسمی دارد. هر دو محلی کار می‌کنند و به شبکه نیازی ندارند.

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

کنار این

بالای این