Hingga titik ini kita hanya menangkap error yang dibangkitkan orang lain. Padahal program kita sendiri juga perlu melaporkan kegagalan: harga tidak boleh negatif, stok tidak boleh minus, konfigurasi wajib lengkap. Pernyataan raise adalah cara Python membangkitkan exception secara sengaja. Pada Chapter ini kita akan mempelajari empat bentuk raise, mulai dari membangkitkan exception baru sampai meneruskan exception lama.
Membangkitkan Exception Baru #
Bentuk paling dasar: raise diikuti class exception atau instance-nya. Saat interpreter Python mengeksekusi raise, eksekusi normal berhenti dan exception mulai mencari penangkap — persis seperti error bawaan. Simpan kode berikut sebagai kasir.py, lalu jalankan dengan python3 kasir.py:
def validasi_harga(harga):
if harga <= 0:
raise ValueError(f"harga harus positif, diterima: {harga}")
validasi_harga(-1000)Output yang Dihasilkan (dicetak interpreter Python ke stderr):
Traceback (most recent call last):
File "kasir.py", line 5, in <module>
validasi_harga(-1000)
File "kasir.py", line 3, in validasi_harga
raise ValueError(f"harga harus positif, diterima: {harga}")
ValueError: harga harus positif, diterima: -1000Traceback menunjukkan dua frame: baris 3 sebagai titik raise di dalam function validasi_harga(), dan baris 5 sebagai pemanggil yang memicu validasi tersebut. Pemanggil bisa menangkap exception ini seperti biasa:
def validasi_harga(harga):
if harga <= 0:
raise ValueError(f"harga harus positif, diterima: {harga}")
print(f"Harga {harga} valid")
try:
validasi_harga(50000)
validasi_harga(-1000)
except ValueError as e:
print("Ditolak sistem:", e)Output yang Dihasilkan:
Harga 50000 valid
Ditolak sistem: harga harus positif, diterima: -1000Mari kita telaah apa yang terjadi pada baris-baris kode di atas:
- Baris 7 (
validasi_harga(50000)): harga positif sehingga validasi lolos dan pesan sukses tercetak. - Baris 8 (
validasi_harga(-1000)): kondisiharga <= 0terpenuhi, sehinggaraisepada baris 3 membangkitkanValueError. - Baris 9–10 (
except ValueError as e:): handler menangkap exception tersebut, lalu pesan penolakan tercetak lengkap dengan isi error dari variabele.
Kapan Program Perlu raise #
Dua situasi paling umum: memvalidasi masukan dan menjaga aturan domain. Validasi berarti menolak data sebelum sempat merusak kondisi lain; menjaga aturan domain berarti membatalkan operasi yang akan melanggar aturan bisnis, misalnya menarik saldo melebihi sisa dana. Prinsipnya: bangkitkan exception di titik error pertama kali terdeteksi, bukan biarkan data buruk mengalir ke kode lain dan meledak di tempat yang jauh dari akar masalah. Strategi lengkapnya kita rangkum pada Chapter Merancang Error Handling.
Pemilihan class juga menyampaikan makna: ValueError untuk nilai yang tidak sah, TypeError untuk tipe yang salah, KeyError untuk kunci yang hilang. Pesan yang menyertainya sebaiknya menyebut dua hal: aturan yang dilanggar dan nilai pelanggarnya. Pesan "harga harus positif, diterima: -1000" jauh lebih cepat di-debug daripada "harga tidak valid" — pembaca log langsung tahu aturan apa dan data apa yang bertabrakan. Saat tidak ada class bawaan yang pas, barulah class custom dari Chapter Membuat Custom Exceptions masuk ke dalam hitungan.
Re-raise dengan raise Kosong #
Di dalam blok except, raise yang ditulis sendirian tanpa operan meneruskan exception yang sedang aktif. Pola ini berguna ketika lapisan tengah perlu mencatat kejadian, tetapi keputusan akhir diserahkan ke lapisan yang lebih atas. Simpan sebagai gudang.py dan jalankan:
def proses_pesanan(pesanan):
try:
jumlah = int(pesanan["jumlah"])
except ValueError:
print(f"Data pesanan bermasalah: {pesanan}")
raise
proses_pesanan({"produk": "Kopi Gayo", "jumlah": "dua"})Output yang Dihasilkan (stdout dan stderr digabung):
Data pesanan bermasalah: {'produk': 'Kopi Gayo', 'jumlah': 'dua'}
Traceback (most recent call last):
File "gudang.py", line 8, in <module>
proses_pesanan({"produk": "Kopi Gayo", "jumlah": "dua"})
File "gudang.py", line 3, in proses_pesanan
jumlah = int(pesanan["jumlah"])
^^^^^^^^^^^^^^^^^^^^^^
ValueError: invalid literal for int() with base 10: 'dua'Dua hal penting dari raise kosong. Pertama, traceback tetap menunjuk baris 3 — titik asli kegagalan int() — bukan baris raise. Kedua, pesan ValueError beserta konteksnya utuh sampai ke pemanggil teratas. Menulis raise ValueError(...) baru di tempat itu akan mengganti exception asli dan memutus jejak asal-usul error.
Perbedaan halus yang layak diketahui: menulis raise e (memakai variabel) juga meneruskan exception yang sama, tetapi traceback hasilnya menambahkan satu frame ekstra pada baris raise e itu sendiri. Dengan raise kosong, jejak tetap bersih dan menunjuk langsung ke titik kegagalan asli, sehingga pembaca log tidak perlu melewati frame yang tidak memberi informasi baru.
Membangkitkan Exception Baru dari Exception Lama #
Terkadang exception bawaan terlalu teknis untuk diteruskan ke pemanggil. Kata kunci from menghubungkan exception baru dengan exception lama sebagai penyebab:
def baca_konfigurasi(data, kunci):
try:
return data[kunci]
except KeyError as e:
raise ValueError(f"kunci '{kunci}' wajib ada") from e
try:
baca_konfigurasi({"host": "localhost"}, "port")
except ValueError as e:
print("Konfigurasi tidak lengkap:", e)
print("Penyebab:", repr(e.__cause__))Output yang Dihasilkan:
Konfigurasi tidak lengkap: kunci 'port' wajib ada
Penyebab: KeyError('port')Pemanggil menerima ValueError yang bermakna, sementara KeyError asli tersimpan sebagai penyebab di atribut __cause__. Sebaliknya, from None justru memutus rantai — dipakai saat detail internal sengaja tidak ingin diperlihatkan:
def set_level(nilai):
try:
return int(nilai)
except ValueError:
raise ValueError(f"level harus berupa angka, diterima: {nilai!r}") from None
try:
set_level("tinggi")
except ValueError as e:
print("Ditolak :", e)
print("__suppress_context__ :", e.__suppress_context__)Output yang Dihasilkan:
Ditolak : level harus berupa angka, diterima: 'tinggi'
__suppress_context__ : TrueKonteks ValueError pertama (pesan invalid literal ...) disembunyikan karena pesan pada exception baru sudah menjelaskan masalahnya dengan lebih jelas. Perilaku lengkap kedua atribut tersebut menjadi fokus Chapter Exception Chaining.
Empat bentuk raise dirangkum dalam tabel berikut:
| Bentuk | Perilaku |
|---|---|
raise ValueError("pesan") | membangkitkan exception baru |
raise | meneruskan exception yang sedang aktif, traceback asli terjaga |
raise Baru("pesan") from e | membangkitkan exception baru, e dicatat sebagai penyebab |
raise Baru("pesan") from None | membangkitkan exception baru, konteks lama disembunyikan |