Kamu menjalankan skrip. Ia keluar dengan kode 0 dan mencetak total. Totalnya salah.
Tidak ada yang melempar error dan tidak ada peringatan tercetak, jadi tidak ada nomor baris untuk memulai.
Poin Penting
- Jawaban salah tanpa traceback tidak punya baris untuk diikuti, jadi kamu menghentikan program di tempat nilai ditulis, bukan membaca pesan error.
breakpoint()memulaipdbdi dalam loop, danpmencetak nilai pada saat itu: hari yang sama datang dua kali dan tulisan kedua menggantikan yang pertama.totals[day] = totals.get(day, 0) + session["minutes"]menambah, bukan menimpa, dan itulah seluruh perbaikannya.- Breakpoint yang tertinggal di batch job menunggu
stdinselamanya, jadi checker gagal kalaubreakpoint()masih ada di file.
Angka salah tanpa traceback
Skrip ini menjumlahkan menit latihan per hari. Datanya tiga sesi, dan dua di antaranya jatuh pada hari yang sama.
[
{"date": "2026-11-03", "minutes": 20},
{"date": "2026-11-03", "minutes": 45},
{"date": "2026-11-04", "minutes": 30}
]
Skrip membaca file, membuat dict berisi menit per hari, lalu mencetak tiap hari diikuti total.
# totals.py
import json
import sys
from pathlib import Path
path = Path(sys.argv[1])
sessions = json.loads(path.read_text())
totals = {}
for session in sessions:
day = session["date"]
totals[day] = session["minutes"]
for day in sorted(totals):
print(f"{day} {totals[day]}")
print(f"total {sum(totals.values())}")
Ini hasil jalan yang sebenarnya, diukur di Python 3.14.9 pada Linux.
$ python3 totals.py sessions.json
2026-11-03 45
2026-11-04 30
total 75
Kode keluarnya 0. Sekarang periksa hitungannya dengan tangan. Pada 2026-11-03 kamu berlatih 20 dan 45 menit, jadi hari itu seharusnya 65. Tanggal 2026-11-04 adalah 30. Totalnya seharusnya 95, bukan 75.
Tidak ada yang melempar error karena loop menimpa di tempat yang seharusnya menambah. Baris totals[day] = session["minutes"] adalah Python yang sah. Baris itu menyimpan angka di bawah sebuah key, dan kalau key-nya sudah ada, nilai lama diganti. Tidak ada TypeError untuk dilempar dan tidak ada traceback untuk dicetak.
Hentikan di titik salahnya
Sebuah traceback menunjuk baris yang gagal. Di sini tidak ada baris yang gagal, jadi kamu menghentikan program di baris yang menulis nilai salah.
Taruh breakpoint() di dalam loop, pada baris sebelum assignment, dengan indentasi yang sama seperti baris di sekitarnya. Lalu jalankan skrip seperti biasa. Pemanggilan itu memulai pdb, dan program menunggu di prompt (Pdb).
Python 3.14 mencetak direktori kerja di depan nama file. Di sini dipotong menjadi totals.py supaya barisnya muat.
$ python3 totals.py sessions.json
> totals.py(12)<module>()
-> breakpoint()
(Pdb) p totals
{}
(Pdb) p session
{'date': '2026-11-03', 'minutes': 20}
(Pdb) n
> totals.py(13)<module>()
-> totals[day] = session["minutes"]
(Pdb) n
> totals.py(10)<module>()
-> for session in sessions:
(Pdb) p totals
{'2026-11-03': 20}
(Pdb) c
> totals.py(12)<module>()
-> breakpoint()
(Pdb) p session
{'date': '2026-11-03', 'minutes': 45}
(Pdb) p totals
{'2026-11-03': 20}
(Pdb) n
> totals.py(13)<module>()
-> totals[day] = session["minutes"]
(Pdb) n
> totals.py(10)<module>()
-> for session in sessions:
(Pdb) p totals
{'2026-11-03': 45}
(Pdb) q
Quitting pdb will kill the process. Quit anyway? [y/n] y
Kamu tidak perlu mengetik sesi persis seperti ini. Enam perintah yang penting, dan masing-masing menjawab satu pertanyaan:
p totalsmencetakreprdaritotals. Ia menjawab apa isidictsaat ini.p sessionmencetak item yang sedang dikerjakan loop.nmenjalankan baris saat ini dan berhenti di baris berikutnya.cmelanjutkan sampaibreakpoint()berikutnya, yang di sini berarti putaran loop berikutnya.lmenampilkan baris kode sumber di sekitar tempat kamu berhenti.qkeluar. Ia bertanyaQuitting pdb will kill the process. Quit anyway? [y/n], karena keluar berarti mengakhiri program.
Dua kali berhenti itu membuktikan penyebabnya. Pada berhenti pertama, session adalah entri 20 menit dan totals kosong. Pada berhenti kedua, session kembali ke 2026-11-03, kali ini dengan 45, dan totals sudah berisi 20 untuk hari itu. Setelah n menjalankan assignment, totals berisi 45. Hari yang sama datang dua kali, dan tulisan kedua mengganti 20 yang pertama dengan 45.
Satu baris yang menambah, bukan menimpa
Perbaikannya satu baris:
totals[day] = totals.get(day, 0) + session["minutes"]
totals.get(day, 0) memberi menit yang sudah tersimpan untuk hari itu, atau 0 kalau harinya baru. Hapus baris breakpoint() dan filenya jadi seperti ini.
# totals.py
import json
import sys
from pathlib import Path
path = Path(sys.argv[1])
sessions = json.loads(path.read_text())
totals = {}
for session in sessions:
day = session["date"]
totals[day] = totals.get(day, 0) + session["minutes"]
for day in sorted(totals):
print(f"{day} {totals[day]}")
print(f"total {sum(totals.values())}")
Kedua sesi pada 2026-11-03 sekarang terhitung, dan skrip keluar dengan kode 0.
$ python3 totals.py sessions.json
2026-11-03 65
2026-11-04 30
total 95
Checker
Hitungan tangan tadi bisa menjadi skrip yang kamu jalankan ulang. Checker ini menjalankan totals.py pada tiga sesi yang sama dan membandingkan keluarannya dengan angkamu.
# check_totals.py
import json
import subprocess
import sys
import tempfile
from pathlib import Path
SESSIONS = [
{"date": "2026-11-03", "minutes": 20},
{"date": "2026-11-03", "minutes": 45},
{"date": "2026-11-04", "minutes": 30},
]
EXPECTED = "2026-11-03 65\n2026-11-04 30\ntotal 95"
def report(results):
for label, ok, detail in results:
print(f"{'PASS' if ok else 'FAIL'} {label}" + (f" <- {detail}" if not ok and detail else ""))
passed = sum(1 for _, ok, _ in results if ok)
print(f"\n{passed} of {len(results)} checks passed")
return passed == len(results)
def main():
folder = Path(tempfile.mkdtemp())
data = folder / "sessions.json"
data.write_text(json.dumps(SESSIONS))
run = subprocess.run(
[sys.executable, "totals.py", str(data)],
capture_output=True, text=True, input="", timeout=20,
)
results = [("the script exits 0", run.returncode == 0, f"exit {run.returncode}")]
by_day = {}
for line in run.stdout.strip().splitlines()[:-1]:
day, minutes = line.split()
by_day[day] = int(minutes)
results.append(("2026-11-03 adds up both sessions", by_day.get("2026-11-03") == 65, str(by_day.get("2026-11-03"))))
results.append(("2026-11-04 keeps its single session", by_day.get("2026-11-04") == 30, str(by_day.get("2026-11-04"))))
results.append(("the report prints the totals it computed", run.stdout.strip() == EXPECTED, repr(run.stdout.strip())))
source = Path("totals.py").read_text()
results.append(("no breakpoint() left in the script", "breakpoint()" not in source, "remove it before shipping"))
return 0 if report(results) else 1
if __name__ == "__main__":
sys.exit(main())
Jalankan terhadap skrip awal dan 3 dari 5 pemeriksaan lolos. Kode keluarnya 1.
$ python3 check_totals.py
PASS the script exits 0
FAIL 2026-11-03 adds up both sessions <- 45
PASS 2026-11-04 keeps its single session
FAIL the report prints the totals it computed <- '2026-11-03 45\n2026-11-04 30\ntotal 75'
PASS no breakpoint() left in the script
3 of 5 checks passed
Dua baris FAIL itu adalah bug yang sama, terlihat dua kali. Jalankan terhadap skrip yang sudah diperbaiki dan semua 5 pemeriksaan lolos. Kode keluarnya 0.
$ python3 check_totals.py
PASS the script exits 0
PASS 2026-11-03 adds up both sessions
PASS 2026-11-04 keeps its single session
PASS the report prints the totals it computed
PASS no breakpoint() left in the script
5 of 5 checks passed
Pemeriksaan terakhir gagal kalau breakpoint() masih ada di totals.py. Breakpoint yang tertinggal di batch job menunggu stdin selamanya, jadi kamu ingin menemukannya sebelum dirilis.
Yang tidak bisa dijawab pdb
p menunjukkan nilai pada saat itu, bukan riwayatnya. Untuk tahu bagaimana sebuah nilai sampai di sana, kamu melangkah dengan n dan mencetak lagi, seperti di transkrip tadi.
Breakpoint juga menghentikan batch job. Program tidak selesai sampai seseorang menjawab di prompt.
PYTHONBREAKPOINT=0 mematikan breakpoint tanpa mengedit file. Pada skrip ini, ia hanya menyembunyikan debugger.
$ PYTHONBREAKPOINT=0 python3 totals.py sessions.json
2026-11-03 45
2026-11-04 30
total 75
Breakpoint dilewati, skrip keluar dengan kode 0, dan bug-nya tetap ada.
Kalau angka salah dan tidak ada yang melempar error, hentikan program di baris yang menulis nilainya, karena traceback tidak akan datang.