[{"data":1,"prerenderedAt":340},["ShallowReactive",2],{"tag-labels":3,"site-header":4,"article-python-debugging-id":72},{},{"featuredPost":5,"navigations":29},{"id":6,"title":7,"title_id":8,"slug":9,"excerpt":10,"excerpt_id":11,"has_translation":12,"published_at":13,"reading_time":14,"hidden_from_listing":15,"tags":16,"thumbnail_image":21,"og_image":22,"series":23,"authors":28},110,"When You Pick a Framework Just to Look Different","Ketika Pilih Framework Cuma Biar Kelihatan Beda","ketika-pilih-framework-cuma-biar-kelihatan-beda","The last time you chose a new framework or library, was it because your problem needed that tool, or because it sounded cool and few people around you were using it? That question is uncomfortable. Technology decisions are often made for reasons that never appear in any technical document.","Terakhir kali kamu milih framework atau library baru, itu karena masalahmu butuh alat itu, atau karena alatnya kedengaran keren dan belum banyak yang pakai? Pertanyaan ini nggak enak dijawab. Soalnya keputusan teknologi sering diambil dengan alasan yang nggak pernah ditulis di dokumen teknis mana pun.",true,"2026-10-06T15:25:21.000Z",5,false,[17,18,19,20],"karier","framework","opini","pengalaman",null,"\u002Fog\u002Fketika-pilih-framework-cuma-biar-kelihatan-beda.png",{"id":24,"title":25,"title_id":26,"slug":27},9,"Articles","Artikel","all",[],[30,46,67],{"collection":31,"item":32},"series",{"__typename":31,"title":25,"title_id":26,"slug":27,"children_series":33},[34,38,42],{"title":35,"title_id":36,"slug":37},"Artificial Intelligence","Kecerdasan Artifisial","kecerdasan-artifisial",{"title":39,"title_id":40,"slug":41},"Programming Fundamentals","Konsep Dasar Pemrograman","konsep-dasar-pemrograman",{"title":43,"title_id":44,"slug":45},"Algorithms and Data Structures","Algoritma dan Struktur Data","algoritma-struktur-data",{"collection":31,"item":47},{"__typename":31,"title":48,"title_id":48,"slug":49,"children_series":50},"Tutorial","tutorial",[51,55,59,63],{"title":52,"title_id":53,"slug":54},"Learning Python","Belajar Python","belajar-python",{"title":56,"title_id":57,"slug":58},"Learning Go","Belajar Golang","belajar-golang",{"title":60,"title_id":61,"slug":62},"Learning Java","Belajar Java","belajar-java",{"title":64,"title_id":65,"slug":66},"Learning Pascal","Belajar Pascal","belajar-pascal",{"collection":31,"item":68},{"__typename":31,"title":69,"title_id":69,"slug":70,"children_series":71},"Lab","lab",[],{"article":73,"renderedBody":221,"renderedTakeaway":222,"latestArticles":223,"toc":323},{"id":74,"title":75,"title_id":75,"slug":76,"excerpt":77,"excerpt_id":77,"has_translation":12,"published_at":78,"reading_time":79,"hidden_from_listing":15,"tags":80,"thumbnail_image":21,"og_image":83,"series":84,"authors":213,"seoTitle":21,"seoDescription":21,"body_locale":216,"available_locales":217,"body":219,"takeway":220,"prerequisite":21},144,"Debugging Python: skrip keluar dengan kode 0, tapi totalnya salah","python-debugging","Skrip penjumlah berjalan mulus, mencetak angka, dan angkanya salah. Tanpa traceback untuk diikuti, kamu menghentikan program di baris yang menulis nilai itu lalu membaca isi dict dari pdb.","2026-11-09T01:00:00.000Z",7,[81,82],"python","debugging","\u002Fog\u002Fpython-debugging.png",{"id":85,"title":86,"title_id":87,"slug":88,"description":89,"description_id":90,"articles":91,"children_series":182,"parent_series":183},18,"Working with Python Scripts","Menggunakan Script Python","python-pemrograman-dengan-script","Leave the interpreter and start writing real .py files, including how to take input and print output.","Keluar dari interpreter dan mulai nulis file .py sendiri, lengkap dengan cara mengambil input dan menampilkan output.",[92,113,129,144,160,175],{"id":93,"title":94,"title_id":95,"slug":96,"excerpt":97,"excerpt_id":98,"has_translation":12,"published_at":99,"reading_time":100,"hidden_from_listing":15,"tags":101,"thumbnail_image":21,"og_image":104,"series":105,"authors":106},90,"Writing and Running a Python Script","Menulis dan Menjalankan Script Python","menulis-dan-menjalankan-script-python","A Python script is a file holding Python code, and it usually carries the .py extension.","Script python merupakan file berisi kode dengan bahasa Python yang biasanya memiliki ekstensi .py","2021-03-21T14:49:24.000Z",4,[81,49,102,103],"tooling","cli","\u002Fog\u002Fmenulis-dan-menjalankan-script-python.png",{"id":85,"title":86,"title_id":87,"slug":88},[107],{"authors_id":108},{"id":109,"name":110,"slug":111,"role":112,"profile_picture":21},1,"Daffa Izzuddin","izzudd","Writer",{"id":114,"title":115,"title_id":116,"slug":117,"excerpt":118,"excerpt_id":119,"has_translation":12,"published_at":120,"reading_time":100,"hidden_from_listing":15,"tags":121,"thumbnail_image":21,"og_image":124,"series":125,"authors":126},91,"Input and Output Systems in Python 3","Sistem Input dan Output pada Python 3","sistem-input-dan-output-pada-python-3","A few functions, print() and input() among them, are the standard tools most I\u002FO work in Python is built on.","Beberapa fungsi seperti print() dan input(), merupakan fungsi standar yang banyak digunakan untuk melakukan I\u002FO.","2021-05-19T14:57:25.000Z",[81,122,123],"input-output","dasar","\u002Fog\u002Fsistem-input-dan-output-pada-python-3.png",{"id":85,"title":86,"title_id":87,"slug":88},[127],{"authors_id":128},{"id":109,"name":110,"slug":111,"role":112,"profile_picture":21},{"id":130,"title":131,"title_id":132,"slug":133,"excerpt":134,"excerpt_id":135,"has_translation":12,"published_at":136,"reading_time":79,"hidden_from_listing":15,"tags":137,"thumbnail_image":21,"og_image":139,"series":140,"authors":141},136,"Python Modules: the Import That Printed the Report","Modul Python: Import yang Mencetak Laporannya","python-modules","You pull one function out into its own file, import it, and the whole report runs again and eats your input. A module is a file, `import` runs it top to bottom, and one line decides which of the two jobs it is doing.","Kamu memindahkan satu fungsi ke file sendiri, meng-import-nya, dan seluruh laporan jalan lagi lalu memakan inputmu. Modul adalah file, `import` menjalankannya dari atas ke bawah, dan satu baris menentukan pekerjaan mana yang sedang dijalankan file itu.","2026-11-06T01:00:00.000Z",[81,138],"modules","\u002Fog\u002Fpython-modules.png",{"id":85,"title":86,"title_id":87,"slug":88},[142],{"authors_id":143},{"id":109,"name":110,"slug":111,"role":112,"profile_picture":21},{"id":145,"title":146,"title_id":147,"slug":148,"excerpt":149,"excerpt_id":150,"has_translation":12,"published_at":151,"reading_time":14,"hidden_from_listing":15,"tags":152,"thumbnail_image":21,"og_image":155,"series":156,"authors":157},138,"Python venv: the Install That Went Into the Wrong Python","venv Python: Instalasi yang Masuk ke Python yang Salah","python-venv-pip","`pip install` printed Successfully installed, and the next run raised ModuleNotFoundError on the same machine. Two interpreters, one install, and the variable your script checks is the one the interpreter never sets.","`pip install` mencetak Successfully installed, lalu jalan berikutnya melempar ModuleNotFoundError di mesin yang sama. Dua interpreter, satu instalasi, dan variabel yang skripmu periksa justru tidak pernah dipasang oleh interpreter.","2026-11-07T12:00:00.000Z",[81,153,154],"venv","pip","\u002Fog\u002Fpython-venv-pip.png",{"id":85,"title":86,"title_id":87,"slug":88},[158],{"authors_id":159},{"id":109,"name":110,"slug":111,"role":112,"profile_picture":21},{"id":161,"title":162,"title_id":163,"slug":164,"excerpt":165,"excerpt_id":166,"has_translation":12,"published_at":167,"reading_time":79,"hidden_from_listing":15,"tags":168,"thumbnail_image":21,"og_image":170,"series":171,"authors":172},143,"Python stdlib: the datetime json.dumps Refused to Write","Python stdlib: datetime yang Ditolak json.dumps","python-stdlib","A script that totals minutes per day dies on the line that writes the report. Four stdlib modules carry it, and one TypeError stands in the way.","Skrip yang menjumlahkan menit per hari mati tepat di baris yang menulis laporan. Empat modul stdlib membawanya, dan satu TypeError menghadang.","2026-11-08T01:00:00.000Z",[81,169,103],"stdlib","\u002Fog\u002Fpython-stdlib.png",{"id":85,"title":86,"title_id":87,"slug":88},[173],{"authors_id":174},{"id":109,"name":110,"slug":111,"role":112,"profile_picture":21},{"id":74,"title":176,"title_id":75,"slug":76,"excerpt":177,"excerpt_id":77,"has_translation":12,"published_at":78,"reading_time":79,"hidden_from_listing":15,"tags":80,"thumbnail_image":21,"og_image":83,"series":178,"authors":179},"Python debugging: the script that exits 0 with the wrong total","A totals script runs clean, prints a number, and the number is wrong. With no traceback to follow, you stop the program at the line that writes the value and read the dict from pdb.",{"id":85,"title":86,"title_id":87,"slug":88},[180],{"authors_id":181},{"id":109,"name":110,"slug":111,"role":112,"profile_picture":21},[],{"id":184,"title":52,"title_id":53,"slug":54,"parent_series":185,"children_series":186},10,{"id":109,"title":48,"title_id":48,"slug":49},[187,192,197,198,203,208],{"id":188,"title":189,"title_id":190,"slug":191,"cover_image":21},16,"Getting Started with Python","Berkenalan dengan Bahasa Python","python-intro",{"id":193,"title":194,"title_id":195,"slug":196,"cover_image":21},17,"Variables, Types, and Operations","Variabel, Tipe Data, dan Operasi","python-variabel-tipe-data",{"id":85,"title":86,"title_id":87,"slug":88,"cover_image":21},{"id":199,"title":200,"title_id":201,"slug":202,"cover_image":21},19,"Control Structures","Struktur Kontrol","python-struktur-kontrol",{"id":204,"title":205,"title_id":206,"slug":207,"cover_image":21},20,"Functional Programming with Python","Pemrograman Fungsional dengan Python","python-pemrograman-fungsional",{"id":209,"title":210,"title_id":211,"slug":212,"cover_image":21},30,"Collections and Strings","Koleksi dan String","python-collections",[214],{"authors_id":215},{"id":109,"name":110,"slug":111,"role":112,"profile_picture":21},"id",[218,216],"en","Kamu menjalankan skrip. Ia keluar dengan kode 0 dan mencetak total. Totalnya salah.\n\nTidak ada yang melempar error dan tidak ada peringatan tercetak, jadi tidak ada nomor baris untuk memulai.\n\n## Angka salah tanpa traceback\n\nSkrip ini menjumlahkan menit latihan per hari. Datanya tiga sesi, dan dua di antaranya jatuh pada hari yang sama.\n\n```json\n[\n  {\"date\": \"2026-11-03\", \"minutes\": 20},\n  {\"date\": \"2026-11-03\", \"minutes\": 45},\n  {\"date\": \"2026-11-04\", \"minutes\": 30}\n]\n```\n\nSkrip membaca file, membuat `dict` berisi menit per hari, lalu mencetak tiap hari diikuti total.\n\n```python\n# totals.py\nimport json\nimport sys\nfrom pathlib import Path\n\npath = Path(sys.argv[1])\nsessions = json.loads(path.read_text())\n\ntotals = {}\nfor session in sessions:\n    day = session[\"date\"]\n    totals[day] = session[\"minutes\"]\n\nfor day in sorted(totals):\n    print(f\"{day}  {totals[day]}\")\nprint(f\"total {sum(totals.values())}\")\n```\n\nIni hasil jalan yang sebenarnya, diukur di Python 3.14.9 pada Linux.\n\n```text\n$ python3 totals.py sessions.json\n2026-11-03  45\n2026-11-04  30\ntotal 75\n```\n\nKode 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.\n\nTidak 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.\n\n## Hentikan di titik salahnya\n\nSebuah `traceback` menunjuk baris yang gagal. Di sini tidak ada baris yang gagal, jadi kamu menghentikan program di baris yang menulis nilai salah.\n\nTaruh `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)`.\n\nPython 3.14 mencetak direktori kerja di depan nama file. Di sini dipotong menjadi `totals.py` supaya barisnya muat.\n\n```text\n$ python3 totals.py sessions.json\n> totals.py(12)\u003Cmodule>()\n-> breakpoint()\n(Pdb) p totals\n{}\n(Pdb) p session\n{'date': '2026-11-03', 'minutes': 20}\n(Pdb) n\n> totals.py(13)\u003Cmodule>()\n-> totals[day] = session[\"minutes\"]\n(Pdb) n\n> totals.py(10)\u003Cmodule>()\n-> for session in sessions:\n(Pdb) p totals\n{'2026-11-03': 20}\n(Pdb) c\n> totals.py(12)\u003Cmodule>()\n-> breakpoint()\n(Pdb) p session\n{'date': '2026-11-03', 'minutes': 45}\n(Pdb) p totals\n{'2026-11-03': 20}\n(Pdb) n\n> totals.py(13)\u003Cmodule>()\n-> totals[day] = session[\"minutes\"]\n(Pdb) n\n> totals.py(10)\u003Cmodule>()\n-> for session in sessions:\n(Pdb) p totals\n{'2026-11-03': 45}\n(Pdb) q\nQuitting pdb will kill the process. Quit anyway? [y\u002Fn] y\n```\n\nKamu tidak perlu mengetik sesi persis seperti ini. Enam perintah yang penting, dan masing-masing menjawab satu pertanyaan:\n\n- `p totals` mencetak `repr` dari `totals`. Ia menjawab apa isi `dict` saat ini.\n- `p session` mencetak item yang sedang dikerjakan loop.\n- `n` menjalankan baris saat ini dan berhenti di baris berikutnya.\n- `c` melanjutkan sampai `breakpoint()` berikutnya, yang di sini berarti putaran loop berikutnya.\n- `l` menampilkan baris kode sumber di sekitar tempat kamu berhenti.\n- `q` keluar. Ia bertanya `Quitting pdb will kill the process. Quit anyway? [y\u002Fn]`, karena keluar berarti mengakhiri program.\n\nDua 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.\n\n## Satu baris yang menambah, bukan menimpa\n\nPerbaikannya satu baris:\n\n`totals[day] = totals.get(day, 0) + session[\"minutes\"]`\n\n`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.\n\n```python\n# totals.py\nimport json\nimport sys\nfrom pathlib import Path\n\npath = Path(sys.argv[1])\nsessions = json.loads(path.read_text())\n\ntotals = {}\nfor session in sessions:\n    day = session[\"date\"]\n    totals[day] = totals.get(day, 0) + session[\"minutes\"]\n\nfor day in sorted(totals):\n    print(f\"{day}  {totals[day]}\")\nprint(f\"total {sum(totals.values())}\")\n```\n\nKedua sesi pada 2026-11-03 sekarang terhitung, dan skrip keluar dengan kode 0.\n\n```text\n$ python3 totals.py sessions.json\n2026-11-03  65\n2026-11-04  30\ntotal 95\n```\n\n## Checker\n\nHitungan tangan tadi bisa menjadi skrip yang kamu jalankan ulang. Checker ini menjalankan `totals.py` pada tiga sesi yang sama dan membandingkan keluarannya dengan angkamu.\n\n```python\n# check_totals.py\nimport json\nimport subprocess\nimport sys\nimport tempfile\nfrom pathlib import Path\n\nSESSIONS = [\n    {\"date\": \"2026-11-03\", \"minutes\": 20},\n    {\"date\": \"2026-11-03\", \"minutes\": 45},\n    {\"date\": \"2026-11-04\", \"minutes\": 30},\n]\nEXPECTED = \"2026-11-03  65\\n2026-11-04  30\\ntotal 95\"\n\n\ndef report(results):\n    for label, ok, detail in results:\n        print(f\"{'PASS' if ok else 'FAIL'}  {label}\" + (f\"  \u003C- {detail}\" if not ok and detail else \"\"))\n    passed = sum(1 for _, ok, _ in results if ok)\n    print(f\"\\n{passed} of {len(results)} checks passed\")\n    return passed == len(results)\n\n\ndef main():\n    folder = Path(tempfile.mkdtemp())\n    data = folder \u002F \"sessions.json\"\n    data.write_text(json.dumps(SESSIONS))\n    run = subprocess.run(\n        [sys.executable, \"totals.py\", str(data)],\n        capture_output=True, text=True, input=\"\", timeout=20,\n    )\n    results = [(\"the script exits 0\", run.returncode == 0, f\"exit {run.returncode}\")]\n    by_day = {}\n    for line in run.stdout.strip().splitlines()[:-1]:\n        day, minutes = line.split()\n        by_day[day] = int(minutes)\n    results.append((\"2026-11-03 adds up both sessions\", by_day.get(\"2026-11-03\") == 65, str(by_day.get(\"2026-11-03\"))))\n    results.append((\"2026-11-04 keeps its single session\", by_day.get(\"2026-11-04\") == 30, str(by_day.get(\"2026-11-04\"))))\n    results.append((\"the report prints the totals it computed\", run.stdout.strip() == EXPECTED, repr(run.stdout.strip())))\n    source = Path(\"totals.py\").read_text()\n    results.append((\"no breakpoint() left in the script\", \"breakpoint()\" not in source, \"remove it before shipping\"))\n    return 0 if report(results) else 1\n\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n```\n\nJalankan terhadap skrip awal dan 3 dari 5 pemeriksaan lolos. Kode keluarnya 1.\n\n```text\n$ python3 check_totals.py\nPASS  the script exits 0\nFAIL  2026-11-03 adds up both sessions  \u003C- 45\nPASS  2026-11-04 keeps its single session\nFAIL  the report prints the totals it computed  \u003C- '2026-11-03  45\\n2026-11-04  30\\ntotal 75'\nPASS  no breakpoint() left in the script\n\n3 of 5 checks passed\n```\n\nDua baris FAIL itu adalah bug yang sama, terlihat dua kali. Jalankan terhadap skrip yang sudah diperbaiki dan semua 5 pemeriksaan lolos. Kode keluarnya 0.\n\n```text\n$ python3 check_totals.py\nPASS  the script exits 0\nPASS  2026-11-03 adds up both sessions\nPASS  2026-11-04 keeps its single session\nPASS  the report prints the totals it computed\nPASS  no breakpoint() left in the script\n\n5 of 5 checks passed\n```\n\nPemeriksaan terakhir gagal kalau `breakpoint()` masih ada di `totals.py`. Breakpoint yang tertinggal di batch job menunggu `stdin` selamanya, jadi kamu ingin menemukannya sebelum dirilis.\n\n## Yang tidak bisa dijawab pdb\n\n`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.\n\nBreakpoint juga menghentikan batch job. Program tidak selesai sampai seseorang menjawab di prompt.\n\n`PYTHONBREAKPOINT=0` mematikan breakpoint tanpa mengedit file. Pada skrip ini, ia hanya menyembunyikan debugger.\n\n```text\n$ PYTHONBREAKPOINT=0 python3 totals.py sessions.json\n2026-11-03  45\n2026-11-04  30\ntotal 75\n```\n\nBreakpoint dilewati, skrip keluar dengan kode 0, dan bug-nya tetap ada.\n\nKalau angka salah dan tidak ada yang melempar error, hentikan program di baris yang menulis nilainya, karena traceback tidak akan datang.\n","- Jawaban salah tanpa traceback tidak punya baris untuk diikuti, jadi kamu menghentikan program di tempat nilai ditulis, bukan membaca pesan error.\n- `breakpoint()` memulai `pdb` di dalam loop, dan `p` mencetak nilai pada saat itu: hari yang sama datang dua kali dan tulisan kedua menggantikan yang pertama.\n- `totals[day] = totals.get(day, 0) + session[\"minutes\"]` menambah, bukan menimpa, dan itulah seluruh perbaikannya.\n- Breakpoint yang tertinggal di batch job menunggu `stdin` selamanya, jadi checker gagal kalau `breakpoint()` masih ada di file.","\u003Cp>Kamu menjalankan skrip. Ia keluar dengan kode 0 dan mencetak total. Totalnya salah.\u003C\u002Fp>\n\u003Cp>Tidak ada yang melempar error dan tidak ada peringatan tercetak, jadi tidak ada nomor baris untuk memulai.\u003C\u002Fp>\n\u003Ch2 id=\"angka-salah-tanpa-traceback\">Angka salah tanpa traceback\u003C\u002Fh2>\n\u003Cp>Skrip ini menjumlahkan menit latihan per hari. Datanya tiga sesi, dan dua di antaranya jatuh pada hari yang sama.\u003C\u002Fp>\n\n\u003Cdiv class=\"code-block-wrapper my-6 not-prose rounded-xl border-2 border-black dark:border-gray-600 shadow-neo overflow-hidden bg-[#24292e] text-[#e1e4e8] transition-all\">\n  \u003Cdiv class=\"code-block-header flex items-center justify-between px-4 py-2.5 bg-[#1f2428] border-b-2 border-black dark:border-gray-600 font-mono text-xs font-bold text-gray-300 select-none\">\n    \u003Cdiv class=\"flex items-center gap-2\">\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ff5f56] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ffbd2e] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#27c93f] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"ml-2 font-mono font-bold text-xs uppercase tracking-wider text-gray-300\">JSON\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n    \u003Cbutton type=\"button\" class=\"copy-code-btn flex items-center gap-1.5 px-3 py-1 text-xs font-bold bg-[#2f363d] text-gray-200 border-2 border-black dark:border-gray-500 rounded-lg shadow-neo-sm hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none hover:bg-gray-700 transition-all cursor-pointer\" data-code=\"%5B%0A%20%20%7B%22date%22%3A%20%222026-11-03%22%2C%20%22minutes%22%3A%2020%7D%2C%0A%20%20%7B%22date%22%3A%20%222026-11-03%22%2C%20%22minutes%22%3A%2045%7D%2C%0A%20%20%7B%22date%22%3A%20%222026-11-04%22%2C%20%22minutes%22%3A%2030%7D%0A%5D\" title=\"Salin\">\n      \u003Csvg class=\"w-3.5 h-3.5 copy-icon-svg\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n        \u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\">\u003C\u002Fpath>\n      \u003C\u002Fsvg>\n      \u003Cspan class=\"copy-text\">Salin\u003C\u002Fspan>\n    \u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"code-block-content p-4 overflow-x-auto text-sm font-mono leading-relaxed bg-[#24292e]\">\n    \u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">[\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  {\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">\"date\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"2026-11-03\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">\"minutes\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">20\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">},\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  {\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">\"date\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"2026-11-03\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">\"minutes\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">45\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">},\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  {\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">\"date\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"2026-11-04\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">\"minutes\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">30\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">]\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\u003Cp>Skrip membaca file, membuat \u003Ccode>dict\u003C\u002Fcode> berisi menit per hari, lalu mencetak tiap hari diikuti total.\u003C\u002Fp>\n\n\u003Cdiv class=\"code-block-wrapper my-6 not-prose rounded-xl border-2 border-black dark:border-gray-600 shadow-neo overflow-hidden bg-[#24292e] text-[#e1e4e8] transition-all\">\n  \u003Cdiv class=\"code-block-header flex items-center justify-between px-4 py-2.5 bg-[#1f2428] border-b-2 border-black dark:border-gray-600 font-mono text-xs font-bold text-gray-300 select-none\">\n    \u003Cdiv class=\"flex items-center gap-2\">\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ff5f56] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ffbd2e] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#27c93f] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"ml-2 font-mono font-bold text-xs uppercase tracking-wider text-gray-300\">PYTHON\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n    \u003Cbutton type=\"button\" class=\"copy-code-btn flex items-center gap-1.5 px-3 py-1 text-xs font-bold bg-[#2f363d] text-gray-200 border-2 border-black dark:border-gray-500 rounded-lg shadow-neo-sm hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none hover:bg-gray-700 transition-all cursor-pointer\" data-code=\"%23%20totals.py%0Aimport%20json%0Aimport%20sys%0Afrom%20pathlib%20import%20Path%0A%0Apath%20%3D%20Path(sys.argv%5B1%5D)%0Asessions%20%3D%20json.loads(path.read_text())%0A%0Atotals%20%3D%20%7B%7D%0Afor%20session%20in%20sessions%3A%0A%20%20%20%20day%20%3D%20session%5B%22date%22%5D%0A%20%20%20%20totals%5Bday%5D%20%3D%20session%5B%22minutes%22%5D%0A%0Afor%20day%20in%20sorted(totals)%3A%0A%20%20%20%20print(f%22%7Bday%7D%20%20%7Btotals%5Bday%5D%7D%22)%0Aprint(f%22total%20%7Bsum(totals.values())%7D%22)\" title=\"Salin\">\n      \u003Csvg class=\"w-3.5 h-3.5 copy-icon-svg\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n        \u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\">\u003C\u002Fpath>\n      \u003C\u002Fsvg>\n      \u003Cspan class=\"copy-text\">Salin\u003C\u002Fspan>\n    \u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"code-block-content p-4 overflow-x-auto text-sm font-mono leading-relaxed bg-[#24292e]\">\n    \u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># totals.py\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">import\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> json\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">import\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> sys\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">from\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> pathlib \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">import\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> Path\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">path \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> Path(sys.argv[\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">1\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">])\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">sessions \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> json.loads(path.read_text())\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">totals \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> {}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">for\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> session \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">in\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> sessions:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    day \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> session[\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"date\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    totals[day] \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> session[\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"minutes\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">for\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> day \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">in\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\"> sorted\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(totals):\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">    print\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">f\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">{\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">day\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">}\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">  {\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">totals[day]\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">}\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">print\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">f\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"total \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">{sum\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(totals.values())\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">}\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">)\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\u003Cp>Ini hasil jalan yang sebenarnya, diukur di Python 3.14.9 pada Linux.\u003C\u002Fp>\n\n\u003Cdiv class=\"code-block-wrapper my-6 not-prose rounded-xl border-2 border-black dark:border-gray-600 shadow-neo overflow-hidden bg-[#24292e] text-[#e1e4e8] transition-all\">\n  \u003Cdiv class=\"code-block-header flex items-center justify-between px-4 py-2.5 bg-[#1f2428] border-b-2 border-black dark:border-gray-600 font-mono text-xs font-bold text-gray-300 select-none\">\n    \u003Cdiv class=\"flex items-center gap-2\">\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ff5f56] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ffbd2e] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#27c93f] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"ml-2 font-mono font-bold text-xs uppercase tracking-wider text-gray-300\">TEXT\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n    \u003Cbutton type=\"button\" class=\"copy-code-btn flex items-center gap-1.5 px-3 py-1 text-xs font-bold bg-[#2f363d] text-gray-200 border-2 border-black dark:border-gray-500 rounded-lg shadow-neo-sm hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none hover:bg-gray-700 transition-all cursor-pointer\" data-code=\"%24%20python3%20totals.py%20sessions.json%0A2026-11-03%20%2045%0A2026-11-04%20%2030%0Atotal%2075\" title=\"Salin\">\n      \u003Csvg class=\"w-3.5 h-3.5 copy-icon-svg\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n        \u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\">\u003C\u002Fpath>\n      \u003C\u002Fsvg>\n      \u003Cspan class=\"copy-text\">Salin\u003C\u002Fspan>\n    \u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"code-block-content p-4 overflow-x-auto text-sm font-mono leading-relaxed bg-[#24292e]\">\n    \u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\">\u003Ccode>$ python3 totals.py sessions.json\n2026-11-03  45\n2026-11-04  30\ntotal 75\u003C\u002Fcode>\u003C\u002Fpre>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\u003Cp>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.\u003C\u002Fp>\n\u003Cp>Tidak ada yang melempar error karena loop menimpa di tempat yang seharusnya menambah. Baris \u003Ccode>totals[day] = session[&quot;minutes&quot;]\u003C\u002Fcode> adalah Python yang sah. Baris itu menyimpan angka di bawah sebuah key, dan kalau key-nya sudah ada, nilai lama diganti. Tidak ada \u003Ccode>TypeError\u003C\u002Fcode> untuk dilempar dan tidak ada \u003Ccode>traceback\u003C\u002Fcode> untuk dicetak.\u003C\u002Fp>\n\u003Ch2 id=\"hentikan-di-titik-salahnya\">Hentikan di titik salahnya\u003C\u002Fh2>\n\u003Cp>Sebuah \u003Ccode>traceback\u003C\u002Fcode> menunjuk baris yang gagal. Di sini tidak ada baris yang gagal, jadi kamu menghentikan program di baris yang menulis nilai salah.\u003C\u002Fp>\n\u003Cp>Taruh \u003Ccode>breakpoint()\u003C\u002Fcode> di dalam loop, pada baris sebelum assignment, dengan indentasi yang sama seperti baris di sekitarnya. Lalu jalankan skrip seperti biasa. Pemanggilan itu memulai \u003Ccode>pdb\u003C\u002Fcode>, dan program menunggu di prompt \u003Ccode>(Pdb)\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Cp>Python 3.14 mencetak direktori kerja di depan nama file. Di sini dipotong menjadi \u003Ccode>totals.py\u003C\u002Fcode> supaya barisnya muat.\u003C\u002Fp>\n\n\u003Cdiv class=\"code-block-wrapper my-6 not-prose rounded-xl border-2 border-black dark:border-gray-600 shadow-neo overflow-hidden bg-[#24292e] text-[#e1e4e8] transition-all\">\n  \u003Cdiv class=\"code-block-header flex items-center justify-between px-4 py-2.5 bg-[#1f2428] border-b-2 border-black dark:border-gray-600 font-mono text-xs font-bold text-gray-300 select-none\">\n    \u003Cdiv class=\"flex items-center gap-2\">\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ff5f56] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ffbd2e] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#27c93f] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"ml-2 font-mono font-bold text-xs uppercase tracking-wider text-gray-300\">TEXT\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n    \u003Cbutton type=\"button\" class=\"copy-code-btn flex items-center gap-1.5 px-3 py-1 text-xs font-bold bg-[#2f363d] text-gray-200 border-2 border-black dark:border-gray-500 rounded-lg shadow-neo-sm hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none hover:bg-gray-700 transition-all cursor-pointer\" data-code=\"%24%20python3%20totals.py%20sessions.json%0A%3E%20totals.py(12)%3Cmodule%3E()%0A-%3E%20breakpoint()%0A(Pdb)%20p%20totals%0A%7B%7D%0A(Pdb)%20p%20session%0A%7B'date'%3A%20'2026-11-03'%2C%20'minutes'%3A%2020%7D%0A(Pdb)%20n%0A%3E%20totals.py(13)%3Cmodule%3E()%0A-%3E%20totals%5Bday%5D%20%3D%20session%5B%22minutes%22%5D%0A(Pdb)%20n%0A%3E%20totals.py(10)%3Cmodule%3E()%0A-%3E%20for%20session%20in%20sessions%3A%0A(Pdb)%20p%20totals%0A%7B'2026-11-03'%3A%2020%7D%0A(Pdb)%20c%0A%3E%20totals.py(12)%3Cmodule%3E()%0A-%3E%20breakpoint()%0A(Pdb)%20p%20session%0A%7B'date'%3A%20'2026-11-03'%2C%20'minutes'%3A%2045%7D%0A(Pdb)%20p%20totals%0A%7B'2026-11-03'%3A%2020%7D%0A(Pdb)%20n%0A%3E%20totals.py(13)%3Cmodule%3E()%0A-%3E%20totals%5Bday%5D%20%3D%20session%5B%22minutes%22%5D%0A(Pdb)%20n%0A%3E%20totals.py(10)%3Cmodule%3E()%0A-%3E%20for%20session%20in%20sessions%3A%0A(Pdb)%20p%20totals%0A%7B'2026-11-03'%3A%2045%7D%0A(Pdb)%20q%0AQuitting%20pdb%20will%20kill%20the%20process.%20Quit%20anyway%3F%20%5By%2Fn%5D%20y\" title=\"Salin\">\n      \u003Csvg class=\"w-3.5 h-3.5 copy-icon-svg\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n        \u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\">\u003C\u002Fpath>\n      \u003C\u002Fsvg>\n      \u003Cspan class=\"copy-text\">Salin\u003C\u002Fspan>\n    \u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"code-block-content p-4 overflow-x-auto text-sm font-mono leading-relaxed bg-[#24292e]\">\n    \u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\">\u003Ccode>$ python3 totals.py sessions.json\n&gt; totals.py(12)&lt;module&gt;()\n-&gt; breakpoint()\n(Pdb) p totals\n{}\n(Pdb) p session\n{'date': '2026-11-03', 'minutes': 20}\n(Pdb) n\n&gt; totals.py(13)&lt;module&gt;()\n-&gt; totals[day] = session[\"minutes\"]\n(Pdb) n\n&gt; totals.py(10)&lt;module&gt;()\n-&gt; for session in sessions:\n(Pdb) p totals\n{'2026-11-03': 20}\n(Pdb) c\n&gt; totals.py(12)&lt;module&gt;()\n-&gt; breakpoint()\n(Pdb) p session\n{'date': '2026-11-03', 'minutes': 45}\n(Pdb) p totals\n{'2026-11-03': 20}\n(Pdb) n\n&gt; totals.py(13)&lt;module&gt;()\n-&gt; totals[day] = session[\"minutes\"]\n(Pdb) n\n&gt; totals.py(10)&lt;module&gt;()\n-&gt; for session in sessions:\n(Pdb) p totals\n{'2026-11-03': 45}\n(Pdb) q\nQuitting pdb will kill the process. Quit anyway? [y\u002Fn] y\u003C\u002Fcode>\u003C\u002Fpre>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\u003Cp>Kamu tidak perlu mengetik sesi persis seperti ini. Enam perintah yang penting, dan masing-masing menjawab satu pertanyaan:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Ccode>p totals\u003C\u002Fcode> mencetak \u003Ccode>repr\u003C\u002Fcode> dari \u003Ccode>totals\u003C\u002Fcode>. Ia menjawab apa isi \u003Ccode>dict\u003C\u002Fcode> saat ini.\u003C\u002Fli>\n\u003Cli>\u003Ccode>p session\u003C\u002Fcode> mencetak item yang sedang dikerjakan loop.\u003C\u002Fli>\n\u003Cli>\u003Ccode>n\u003C\u002Fcode> menjalankan baris saat ini dan berhenti di baris berikutnya.\u003C\u002Fli>\n\u003Cli>\u003Ccode>c\u003C\u002Fcode> melanjutkan sampai \u003Ccode>breakpoint()\u003C\u002Fcode> berikutnya, yang di sini berarti putaran loop berikutnya.\u003C\u002Fli>\n\u003Cli>\u003Ccode>l\u003C\u002Fcode> menampilkan baris kode sumber di sekitar tempat kamu berhenti.\u003C\u002Fli>\n\u003Cli>\u003Ccode>q\u003C\u002Fcode> keluar. Ia bertanya \u003Ccode>Quitting pdb will kill the process. Quit anyway? [y\u002Fn]\u003C\u002Fcode>, karena keluar berarti mengakhiri program.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Dua kali berhenti itu membuktikan penyebabnya. Pada berhenti pertama, \u003Ccode>session\u003C\u002Fcode> adalah entri 20 menit dan \u003Ccode>totals\u003C\u002Fcode> kosong. Pada berhenti kedua, \u003Ccode>session\u003C\u002Fcode> kembali ke 2026-11-03, kali ini dengan 45, dan \u003Ccode>totals\u003C\u002Fcode> sudah berisi 20 untuk hari itu. Setelah \u003Ccode>n\u003C\u002Fcode> menjalankan assignment, \u003Ccode>totals\u003C\u002Fcode> berisi 45. Hari yang sama datang dua kali, dan tulisan kedua mengganti 20 yang pertama dengan 45.\u003C\u002Fp>\n\u003Ch2 id=\"satu-baris-yang-menambah-bukan-menimpa\">Satu baris yang menambah, bukan menimpa\u003C\u002Fh2>\n\u003Cp>Perbaikannya satu baris:\u003C\u002Fp>\n\u003Cp>\u003Ccode>totals[day] = totals.get(day, 0) + session[&quot;minutes&quot;]\u003C\u002Fcode>\u003C\u002Fp>\n\u003Cp>\u003Ccode>totals.get(day, 0)\u003C\u002Fcode> memberi menit yang sudah tersimpan untuk hari itu, atau 0 kalau harinya baru. Hapus baris \u003Ccode>breakpoint()\u003C\u002Fcode> dan filenya jadi seperti ini.\u003C\u002Fp>\n\n\u003Cdiv class=\"code-block-wrapper my-6 not-prose rounded-xl border-2 border-black dark:border-gray-600 shadow-neo overflow-hidden bg-[#24292e] text-[#e1e4e8] transition-all\">\n  \u003Cdiv class=\"code-block-header flex items-center justify-between px-4 py-2.5 bg-[#1f2428] border-b-2 border-black dark:border-gray-600 font-mono text-xs font-bold text-gray-300 select-none\">\n    \u003Cdiv class=\"flex items-center gap-2\">\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ff5f56] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ffbd2e] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#27c93f] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"ml-2 font-mono font-bold text-xs uppercase tracking-wider text-gray-300\">PYTHON\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n    \u003Cbutton type=\"button\" class=\"copy-code-btn flex items-center gap-1.5 px-3 py-1 text-xs font-bold bg-[#2f363d] text-gray-200 border-2 border-black dark:border-gray-500 rounded-lg shadow-neo-sm hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none hover:bg-gray-700 transition-all cursor-pointer\" data-code=\"%23%20totals.py%0Aimport%20json%0Aimport%20sys%0Afrom%20pathlib%20import%20Path%0A%0Apath%20%3D%20Path(sys.argv%5B1%5D)%0Asessions%20%3D%20json.loads(path.read_text())%0A%0Atotals%20%3D%20%7B%7D%0Afor%20session%20in%20sessions%3A%0A%20%20%20%20day%20%3D%20session%5B%22date%22%5D%0A%20%20%20%20totals%5Bday%5D%20%3D%20totals.get(day%2C%200)%20%2B%20session%5B%22minutes%22%5D%0A%0Afor%20day%20in%20sorted(totals)%3A%0A%20%20%20%20print(f%22%7Bday%7D%20%20%7Btotals%5Bday%5D%7D%22)%0Aprint(f%22total%20%7Bsum(totals.values())%7D%22)\" title=\"Salin\">\n      \u003Csvg class=\"w-3.5 h-3.5 copy-icon-svg\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n        \u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\">\u003C\u002Fpath>\n      \u003C\u002Fsvg>\n      \u003Cspan class=\"copy-text\">Salin\u003C\u002Fspan>\n    \u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"code-block-content p-4 overflow-x-auto text-sm font-mono leading-relaxed bg-[#24292e]\">\n    \u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># totals.py\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">import\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> json\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">import\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> sys\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">from\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> pathlib \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">import\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> Path\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">path \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> Path(sys.argv[\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">1\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">])\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">sessions \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> json.loads(path.read_text())\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">totals \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> {}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">for\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> session \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">in\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> sessions:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    day \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> session[\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"date\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    totals[day] \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> totals.get(day, \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">0\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">) \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">+\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> session[\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"minutes\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">for\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> day \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">in\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\"> sorted\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(totals):\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">    print\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">f\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">{\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">day\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">}\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">  {\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">totals[day]\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">}\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">print\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">f\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"total \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">{sum\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(totals.values())\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">}\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">)\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\u003Cp>Kedua sesi pada 2026-11-03 sekarang terhitung, dan skrip keluar dengan kode 0.\u003C\u002Fp>\n\n\u003Cdiv class=\"code-block-wrapper my-6 not-prose rounded-xl border-2 border-black dark:border-gray-600 shadow-neo overflow-hidden bg-[#24292e] text-[#e1e4e8] transition-all\">\n  \u003Cdiv class=\"code-block-header flex items-center justify-between px-4 py-2.5 bg-[#1f2428] border-b-2 border-black dark:border-gray-600 font-mono text-xs font-bold text-gray-300 select-none\">\n    \u003Cdiv class=\"flex items-center gap-2\">\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ff5f56] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ffbd2e] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#27c93f] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"ml-2 font-mono font-bold text-xs uppercase tracking-wider text-gray-300\">TEXT\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n    \u003Cbutton type=\"button\" class=\"copy-code-btn flex items-center gap-1.5 px-3 py-1 text-xs font-bold bg-[#2f363d] text-gray-200 border-2 border-black dark:border-gray-500 rounded-lg shadow-neo-sm hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none hover:bg-gray-700 transition-all cursor-pointer\" data-code=\"%24%20python3%20totals.py%20sessions.json%0A2026-11-03%20%2065%0A2026-11-04%20%2030%0Atotal%2095\" title=\"Salin\">\n      \u003Csvg class=\"w-3.5 h-3.5 copy-icon-svg\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n        \u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\">\u003C\u002Fpath>\n      \u003C\u002Fsvg>\n      \u003Cspan class=\"copy-text\">Salin\u003C\u002Fspan>\n    \u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"code-block-content p-4 overflow-x-auto text-sm font-mono leading-relaxed bg-[#24292e]\">\n    \u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\">\u003Ccode>$ python3 totals.py sessions.json\n2026-11-03  65\n2026-11-04  30\ntotal 95\u003C\u002Fcode>\u003C\u002Fpre>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\u003Ch2 id=\"checker\">Checker\u003C\u002Fh2>\n\u003Cp>Hitungan tangan tadi bisa menjadi skrip yang kamu jalankan ulang. Checker ini menjalankan \u003Ccode>totals.py\u003C\u002Fcode> pada tiga sesi yang sama dan membandingkan keluarannya dengan angkamu.\u003C\u002Fp>\n\n\u003Cdiv class=\"code-block-wrapper my-6 not-prose rounded-xl border-2 border-black dark:border-gray-600 shadow-neo overflow-hidden bg-[#24292e] text-[#e1e4e8] transition-all\">\n  \u003Cdiv class=\"code-block-header flex items-center justify-between px-4 py-2.5 bg-[#1f2428] border-b-2 border-black dark:border-gray-600 font-mono text-xs font-bold text-gray-300 select-none\">\n    \u003Cdiv class=\"flex items-center gap-2\">\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ff5f56] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ffbd2e] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#27c93f] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"ml-2 font-mono font-bold text-xs uppercase tracking-wider text-gray-300\">PYTHON\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n    \u003Cbutton type=\"button\" class=\"copy-code-btn flex items-center gap-1.5 px-3 py-1 text-xs font-bold bg-[#2f363d] text-gray-200 border-2 border-black dark:border-gray-500 rounded-lg shadow-neo-sm hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none hover:bg-gray-700 transition-all cursor-pointer\" data-code=\"%23%20check_totals.py%0Aimport%20json%0Aimport%20subprocess%0Aimport%20sys%0Aimport%20tempfile%0Afrom%20pathlib%20import%20Path%0A%0ASESSIONS%20%3D%20%5B%0A%20%20%20%20%7B%22date%22%3A%20%222026-11-03%22%2C%20%22minutes%22%3A%2020%7D%2C%0A%20%20%20%20%7B%22date%22%3A%20%222026-11-03%22%2C%20%22minutes%22%3A%2045%7D%2C%0A%20%20%20%20%7B%22date%22%3A%20%222026-11-04%22%2C%20%22minutes%22%3A%2030%7D%2C%0A%5D%0AEXPECTED%20%3D%20%222026-11-03%20%2065%5Cn2026-11-04%20%2030%5Cntotal%2095%22%0A%0A%0Adef%20report(results)%3A%0A%20%20%20%20for%20label%2C%20ok%2C%20detail%20in%20results%3A%0A%20%20%20%20%20%20%20%20print(f%22%7B'PASS'%20if%20ok%20else%20'FAIL'%7D%20%20%7Blabel%7D%22%20%2B%20(f%22%20%20%3C-%20%7Bdetail%7D%22%20if%20not%20ok%20and%20detail%20else%20%22%22))%0A%20%20%20%20passed%20%3D%20sum(1%20for%20_%2C%20ok%2C%20_%20in%20results%20if%20ok)%0A%20%20%20%20print(f%22%5Cn%7Bpassed%7D%20of%20%7Blen(results)%7D%20checks%20passed%22)%0A%20%20%20%20return%20passed%20%3D%3D%20len(results)%0A%0A%0Adef%20main()%3A%0A%20%20%20%20folder%20%3D%20Path(tempfile.mkdtemp())%0A%20%20%20%20data%20%3D%20folder%20%2F%20%22sessions.json%22%0A%20%20%20%20data.write_text(json.dumps(SESSIONS))%0A%20%20%20%20run%20%3D%20subprocess.run(%0A%20%20%20%20%20%20%20%20%5Bsys.executable%2C%20%22totals.py%22%2C%20str(data)%5D%2C%0A%20%20%20%20%20%20%20%20capture_output%3DTrue%2C%20text%3DTrue%2C%20input%3D%22%22%2C%20timeout%3D20%2C%0A%20%20%20%20)%0A%20%20%20%20results%20%3D%20%5B(%22the%20script%20exits%200%22%2C%20run.returncode%20%3D%3D%200%2C%20f%22exit%20%7Brun.returncode%7D%22)%5D%0A%20%20%20%20by_day%20%3D%20%7B%7D%0A%20%20%20%20for%20line%20in%20run.stdout.strip().splitlines()%5B%3A-1%5D%3A%0A%20%20%20%20%20%20%20%20day%2C%20minutes%20%3D%20line.split()%0A%20%20%20%20%20%20%20%20by_day%5Bday%5D%20%3D%20int(minutes)%0A%20%20%20%20results.append((%222026-11-03%20adds%20up%20both%20sessions%22%2C%20by_day.get(%222026-11-03%22)%20%3D%3D%2065%2C%20str(by_day.get(%222026-11-03%22))))%0A%20%20%20%20results.append((%222026-11-04%20keeps%20its%20single%20session%22%2C%20by_day.get(%222026-11-04%22)%20%3D%3D%2030%2C%20str(by_day.get(%222026-11-04%22))))%0A%20%20%20%20results.append((%22the%20report%20prints%20the%20totals%20it%20computed%22%2C%20run.stdout.strip()%20%3D%3D%20EXPECTED%2C%20repr(run.stdout.strip())))%0A%20%20%20%20source%20%3D%20Path(%22totals.py%22).read_text()%0A%20%20%20%20results.append((%22no%20breakpoint()%20left%20in%20the%20script%22%2C%20%22breakpoint()%22%20not%20in%20source%2C%20%22remove%20it%20before%20shipping%22))%0A%20%20%20%20return%200%20if%20report(results)%20else%201%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20sys.exit(main())\" title=\"Salin\">\n      \u003Csvg class=\"w-3.5 h-3.5 copy-icon-svg\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n        \u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\">\u003C\u002Fpath>\n      \u003C\u002Fsvg>\n      \u003Cspan class=\"copy-text\">Salin\u003C\u002Fspan>\n    \u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"code-block-content p-4 overflow-x-auto text-sm font-mono leading-relaxed bg-[#24292e]\">\n    \u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># check_totals.py\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">import\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> json\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">import\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> subprocess\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">import\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> sys\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">import\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> tempfile\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">from\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> pathlib \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">import\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> Path\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">SESSIONS\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\"> =\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> [\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    {\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"date\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"2026-11-03\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"minutes\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">20\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">},\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    {\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"date\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"2026-11-03\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"minutes\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">45\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">},\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    {\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"date\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"2026-11-04\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"minutes\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">30\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">},\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">EXPECTED\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\"> =\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\"> \"2026-11-03  65\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">\\n\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">2026-11-04  30\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">\\n\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">total 95\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">def\u003C\u002Fspan>\u003Cspan style=\"color:#B392F0\"> report\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(results):\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    for\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> label, ok, detail \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">in\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> results:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">        print\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">f\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">{\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">'PASS'\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\"> if\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> ok \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">else\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\"> 'FAIL'\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">}\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">  {\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">label\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">}\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\"> +\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> (\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">f\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"  &#x3C;- \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">{\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">detail\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">}\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\"> if\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\"> not\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> ok \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">and\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> detail \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">else\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\"> \"\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">))\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    passed \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\"> sum\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">1\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\"> for\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> _, ok, _ \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">in\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> results \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">if\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> ok)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">    print\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">f\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">\\n{\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">passed\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">}\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\"> of \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">{len\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(results)\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">}\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\"> checks passed\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    return\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> passed \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">==\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\"> len\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(results)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">def\u003C\u002Fspan>\u003Cspan style=\"color:#B392F0\"> main\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">():\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    folder \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> Path(tempfile.mkdtemp())\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    data \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> folder \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">\u002F\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\"> \"sessions.json\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    data.write_text(json.dumps(\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">SESSIONS\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">))\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    run \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> subprocess.run(\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        [sys.executable, \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"totals.py\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">str\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(data)],\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFAB70\">        capture_output\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">True\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#FFAB70\">text\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">True\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#FFAB70\">input\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#FFAB70\">timeout\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">20\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    )\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    results \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> [(\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"the script exits 0\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, run.returncode \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">==\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\"> 0\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">f\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"exit \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">{\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">run.returncode\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">}\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">)]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    by_day \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> {}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    for\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> line \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">in\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> run.stdout.strip().splitlines()[:\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">-\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">1\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">]:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        day, minutes \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> line.split()\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        by_day[day] \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\"> int\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(minutes)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    results.append((\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"2026-11-03 adds up both sessions\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, by_day.get(\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"2026-11-03\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">) \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">==\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\"> 65\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">str\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(by_day.get(\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"2026-11-03\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">))))\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    results.append((\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"2026-11-04 keeps its single session\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, by_day.get(\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"2026-11-04\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">) \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">==\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\"> 30\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">str\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(by_day.get(\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"2026-11-04\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">))))\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    results.append((\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"the report prints the totals it computed\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, run.stdout.strip() \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">==\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\"> EXPECTED\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">repr\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(run.stdout.strip())))\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    source \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> Path(\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"totals.py\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">).read_text()\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    results.append((\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"no breakpoint() left in the script\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"breakpoint()\"\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\"> not\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\"> in\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> source, \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"remove it before shipping\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">))\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    return\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\"> 0\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\"> if\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> report(results) \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">else\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\"> 1\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">if\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\"> __name__\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\"> ==\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\"> \"__main__\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    sys.exit(main())\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\u003Cp>Jalankan terhadap skrip awal dan 3 dari 5 pemeriksaan lolos. Kode keluarnya 1.\u003C\u002Fp>\n\n\u003Cdiv class=\"code-block-wrapper my-6 not-prose rounded-xl border-2 border-black dark:border-gray-600 shadow-neo overflow-hidden bg-[#24292e] text-[#e1e4e8] transition-all\">\n  \u003Cdiv class=\"code-block-header flex items-center justify-between px-4 py-2.5 bg-[#1f2428] border-b-2 border-black dark:border-gray-600 font-mono text-xs font-bold text-gray-300 select-none\">\n    \u003Cdiv class=\"flex items-center gap-2\">\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ff5f56] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ffbd2e] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#27c93f] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"ml-2 font-mono font-bold text-xs uppercase tracking-wider text-gray-300\">TEXT\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n    \u003Cbutton type=\"button\" class=\"copy-code-btn flex items-center gap-1.5 px-3 py-1 text-xs font-bold bg-[#2f363d] text-gray-200 border-2 border-black dark:border-gray-500 rounded-lg shadow-neo-sm hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none hover:bg-gray-700 transition-all cursor-pointer\" data-code=\"%24%20python3%20check_totals.py%0APASS%20%20the%20script%20exits%200%0AFAIL%20%202026-11-03%20adds%20up%20both%20sessions%20%20%3C-%2045%0APASS%20%202026-11-04%20keeps%20its%20single%20session%0AFAIL%20%20the%20report%20prints%20the%20totals%20it%20computed%20%20%3C-%20'2026-11-03%20%2045%5Cn2026-11-04%20%2030%5Cntotal%2075'%0APASS%20%20no%20breakpoint()%20left%20in%20the%20script%0A%0A3%20of%205%20checks%20passed\" title=\"Salin\">\n      \u003Csvg class=\"w-3.5 h-3.5 copy-icon-svg\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n        \u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\">\u003C\u002Fpath>\n      \u003C\u002Fsvg>\n      \u003Cspan class=\"copy-text\">Salin\u003C\u002Fspan>\n    \u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"code-block-content p-4 overflow-x-auto text-sm font-mono leading-relaxed bg-[#24292e]\">\n    \u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\">\u003Ccode>$ python3 check_totals.py\nPASS  the script exits 0\nFAIL  2026-11-03 adds up both sessions  &lt;- 45\nPASS  2026-11-04 keeps its single session\nFAIL  the report prints the totals it computed  &lt;- '2026-11-03  45\\n2026-11-04  30\\ntotal 75'\nPASS  no breakpoint() left in the script\n\n3 of 5 checks passed\u003C\u002Fcode>\u003C\u002Fpre>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\u003Cp>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.\u003C\u002Fp>\n\n\u003Cdiv class=\"code-block-wrapper my-6 not-prose rounded-xl border-2 border-black dark:border-gray-600 shadow-neo overflow-hidden bg-[#24292e] text-[#e1e4e8] transition-all\">\n  \u003Cdiv class=\"code-block-header flex items-center justify-between px-4 py-2.5 bg-[#1f2428] border-b-2 border-black dark:border-gray-600 font-mono text-xs font-bold text-gray-300 select-none\">\n    \u003Cdiv class=\"flex items-center gap-2\">\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ff5f56] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ffbd2e] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#27c93f] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"ml-2 font-mono font-bold text-xs uppercase tracking-wider text-gray-300\">TEXT\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n    \u003Cbutton type=\"button\" class=\"copy-code-btn flex items-center gap-1.5 px-3 py-1 text-xs font-bold bg-[#2f363d] text-gray-200 border-2 border-black dark:border-gray-500 rounded-lg shadow-neo-sm hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none hover:bg-gray-700 transition-all cursor-pointer\" data-code=\"%24%20python3%20check_totals.py%0APASS%20%20the%20script%20exits%200%0APASS%20%202026-11-03%20adds%20up%20both%20sessions%0APASS%20%202026-11-04%20keeps%20its%20single%20session%0APASS%20%20the%20report%20prints%20the%20totals%20it%20computed%0APASS%20%20no%20breakpoint()%20left%20in%20the%20script%0A%0A5%20of%205%20checks%20passed\" title=\"Salin\">\n      \u003Csvg class=\"w-3.5 h-3.5 copy-icon-svg\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n        \u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\">\u003C\u002Fpath>\n      \u003C\u002Fsvg>\n      \u003Cspan class=\"copy-text\">Salin\u003C\u002Fspan>\n    \u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"code-block-content p-4 overflow-x-auto text-sm font-mono leading-relaxed bg-[#24292e]\">\n    \u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\">\u003Ccode>$ python3 check_totals.py\nPASS  the script exits 0\nPASS  2026-11-03 adds up both sessions\nPASS  2026-11-04 keeps its single session\nPASS  the report prints the totals it computed\nPASS  no breakpoint() left in the script\n\n5 of 5 checks passed\u003C\u002Fcode>\u003C\u002Fpre>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\u003Cp>Pemeriksaan terakhir gagal kalau \u003Ccode>breakpoint()\u003C\u002Fcode> masih ada di \u003Ccode>totals.py\u003C\u002Fcode>. Breakpoint yang tertinggal di batch job menunggu \u003Ccode>stdin\u003C\u002Fcode> selamanya, jadi kamu ingin menemukannya sebelum dirilis.\u003C\u002Fp>\n\u003Ch2 id=\"yang-tidak-bisa-dijawab-pdb\">Yang tidak bisa dijawab pdb\u003C\u002Fh2>\n\u003Cp>\u003Ccode>p\u003C\u002Fcode> menunjukkan nilai pada saat itu, bukan riwayatnya. Untuk tahu bagaimana sebuah nilai sampai di sana, kamu melangkah dengan \u003Ccode>n\u003C\u002Fcode> dan mencetak lagi, seperti di transkrip tadi.\u003C\u002Fp>\n\u003Cp>Breakpoint juga menghentikan batch job. Program tidak selesai sampai seseorang menjawab di prompt.\u003C\u002Fp>\n\u003Cp>\u003Ccode>PYTHONBREAKPOINT=0\u003C\u002Fcode> mematikan breakpoint tanpa mengedit file. Pada skrip ini, ia hanya menyembunyikan debugger.\u003C\u002Fp>\n\n\u003Cdiv class=\"code-block-wrapper my-6 not-prose rounded-xl border-2 border-black dark:border-gray-600 shadow-neo overflow-hidden bg-[#24292e] text-[#e1e4e8] transition-all\">\n  \u003Cdiv class=\"code-block-header flex items-center justify-between px-4 py-2.5 bg-[#1f2428] border-b-2 border-black dark:border-gray-600 font-mono text-xs font-bold text-gray-300 select-none\">\n    \u003Cdiv class=\"flex items-center gap-2\">\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ff5f56] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ffbd2e] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#27c93f] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"ml-2 font-mono font-bold text-xs uppercase tracking-wider text-gray-300\">TEXT\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n    \u003Cbutton type=\"button\" class=\"copy-code-btn flex items-center gap-1.5 px-3 py-1 text-xs font-bold bg-[#2f363d] text-gray-200 border-2 border-black dark:border-gray-500 rounded-lg shadow-neo-sm hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none hover:bg-gray-700 transition-all cursor-pointer\" data-code=\"%24%20PYTHONBREAKPOINT%3D0%20python3%20totals.py%20sessions.json%0A2026-11-03%20%2045%0A2026-11-04%20%2030%0Atotal%2075\" title=\"Salin\">\n      \u003Csvg class=\"w-3.5 h-3.5 copy-icon-svg\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n        \u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\">\u003C\u002Fpath>\n      \u003C\u002Fsvg>\n      \u003Cspan class=\"copy-text\">Salin\u003C\u002Fspan>\n    \u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"code-block-content p-4 overflow-x-auto text-sm font-mono leading-relaxed bg-[#24292e]\">\n    \u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\">\u003Ccode>$ PYTHONBREAKPOINT=0 python3 totals.py sessions.json\n2026-11-03  45\n2026-11-04  30\ntotal 75\u003C\u002Fcode>\u003C\u002Fpre>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\u003Cp>Breakpoint dilewati, skrip keluar dengan kode 0, dan bug-nya tetap ada.\u003C\u002Fp>\n\u003Cp>Kalau angka salah dan tidak ada yang melempar error, hentikan program di baris yang menulis nilainya, karena traceback tidak akan datang.\u003C\u002Fp>\n","\u003Cul>\n\u003Cli>Jawaban salah tanpa traceback tidak punya baris untuk diikuti, jadi kamu menghentikan program di tempat nilai ditulis, bukan membaca pesan error.\u003C\u002Fli>\n\u003Cli>\u003Ccode>breakpoint()\u003C\u002Fcode> memulai \u003Ccode>pdb\u003C\u002Fcode> di dalam loop, dan \u003Ccode>p\u003C\u002Fcode> mencetak nilai pada saat itu: hari yang sama datang dua kali dan tulisan kedua menggantikan yang pertama.\u003C\u002Fli>\n\u003Cli>\u003Ccode>totals[day] = totals.get(day, 0) + session[&quot;minutes&quot;]\u003C\u002Fcode> menambah, bukan menimpa, dan itulah seluruh perbaikannya.\u003C\u002Fli>\n\u003Cli>Breakpoint yang tertinggal di batch job menunggu \u003Ccode>stdin\u003C\u002Fcode> selamanya, jadi checker gagal kalau \u003Ccode>breakpoint()\u003C\u002Fcode> masih ada di file.\u003C\u002Fli>\n\u003C\u002Ful>\n",[224,246,265,282,287,306],{"id":225,"title":226,"title_id":227,"slug":228,"excerpt":229,"excerpt_id":230,"has_translation":12,"published_at":231,"reading_time":100,"hidden_from_listing":15,"tags":232,"thumbnail_image":21,"og_image":237,"series":238,"authors":243},48,"What a Linked List Actually Is","Tentang Struktur Data LinkedList","tentang-struktur-data-linked-list","A linked list is a linear chain of nodes, each holding data and a pointer to the next node. Here is what that chain looks like, what it costs next to an array, and the same minimal implementation in Java, Python, and JavaScript.","Linked list adalah rantai linear berisi node, dan setiap node menyimpan data plus pointer ke node berikutnya. Ini bentuk rantainya, ongkosnya dibanding array, dan implementasi paling sederhana dalam Java, Python, serta JavaScript.","2026-11-21T01:00:00.000Z",[233,234,235,236],"struktur-data","linked-list","pointer","konsep","\u002Fog\u002Ftentang-struktur-data-linked-list.png",{"id":239,"title":240,"title_id":241,"slug":242},28,"Linear Data Structures","Struktur Data Linear","asd-struktur-linear",[244],{"authors_id":245},{"id":109,"name":110,"slug":111,"role":112,"profile_picture":21},{"id":247,"title":248,"title_id":249,"slug":250,"excerpt":251,"excerpt_id":252,"has_translation":12,"published_at":253,"reading_time":79,"hidden_from_listing":15,"tags":254,"thumbnail_image":21,"og_image":256,"series":257,"authors":262},47,"Why Understanding Algorithms and Data Structures Actually Matters","Pentingnya Memahami Algoritma dan Struktur Data","pentingnya-memahami-algoritma-dan-struktur-data","An algorithm is an ordered list of steps, and that order decides how expensive a problem is. The examples are on the back of an instant noodle packet, in a factorial, and behind ctrl+z.","Sebagai konsep fundamental, algoritma menentukan seberapa mahal sebuah masalah diselesaikan. Contohnya ada di bungkus mie instan, di bilangan faktorial, dan di tombol undo.","2026-11-19T01:00:00.000Z",[255,233,236,123],"algoritma","\u002Fog\u002Fpentingnya-memahami-algoritma-dan-struktur-data.png",{"id":258,"title":259,"title_id":260,"slug":261},27,"About Algorithms and Data Structures","Tentang Algoritma dan Struktur Data","asd-intro",[263],{"authors_id":264},{"id":109,"name":110,"slug":111,"role":112,"profile_picture":21},{"id":266,"title":267,"title_id":268,"slug":269,"excerpt":270,"excerpt_id":271,"has_translation":12,"published_at":272,"reading_time":14,"hidden_from_listing":15,"tags":273,"thumbnail_image":21,"og_image":277,"series":278,"authors":279},108,"At 11 a.m. the AI Quota Runs Out, and the Work Stops There","Jam 11 siang kuota AI-nya habis, dan kerjaan berhenti di situ","jam-11-kuota-ai-habis-kerjaan-berhenti","The panel says the usage limit is reached and the work stops, not because it is finished. Three guesses explain it, and all three are wrong: the limit is shaped like a cliff, and the day is built on top of one tool.","Panel di layar bilang batas pemakaian tercapai, dan kerja berhenti di situ. Bukan karena selesai. Ada tiga tebakan kenapa ini terjadi, dan ketiganya salah: batasnya berbentuk tebing, dan harinya dibangun di atas satu alat.","2026-11-12T01:00:00.000Z",[274,102,275,276],"ai","rate-limit","produktivitas","\u002Fog\u002Fjam-11-kuota-ai-habis-kerjaan-berhenti.png",{"id":24,"title":25,"title_id":26,"slug":27},[280],{"authors_id":281},{"id":109,"name":110,"slug":111,"role":112,"profile_picture":21},{"id":74,"title":176,"title_id":75,"slug":76,"excerpt":177,"excerpt_id":77,"has_translation":12,"published_at":78,"reading_time":79,"hidden_from_listing":15,"tags":80,"thumbnail_image":21,"og_image":83,"series":283,"authors":284},{"id":85,"title":86,"title_id":87,"slug":88},[285],{"authors_id":286},{"id":109,"name":110,"slug":111,"role":112,"profile_picture":21},{"id":288,"title":289,"title_id":290,"slug":291,"excerpt":292,"excerpt_id":293,"has_translation":12,"published_at":294,"reading_time":295,"hidden_from_listing":15,"tags":296,"thumbnail_image":21,"og_image":300,"series":301,"authors":303},141,"A 6,000-Token Handbook and a 0% Cache Hit Rate","Buku Panduan 6.000 Token dan Hit Rate Cache 0%","cache-hit-rate-zero","Twenty-four turns, caching switched on, and a bill 21% higher than running with no cache at all. A support agent whose prompt looks static, and the three lines that quietly change it every turn.","Dua puluh empat giliran, caching menyala, dan tagihan 21% lebih tinggi daripada menjalankannya tanpa cache sama sekali. Agen support yang prompt-nya kelihatan statis, dan tiga baris yang diam-diam mengubahnya tiap giliran.","2026-11-08T01:20:00.000Z",3,[70,297,298,299],"llm","cache","api","\u002Fog\u002Fcache-hit-rate-zero.png",{"id":302,"title":69,"title_id":69,"slug":70},29,[304],{"authors_id":305},{"id":109,"name":110,"slug":111,"role":112,"profile_picture":21},{"id":307,"title":308,"title_id":309,"slug":310,"excerpt":311,"excerpt_id":312,"has_translation":12,"published_at":313,"reading_time":314,"hidden_from_listing":15,"tags":315,"thumbnail_image":21,"og_image":317,"series":318,"authors":320},140,"What a Prompt Cache Actually Remembers","Apa yang Sebenarnya Diingat Prompt Cache","what-a-prompt-cache-remembers","Four providers cache the same bytes under four different rulebooks. The question that decides your bill: when the second request is longer than the first, does it reuse the whole prefix or only the part that was already written?","Empat provider meng-cache byte yang sama dengan empat buku aturan berbeda. Pertanyaan yang menentukan tagihanmu: saat permintaan kedua lebih panjang, apakah seluruh prefix dipakai ulang atau cuma bagian yang sudah pernah ditulis?","2026-11-08T01:10:00.000Z",6,[297,316,298,299],"ai-agent","\u002Fog\u002Fwhat-a-prompt-cache-remembers.png",{"id":319,"title":35,"title_id":36,"slug":37},8,[321],{"authors_id":322},{"id":109,"name":110,"slug":111,"role":112,"profile_picture":21},[324,328,331,334,337],{"text":325,"depth":326,"id":327},"Angka salah tanpa traceback",2,"angka-salah-tanpa-traceback",{"text":329,"depth":326,"id":330},"Hentikan di titik salahnya","hentikan-di-titik-salahnya",{"text":332,"depth":326,"id":333},"Satu baris yang menambah, bukan menimpa","satu-baris-yang-menambah-bukan-menimpa",{"text":335,"depth":326,"id":336},"Checker","checker",{"text":338,"depth":326,"id":339},"Yang tidak bisa dijawab pdb","yang-tidak-bisa-dijawab-pdb",1794269487267]