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 #

AspekLinter (ruff check)Formatter (ruff format)
Yang dikerjakanmenemukan pola bermasalahmenata spasi, indentasi, pemenggalan baris
Sifatnyamelaporkan; sebagian bisa diperbaiki otomatismenulis ulang file langsung
Contoh temuanimport tak terpakai (F401), variabel tak terpakai (F841)total=sum(items) menjadi total = sum(items)
Sikap terhadap gayamemeriksa 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:

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

Jalankan linter:

bash
ruff check harga.py

Output yang Dihasilkan (dipangkas; ruff juga menampilkan saran perbaikan pada tiap temuan):

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

bash
ruff check --fix harga.py

Output yang Dihasilkan:

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

python
def hitung_rata(items):
    total=sum(items)
    n=len(items)
    return total/n if n else 0

Periksa statusnya tanpa mengubah apa pun:

bash
ruff format --check format_demo.py

Output yang Dihasilkan:

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

Terapkan formatnya, lalu periksa ulang:

bash
ruff format format_demo.py
ruff format --check format_demo.py

Output yang Dihasilkan:

text
1 file reformatted
text
1 file already formatted

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

toml
[tool.ruff]
line-length = 100
target-version = "py312"

[tool.ruff.lint]
select = ["E", "F", "W", "I", "B", "UP"]
  1. line-length: panjang baris maksimum yang dianggap sehat (bawaan 88).
  2. target-version: versi Python minimum proyek; aturan modernisasi menyesuaikan (misalnya menyarankan sintaks terbaru yang sah).
  3. select: keluarga aturan yang aktif — E/W gaya PEP 8, F pyflakes (bug nyata seperti F401), I urutan import, B jebakan umum, UP modernisasi sintaks.

Dengan konfigurasi di atas, file yang bersih akan dijawab ringkas. Simpan contoh_bersih.py berikut satu folder dengan pyproject.toml:

python
import math


def luas_lingkaran(jari_jari):
    if jari_jari <= 0:
        raise ValueError("jari-jari harus positif")
    return math.pi * jari_jari ** 2

Lalu periksa:

bash
ruff check contoh_bersih.py

Output yang Dihasilkan:

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