Test menjaga perilaku kode, tetapi ada kelas masalah lain yang tidak perlu test untuk menangkapnya: import yang tidak terpakai, variabel yang diberi nilai lalu dilupakan, dan gaya penulisan yang berbeda di setiap file. Semua ini bisa dideteksi dengan membaca kode secara statis — tanpa menjalankannya. Tugas itu diemban linter, sementara formatter merapikan tata letak kode secara otomatis. Di ekosistem Python modern, keduanya dijalankan oleh satu tool bernama ruff; seluruh contoh di Chapter ini diuji dengan ruff 0.17.0.
Linter versus Formatter #
| Aspek | Linter (ruff check) | Formatter (ruff format) |
|---|---|---|
| Yang dikerjakan | menemukan pola bermasalah | menata spasi, indentasi, pemenggalan baris |
| Sifatnya | melaporkan; sebagian bisa diperbaiki otomatis | menulis ulang file langsung |
| Contoh temuan | import tak terpakai (F401), variabel tak terpakai (F841) | total=sum(items) menjadi total = sum(items) |
| Sikap terhadap gaya | memeriksa aturan yang bisa berubah pendapat (misal panjang baris) | satu gaya final, tanpa debat |
Demo Linter: ruff check #
Simpan file harga.py dengan tiga masalah yang disengaja:
import os
import json
def hitung_diskon(total):
ambang = 100000
if total > ambang:
return total - (total * 10 // 100)
return total
def total_belanja(items):
total = sum(items)
jumlah = len(items)
return totalJalankan linter:
ruff check harga.pyOutput yang Dihasilkan (dipangkas; ruff juga menampilkan saran perbaikan pada tiap temuan):
F401 [*] `os` imported but unused
--> harga.py:1:8
|
1 | import os
| ^^
2 | import json
|
F841 Local variable `jumlah` is assigned to but never used
--> harga.py:14:5
|
12 | def total_belanja(items):
13 | total = sum(items)
14 | jumlah = len(items)
| ^^^^^^
15 | return total
|
Found 4 errors.
[*] 3 fixable with `--fix` (1 unsafe fix available with `--fix --unsafe-fixes`).Struktur setiap temuan konsisten: kode aturan (F401, F841), deskripsi singkat, lokasi file:baris:kolom, lalu cuplikan barisnya. Empat temuan pada run ini terdiri dari dua import tak terpakai (os dan json), urutan import yang belum terurut (I001), dan satu variabel lokal jumlah yang diberi nilai tetapi tak pernah dibaca.
Perbaikan otomatis tersedia untuk sebagian besar temuan:
ruff check --fix harga.pyOutput yang Dihasilkan:
Found 3 errors (2 fixed, 1 remaining).
1 unsafe fix available with `--fix --unsafe-fixes`.Ruff menghapus kedua import tak terpakai dan mengurutkan blok import, tetapi menahan perbaikan F841 — menghapus assignment variabel bisa mengubah perilaku bila sisi kanannya punya efek samping, sehingga ruff menandainya sebagai unsafe fix dan meminta keputusan kita.
Demo Formatter: ruff format #
File berikut berjalan benar tetapi ditulis serba mepet:
def hitung_rata(items):
total=sum(items)
n=len(items)
return total/n if n else 0Periksa statusnya tanpa mengubah apa pun:
ruff format --check format_demo.pyOutput yang Dihasilkan:
unformatted: File would be reformatted
--> format_demo.py:2:10
|
1 | def hitung_rata(items):
- total=sum(items)
- n=len(items)
- return total/n if n else 0
2 + total = sum(items)
3 + n = len(items)
4 + return total / n if n else 0
|
1 file would be reformattedTerapkan formatnya, lalu periksa ulang:
ruff format format_demo.py
ruff format --check format_demo.pyOutput yang Dihasilkan:
1 file reformatted1 file already formattedRuang di sekitar = dan / ditambahkan, sisanya tidak diutak-atik — formatter tidak mengubah makna kode sedikit pun. Gaya ruff format sengaja mengikuti keputusan gaya proyek Black agar hasilnya sudah akrab bagi komunitas.
Konfigurasi di pyproject.toml #
Ruff membaca konfigurasi dari pyproject.toml, satu tempat yang sama dengan metadata proyek:
[tool.ruff]
line-length = 100
target-version = "py312"
[tool.ruff.lint]
select = ["E", "F", "W", "I", "B", "UP"]line-length: panjang baris maksimum yang dianggap sehat (bawaan 88).target-version: versi Python minimum proyek; aturan modernisasi menyesuaikan (misalnya menyarankan sintaks terbaru yang sah).select: keluarga aturan yang aktif —E/Wgaya PEP 8,Fpyflakes (bug nyata seperti F401),Iurutan import,Bjebakan umum,UPmodernisasi sintaks.
Dengan konfigurasi di atas, file yang bersih akan dijawab ringkas. Simpan contoh_bersih.py berikut satu folder dengan pyproject.toml:
import math
def luas_lingkaran(jari_jari):
if jari_jari <= 0:
raise ValueError("jari-jari harus positif")
return math.pi * jari_jari ** 2Lalu periksa:
ruff check contoh_bersih.pyOutput yang Dihasilkan:
All checks passed!Ruff kini menjaga kualitas di mesin lokal. Tantangan terakhir adalah memastikan setiap kontributor benar-benar menjalankannya sebelum perubahan masuk — tugas pre-commit hooks pada Chapter Pre-Commit Hooks.