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:

python
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):

text
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: -1000

Traceback 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:

python
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:

text
Harga 50000 valid
Ditolak sistem: harga harus positif, diterima: -1000

Mari kita telaah apa yang terjadi pada baris-baris kode di atas:

  1. Baris 7 (validasi_harga(50000)): harga positif sehingga validasi lolos dan pesan sukses tercetak.
  2. Baris 8 (validasi_harga(-1000)): kondisi harga <= 0 terpenuhi, sehingga raise pada baris 3 membangkitkan ValueError.
  3. Baris 9–10 (except ValueError as e:): handler menangkap exception tersebut, lalu pesan penolakan tercetak lengkap dengan isi error dari variabel e.

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:

python
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):

text
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:

python
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:

text
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:

python
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:

text
Ditolak              : level harus berupa angka, diterima: 'tinggi'
__suppress_context__ : True

Konteks 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:

BentukPerilaku
raise ValueError("pesan")membangkitkan exception baru
raisemeneruskan exception yang sedang aktif, traceback asli terjaga
raise Baru("pesan") from emembangkitkan exception baru, e dicatat sebagai penyebab
raise Baru("pesan") from Nonemembangkitkan exception baru, konteks lama disembunyikan