Kodėl dar vienas linteris, jei turime Flake8 ir Pylint?
Jei dirbate su Python kodu, tikriausiai jau esate susipažinę su įvairiais linteriais – Flake8, Pylint, mypy ir visa kita įrankių ekosistema. Galite paklausti: „Kam man dar vienas įrankis?” Ir tai visiškai teisėtas klausimas. Tačiau Ruff atsirado ne tam, kad paprasčiausiai papildytų sąrašą, o tam, kad iš esmės pakeistų žaidimo taisykles.
Pirmiausia – greitis. Ruff parašytas Rust programavimo kalba, o tai reiškia, kad jis veikia 10-100 kartų greičiau nei tradiciniai Python linteriai. Tai nėra tik teorinis pranašumas – praktikoje tai reiškia, kad galite paleisti linterį ant visos savo kodo bazės per kelias sekundes, o ne minutes. Kai dirbate su dideliais projektais, šis skirtumas tampa kritiškai svarbus.
Antra, Ruff nėra tik linteris – tai yra daugiafunkcinis įrankis, kuris gali pakeisti kelis atskirus įrankius vienu. Jis apima Flake8, isort, pydocstyle, pyupgrade ir daugiau nei 700 taisyklių iš įvairių šaltinių. Vietoj to, kad konfigūruotumėte ir paleistumėte 5-6 skirtingus įrankius, galite naudoti vieną.
Kaip pradėti naudoti Ruff savo projekte
Pradėti naudoti Ruff yra neįtikėtinai paprasta. Pirmas žingsnis – įdiegimas per pip:
pip install ruff
Arba, jei naudojate poetry:
poetry add --group dev ruff
Kai tik įdiegėte, galite iš karto pradėti tikrinti savo kodą:
ruff check .
Štai ir viskas. Ruff automatiškai nuskenuos visus Python failus esamame kataloge ir parodys rastas problemas. Nereikia jokios sudėtingos konfigūracijos ar nustatymų – įrankis veikia iš karto su protingomis numatytomis reikšmėmis.
Bet tikroji magija prasideda, kai pradedate jį konfigūruoti pagal savo poreikius. Ruff konfigūraciją galite laikyti pyproject.toml faile, kuris jau tapo Python projektų standartu:
[tool.ruff]
line-length = 88
target-version = "py311"
[tool.ruff.lint]
select = ["E", "F", "I", "N", "W"]
ignore = ["E501"]
Šioje konfigūracijoje nurodome maksimalų eilutės ilgį (88 simboliai, kaip Black formateryje), tikslinę Python versiją ir kurias taisykles norime įjungti ar išjungti.
Automatinis kodo taisymas – ne tik aptikimas
Vienas iš dalykų, kuris išskiria Ruff iš kitų linterių, yra jo gebėjimas ne tik aptikti problemas, bet ir automatiškai jas taisyti. Tai ne kokia nors nauja idėja, bet Ruff tai daro neįtikėtinai greitai ir patikimai.
Norėdami automatiškai pataisyti aptiktas problemas, tiesiog pridėkite --fix parametrą:
ruff check --fix .
Ruff gali automatiškai sutvarkyti importus (kaip isort), pašalinti nenaudojamus kintamuosius, atnaujinti pasenusią sintaksę į naujesnę Python versiją, pridėti trūkstamus docstring’us ir dar daug ko. Tai sutaupo milžinišką kiekį laiko, kurį anksčiau turėdavote praleisti rankiniu kodo tvarkymu.
Pavyzdžiui, jei jūsų kode yra tokia problema:
import sys
import os
from typing import List
def process_data(items: List[str]):
unused_var = "something"
return [x.upper() for x in items]
Ruff automatiškai:
- Surikiuos importus tinkama tvarka
- Pašalins nenaudojamą
unused_varkintamąjį - Pasiūlys pakeisti
List[str]į modernesnįlist[str](jei naudojate Python 3.9+)
Integracija su IDE ir CI/CD
Ruff puikiai integruojasi su populiariausiomis kūrimo aplinkomis. VS Code naudotojams yra oficiali Ruff plėtinio versija, kuri veikia tiesiog nepriekaištingai. Įdiegę plėtinį, gausite realaus laiko klaidu paryškinimą ir automatinio taisymo pasiūlymus tiesiog rašydami kodą.
PyCharm naudotojams situacija šiek tiek sudėtingesnė, nes nėra oficialaus plėtinio, bet galite sukonfigūruoti Ruff kaip išorinį įrankį arba naudoti per File Watchers funkciją. Tai ne taip patogu kaip VS Code, bet vis tiek veikia gerai.
Integruoti Ruff į CI/CD pipeline’ą yra dar paprasčiau. Štai pavyzdys GitHub Actions:
name: Lint
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.11'
- run: pip install ruff
- run: ruff check .
Kadangi Ruff veikia taip greitai, jis nepailgins jūsų CI proceso. Priešingai, jei anksčiau naudojote kelis linterius, Ruff gali net sutrumpinti bendrą vykdymo laiką.
Taisyklių pasirinkimas ir konfigūravimas
Vienas iš Ruff privalumų – milžiniškas taisyklių rinkinys. Tačiau tai gali būti ir iššūkis pradedantiesiems. Kaip žinoti, kurias taisykles įjungti?
Ruff taisyklės sugrupuotos pagal šaltinius ir kategorijas. Štai kelios svarbiausios:
- E, W – pycodestyle klaidos ir įspėjimai (pagrindinės stiliaus taisyklės)
- F – Pyflakes (loginės klaidos, nenaudojami importai)
- I – isort (importų rikiavimas)
- N – pep8-naming (vardų suteikimo konvencijos)
- UP – pyupgrade (modernesnės Python sintaksės pasiūlymai)
- B – flake8-bugbear (potencialių klaidų aptikimas)
- C4 – flake8-comprehensions (list/dict comprehensions optimizavimas)
- SIM – flake8-simplify (kodo supaprastinimo pasiūlymai)
Pradedantiesiems rekomenduoju pradėti nuo bazinių taisyklių rinkinio:
[tool.ruff.lint]
select = ["E", "F", "I", "N", "W", "UP"]
Kai komanda pripranta prie Ruff, galite palaipsniui pridėti daugiau taisyklių. Pavyzdžiui, B (bugbear) taisyklės padeda aptikti subtilias klaidas, kurias lengva praleisti:
select = ["E", "F", "I", "N", "W", "UP", "B", "C4", "SIM"]
Svarbu suprasti, kad nereikia įjungti visų taisyklių iš karto. Geriau pradėti konservatyviai ir plėsti palaipsniui, nei užversti komandą šimtais įspėjimų.
Ruff vs Black: ar reikia abiejų?
Dažnas klausimas, kurį girdžiu: „Jei naudoju Ruff, ar man vis dar reikia Black?” Atsakymas: priklauso nuo jūsų prioritetų.
Black yra opinionated code formatter – jis turi labai aiškią viziją, kaip turėtų atrodyti Python kodas, ir nesiūlo jokių konfigūracijos galimybių (išskyrus kelias išimtis). Ruff, nors ir turi formatavimo galimybes (nuo 0.1.0 versijos), yra pirmiausiai linteris.
Nuo Ruff 0.1.0 versijos, jis įtraukė ruff format komandą, kuri yra beveik 100% suderinama su Black. Tai reiškia, kad daugelyje projektų galite visiškai pakeisti Black į Ruff ir gauti identišką rezultatą, tik daug greičiau.
ruff format .
Mano asmeninė rekomendacija: jei pradedate naują projektą, naudokite tik Ruff – ir lintingui, ir formatavimui. Jei turite esamą projektą, kuris jau naudoja Black, galite palaipsniui pereiti prie Ruff arba tiesiog palikti Black – abu įrankiai puikiai sugyvena kartu.
Praktiniai patarimai iš realių projektų
Dirbdamas su Ruff įvairiuose projektuose, pastebėjau keletą dalykų, kurie gali sutaupyti jums laiko ir nervų.
Pirma, naudokite per-file-ignores funkciją. Kartais turite failus, kuriuose tam tikros taisyklės tiesiog neturi prasmės. Pavyzdžiui, testų failuose dažnai naudojami assert’ai, kurie gali sukelti įspėjimus:
[tool.ruff.lint.per-file-ignores]
"tests/*" = ["S101"] # Leisti assert'us testuose
"__init__.py" = ["F401"] # Leisti nenaudojamus importus init failuose
Antra, išmokite naudoti # noqa komentarus protingai. Kartais Ruff aptinka „problemą”, kuri iš tikrųjų yra tyčinė. Vietoj to, kad išjungtumėte taisyklę visam projektui, galite ją ignoruoti konkrečioje eilutėje:
import platform_specific_module # noqa: F401
Arba ignoruoti konkrečią taisyklę:
x = 1 # noqa: E701
Trečia, reguliariai atnaujinkite Ruff. Projektas vystosi neįtikėtinai greitai – naujos versijos išleidžiamos kas kelias savaites, ir kiekviena atneša naujų taisyklių, klaidų pataisymų ir našumo patobulinimų. Kadangi Ruff yra development įrankis, atnaujinimai paprastai yra saugūs ir nesulaužo egzistuojančios konfigūracijos.
Kai Ruff tampa komandos standartu
Įdiegti naują įrankį asmeniniame projekte yra viena, bet įtikinti visą komandą pereiti prie naujo linterio – visai kas kita. Štai keletas patarimų, kaip tai padaryti sklandžiai.
Pradėkite nuo mažo. Vietoj to, kad iš karto pakeistumėte visą linting infrastruktūrą, pradėkite nuo vieno projekto ar net vieno modulio. Leiskite žmonėms pamatyti, kaip Ruff veikia praktikoje, ir patys įsitikinti jo privalumais.
Sukurkite bendrą konfigūraciją ir dokumentuokite ją. Komandoje visi turėtų naudoti tas pačias taisykles, kitaip gausit chaosą. Sukurkite pyproject.toml šabloną, kurį galima naudoti visuose projektuose:
[tool.ruff]
line-length = 88
target-version = "py311"
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort
"N", # pep8-naming
"UP", # pyupgrade
"B", # flake8-bugbear
]
ignore = [
"E501", # line too long (handled by formatter)
]
[tool.ruff.lint.isort]
known-first-party = ["your_package_name"]
Integruokite Ruff į pre-commit hooks. Tai užtikrina, kad kodas būtų patikrintas prieš commit’inant:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.6
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
Ir pats svarbiausias patarimas – būkite kantrūs. Ne visi iš karto įvertins naują įrankį. Kai kurie komandos nariai gali būti prisirišę prie senų įrankių. Leiskite jiems laiko prisitaikyti ir nebūkite per daug griežti. Svarbiausia, kad komanda matytų realią naudą, o ne jaustų, kad jiems primesta dar viena prievolė.
Kodėl Ruff – tai ne tik greitis
Grįžtant prie pradinio klausimo – kodėl turėtumėte išbandyti Ruff? Taip, greitis yra įspūdingas, bet tai tik viena medalio pusė.
Ruff keičia tai, kaip galvojame apie code quality įrankius. Vietoj lėtų, fragmentuotų įrankių, kurie visi daro kažką šiek tiek skirtingo, gauname vieną greitą, išsamų sprendimą. Tai reiškia mažiau konfigūracijos, mažiau priklausomybių, greitesnį CI/CD, ir svarbiausia – mažiau trinties tarp kodo rašymo ir jo kokybės tikrinimo.
Projektas turi puikią dokumentaciją, aktyvią bendruomenę ir yra aktyviai prižiūrimas. Astral komanda, kuri kuria Ruff, aiškiai supranta Python ekosistemos poreikius ir kuria įrankius, kurie tikrai sprendžia realias problemas.
Ar Ruff yra tobulas? Ne. Ar jis pakeičia absoliučiai viską? Irgi ne. Bet jei ieškote būdo pagerinti savo Python kodo kokybę nesugaištant daug laiko, Ruff yra vienas geriausių sprendimų šiandien. Išbandykite jį savo projekte – tikriausiai nustebsite, kaip greitai jis taps neatsiejama jūsų darbo eigos dalimi.
