1 Punkte von GN⁺ 1 시간 전 | 1 Kommentare | Auf WhatsApp teilen
  • Ruff v0.16.0, ein in Rust geschriebener Python-Linter und -Formatter, erhöht die standardmäßig aktivierten Regeln von 59 auf 413 und erkennt damit ohne separate Konfiguration deutlich mehr Syntaxfehler und sofort auftretende Laufzeitfehler
  • Unterstützt die Formatierung von Python-Codeblöcken in Markdown, die etwa mit python, py, pyi oder pycon gekennzeichnet sind, und kann auch auf Quarto-Notebooks angewendet werden
  • ruff: ignore und ruff: file-ignore wurden hinzugefügt, um Diagnosen für logische Codezeilen oder ganze Dateien zu unterdrücken; mit --add-ignore lassen sich Kommentare automatisch einfügen
  • check und format --check zeigen Änderungen standardmäßig als Diff unterhalb der Diagnose an; die Formatter-Prüfung unterstützt außerdem Ausgabeformate für JSON sowie GitHub- und GitLab-CI-Kommentare
  • Die meisten können ohne große Änderungen upgraden, sollten aber prüfen, wie sich die erweiterten Standardregeln und Änderungen an der JSON-Ausgabe, bei denen einige Werte null sein können, auf bestehende Konfigurationen und Automatisierungs-Tools auswirken

Standardregeln auf 413 erweitert

  • Ruff v0.16.0 ist ein in Rust geschriebener schneller Python-Linter und -Formatter und kann über PyPI oder mit uv tool install ruff@latest installiert werden
  • Die Gesamtzahl der Ruff-Regeln ist seit v0.1.0 von 708 auf 968 gestiegen, die standardmäßig aktivierten Regeln blieben bislang jedoch bei 59
  • v0.16 erweitert die Standardregeln auf 413 und erkennt damit ohne zusätzliche Konfiguration schwerwiegende Probleme, darunter Syntaxfehler und sofort auftretende Laufzeitfehler
    • Enthalten sind unter anderem Regeln aus den Kategorien B von flake8-bugbear, UP von pyupgrade sowie Ruffs eigene Kategorie RUF
    • Die vollständige Liste steht in der Dokumentation Default Rules
  • Auch Projekte, die bereits select oder extend-select verwenden, können über die neuen Standardregeln nützliche Regeln entdecken, die ihnen zuvor nicht bekannt waren
  • Wer zu den bisherigen Standardregeln zurückkehren möchte, konfiguriert dies wie folgt
[lint]
select = ["E4", "E7", "E9", "F"]
  • Diese Änderung hängt mit der langfristigen Aufgabe der Neuklassifizierung von Regeln zusammen; die entsprechenden Arbeiten sollen weitergehen

Formatierung von Markdown-Codeblöcken

  • ruff format formatiert Python-Fenced-Codeblöcke, die in Markdown-Dateien enthalten sind
  • Unterstützte Info-Strings sind python, py, python3, py3, pyi, pycon
    • pyi wird als Stub-Dateiformat behandelt
    • pycon wird als REPL-Sitzungsformat behandelt
    • Alle übrigen werden wie normale Python-Dateien formatiert
  • Auch wenn der Sprachname wie bei {python} in geschweifte Klammern gesetzt ist, wird er erkannt, sodass sich dies auch in Quarto-Notebooks verwenden lässt
    • Bei Verwendung der Erweiterung .qmd kann eine extension-Mapping-Konfiguration erforderlich sein
  • Innerhalb von Codeblöcken kann die Formatierung teilweise mit fmt: off und fmt: on unterdrückt werden
  • Ganze Bereiche eines Markdown-Dokuments lassen sich mit den HTML-Kommentaren <!-- fmt: off --> und <!-- fmt: on --> ausschließen
  • Um alle Markdown-Dateien auszuschließen, wird in extend-exclude ein Glob wie *.md angegeben
  • Details zum Verhalten stehen in der Dokumentation Markdown code formatting

Neue Kommentare zur Diagnose-Unterdrückung

  • Nach der Bereichsunterdrückung mit ruff: disable und ruff: enable in v0.15 ergänzt v0.16 nun ruff: ignore und ruff: file-ignore
  • ruff: ignore kann wie noqa Diagnosen in derselben Zeile unterdrücken oder als eigenständiger Kommentar für die gesamte folgende logische Zeile gelten
    • Bei mehrzeiligen Funktions-Headern wird der Bereich von def bis zum Doppelpunkt als eine logische Zeile behandelt
  • ruff: file-ignore unterdrückt wie ruff: noqa die angegebenen Diagnosen für die gesamte Datei
  • Bei jedem Unterdrückungskommentar kann nach dem Regelcode auch der Grund der Anwendung angegeben werden
  • Die CLI-Option --add-ignore fügt die benötigten ruff: ignore-Kommentare automatisch hinzu
  • Im Preview-Modus können statt Codes wie F401 auch Regelnamen wie unused-import verwendet werden
  • Die vollständige Spezifikation der Kommentare ist in der Ruff-linter-Dokumentation zusammengefasst

Fix-Diffs und Ausgabeformate

  • check und format unterstützten bereits zuvor --diff, dies arbeitete jedoch getrennt von normalen Diagnosen und wurde daher nicht zusammen mit Diagnosen angezeigt, die den Grund für Korrekturen zeigen
  • Die Standardausgabe full von v0.16 zeigt mögliche Korrekturen des Linters und Formatters als Diff unterhalb der Diagnose an
  • format --check kann nun auch alle vom Linter unterstützten Ausgabeformate verwenden
    • Es kann maschinenlesbares JSON erzeugen
    • Es kann Formate ausgeben, die von GitHub und GitLab in CI als Kommentare gerendert werden
  • Unterstützte Formate sind in der CLI-Hilfe und in der Dokumentation Output Format aufgeführt

Kompatibilität und Stabilisierung

  • Die Breaking Changes in v0.16 sind überschaubar, sodass die meisten ohne größere Änderungen an Code oder Konfiguration aktualisieren können
  • In der JSON-Ausgabe können filename, location, end_location, fix.edits[].location, fix.edits[].end_location nun null sein, statt standardmäßig leere Strings oder Zeile 1, Spalte 1 zu verwenden
    • Aktuell betrifft dies nur sehr wenige Diagnosen, bei künftigen Regeln könnte dies aber häufiger werden
  • 12 Regeln wechseln vom Preview- in den stabilen Status
    • Airflow-3-Funktionssignatur-Kompatibilität AIR303, Copyright-Hinweis CPY001, Float-Konvertierung FURB164, sortiertes min/max FURB192
    • String-Verkettung in Collection-Literalen ISC004, Exception-Logging außerhalb eines Exception-Handlers LOG004, ungültiger bool-Rückgabetyp PLE0304
    • Zu viele Positionsargumente PLR0917, Rückgabe von StopIteration PLR1708, Position von None in Union RUF036
    • Zugriff auf Annotationen im Klassen-Dictionary RUF063, doppelte Einträge in __all__ RUF068
  • Auch das stabilisierte Verhalten einiger bestehender Regeln wird standardmäßig angewendet
    • BLE001 wird auch dann unterdrückt, wenn Exceptions mit anderen logging-Methoden als critical, error, exception protokolliert werden
    • FA102 prüft zusätzliche mit PEP 585 kompatible APIs wie collections.abc
    • INT001, INT002, INT003 prüfen auch gängige Nutzungsmuster wie die Zuweisung von gettext an builtins._
    • S310 wertet lokale String-Literal-Bindings aus, um False Positives zu reduzieren
    • S508 und S509 unterstützen die empfohlenen APIs aktueller PySNMP-Versionen
    • UP019 erkennt neben typing.Text auch typing_extensions.Text
  • Alle Änderungen sind im GitHub-Release einsehbar

1 Kommentare

 
GN⁺ 1 시간 전
Hacker-News-Meinungen
  • Ich habe ein Python-Projekt mit rund 3.000 Zeilen von v0.15.x auf die neue Version aktualisiert; es dauerte nicht lange, und die neue Version fand viele Probleme, die die vorherige übersehen hatte, wodurch auch die Codequalität besser wurde.
    Manuelle Korrekturen gemäß Vorschlägen: https://github.com/nickjj/plutus/commit/9af66d31f98bef841588...
    Reaktivierung der Zeilenlängen-Regel: https://github.com/nickjj/plutus/commit/21789f89bbcee37913c1...
    Erzwingen eines _-Präfixes für ungenutzte Variablen: https://github.com/nickjj/plutus/commit/a272c77b932e1c78558a...
    Automatische Ruff-Korrekturen: https://github.com/nickjj/plutus/commit/6fe69cf88385ebdf9b8c...

  • Es ist erfreulich, dass Ruff, ty und uv auch nach der Übernahme von Astral durch OpenAI aktiv weiterentwickelt werden.

    • Ich hatte mich auf ty gefreut, es lag aber deutlich hinter basedpyright zurück, sodass ich es letztlich nicht mehr verwendet habe. Nicht die fehlenden Checks waren das größte Problem, sondern die False Positives; außerdem fehlte eine Baseline-Funktion, die in großen Codebasen sehr nützlich ist.
      uv und Ruff sind großartig, und ich hoffe, dass ty irgendwann dieses Niveau erreicht.
  • Es ist erstaunlich, wie sehr man sich für Syntax-Polizei-Tools begeistert, die jeweils willkürliche Regeln implementieren und sich nicht einmal darüber einig sind, was guter Python-Code ist.
    Sie fassen mehrzeilige Dictionaries zu einer Zeile zusammen und zerstören damit die Absicht von Kommentaren, während sie nur Kleinigkeiten wie zwei Leerzeichen oder doppelte Anführungszeichen korrigieren. Das eigentliche Problem sind nicht Leerzeichen am Zeilenende oder die Sortierung von Imports, sondern schwer verständliche zehnzeilige List Comprehensions, die solche Tools nicht erkennen.
    Auf der Arbeit haben wir pylint, flake8, black und Ruff verwendet, wodurch bei jeder Änderung Hunderte Commits entstanden; diese Energie wäre an anderer Stelle besser aufgehoben gewesen.

    • Der Zweck solcher Tools ist, dass man sich auf echte Probleme konzentrieren kann. Wenn Lint-Entscheidungen automatisiert sind, verschwendet man in PRs keine Denkleistung mit Diskussionen über Formatierung.
      Wenn man die automatisch ausgeführten Ergebnisse einfach akzeptiert, kann man sich aus Lint-Diskussionen heraushalten; in Organisationen ohne solche Tools musste man aber tatsächlich Zeit darauf verwenden, über Formatierung nachzudenken und zu diskutieren.
    • Die Abneigung, Linter würden Zeit verschwenden, scheint über ein vernünftiges Maß hinauszugehen. In der Praxis gibt es genügend Hinweise darauf, dass sie eher Zeit sparen; letztlich läuft es nur darauf hinaus, dass ein Linter gelegentlich auch Änderungen vornimmt, die einem nicht gefallen.
      In der Teamentwicklung sollte man weniger stark auf persönlichen Vorlieben beharren, mehr auf die Meinungen im Umfeld hören und die Prioritäten von Zusammenarbeit und Handwerkskunst neu abwägen.
    • Ruff erkennt Kommentare am Zeilenende, hält die einzelnen Einträge in separaten Zeilen und fügt nur nachgestellte Kommas und Leerzeichen hinzu. Wenn ein nachgestelltes Komma vorhanden ist, fasst es die Zeilen auch nicht zusammen; das gezeigte Ergebnis scheint eher von Black zu stammen.
      Ein Verhalten, das Zeilen trotz Zeilenkommentaren zusammenfasst, wirkt unpassend. Doppelte Anführungszeichen sind in Python einfach eine Stilentscheidung, und wenn in einfachen Anführungszeichen doppelte Anführungszeichen vorkommen, lässt Ruff sie ebenfalls unverändert.
    • Solche Tools sparen dem Team eher Energie. Ohne Tools haben Entwickler jeweils unterschiedliche Maßstäbe für Formatierung, Codequalität und Lesbarkeit, sodass man endlos diskutiert; besser, man überlässt es Ruff.
    • Es wurde zu einer Zeile zusammengefasst, weil nach dem letzten Eintrag das Komma fehlte. Zumindest bei Black werden die Einträge nicht komprimiert, wenn man das nachgestellte Komma beibehält; da es aber häufig die beabsichtigte Formatierung zerstört, habe ich es nicht mehr an meinen Code angebunden.
  • Ich wünschte, es gäbe für Go ein Tool wie Ruff. In vielen Sprachen entstehen hervorragende Tools, aber das Go-Ökosystem ist fragmentiert, und es gibt kein Tool, das sich so ausgereift anfühlt wie Ruff, Oxc, Biome oder Mago.

    • Für Go gibt es ein besseres Go Analysis Framework: https://pkg.go.dev/golang.org/x/tools/go/analysis
      Es ist relativ neu und daher weniger bekannt, bildet aber die Grundlage von go fix und go vet; offenbar arbeitet das Go-Team daran, dass Modulautoren einfach eigene Analyse-Passes definieren können, die beim Ausführen von go fix automatisch greifen.
      Über die Struktur analysis.Analyzer erhält man Zugriff auf AST-, Typ- und SSA-Informationen und kann Informationen zwischen Analysatoren kombinieren; kompiliert man das Ganze zu einem Binary und übergibt es an go fix, kümmert sich die Toolchain sogar um komplexes Caching. Da das Go-Team es selbst gebaut und in die Toolchain aufgenommen hat, ist es sehr wahrscheinlich, dass sich auch Tools wie golangci-lint langfristig in dieses Framework integrieren.
      Man kann einen AI-Agenten anweisen, einen Go-Analysis-Analyzer zu schreiben und ihn mit go fix auszuführen; auch in meinem Projekt nutze ich das, um mehrere Regeln deterministisch und automatisch durchzusetzen, statt mich auf ungenaue Markdown-Anweisungen zu verlassen.
    • Bis vor Kurzem war die Stimmung genau umgekehrt: Die Python-Community litt unter einem Mangel an Tools, und alle wollten gofmt für Python. Ruff ist zwar kein Formatter, sondern ein Linter, aber die jüngste Entwicklungsrichtung des Python-Ökosystems ist erfreulich.
    • Ich verstehe nicht recht, inwiefern das Go-Ökosystem fragmentiert sein soll. Go hat offizielle Formatierungs- und Linting-Tools, und die Sprache selbst ist absichtlich eingeschränkt, damit selbst von Anfängern geschriebener Code eine einheitliche Form annimmt.
      Anders als bei Python oder TypeScript, wo offizielle Tools keinen bestimmten Stil erzwingen, ist es in Go schwierig, einen so drastischen Effekt zu erzielen wie beim ersten Einsatz von Ruff oder Biome.
    • Go hat eines der besten Sprachtooling-Ökosysteme, das auch ohne riesige IDE nutzbar ist, und golangci-lint ist ebenfalls ziemlich umfassend. Die Go-Distribution selbst löst bereits viele Dinge.
    • golangci-lint existiert schon seit langer Zeit und wird breit genutzt.
  • Wenn standardmäßig 413 Regeln aktiviert sind, ist das eine gute Änderung, weil die meisten Projekte nützliches Linting bekommen, ohne an der Konfiguration drehen zu müssen.

    • Ob es bei bestehenden Projekten nützlich ist, plötzlich 413 potenzielle Warnungen zu bekommen, ist fraglich; es wirkt eher für neue Projekte geeignet.
      Heutzutage kann man zwar einen Agenten damit beauftragen, alle Lint-Warnungen nach festgelegten Kriterien zu beheben, und ihn ein paar Stunden laufen lassen, aber die detaillierten Diagnosen, die Ruff bietet, sind an sich willkommen.
  • Ruff braucht ebenfalls eine Funktion wie Nix’ stateVersion, die festlegt, welche Defaults gelten sollen. Wenn man Ruff in mehreren Repositories aktualisiert, muss man jedes Mal, wenn neue Standardregeln hinzukommen, diese sofort deaktivieren oder die Verstöße beheben, was die Ergebnisse schwer vorhersagbar macht.
    Man könnte auch alle zu aktivierenden Regeln in eine Allowlist schreiben, aber es ist besser, die Konfiguration einfach zu halten und die Statusversion erst dann anzuheben, wenn alle ein paar Stunden investieren können.

    • Sinnvoller wäre es, pro Projekt die gewünschte Ruff-Version in pyproject.toml festzupinnen. Dann kann jedes Projekt die Version anheben, wenn es bereit ist; man muss nicht mehrere Projekte gleichzeitig koordinieren, und wenn eines zurückbleibt, blockiert das die anderen nicht.
    • Dem Original zufolge hat sich Ruffs Standard-Regelsatz seit über zwei Jahren nicht geändert; die letzte Änderung war v0.1.0.
      https://github.com/astral-sh/ruff/releases/tag/v0.1.0
  • Im Zeitalter des Agentic Coding ist starkes Linting wichtiger denn je, und ich würde mir Tools wie forbidigo für mehr Sprachen wünschen.

    • Ich aktualisiere gerade Projekte, habe dabei aber gemischte Gefühle. Wenn ich selbst Code schreibe, entscheide ich intuitiv, wann ich Regeln überspringe oder ignoriere; in Projekten, in denen die meisten pylint-Regeln aktiviert waren, wurde der Code dagegen durch Workarounds, nur um pylint zufriedenzustellen, eher schwerer lesbar.
      Coding-Agenten verbrauchen ebenfalls viele Tokens, um Kleinigkeiten zu beheben, oder deaktivieren Regeln ganz, wenn Tests fehlschlagen. Der allgemeinen Korrektheit von AI-Ergebnissen vertraue ich inzwischen, aber ihrem Urteilsvermögen bei der Codequalität kann man weiterhin nur schwer trauen.
  • Obwohl es ganze 413 Regeln gibt, führt man jedes Mal, wenn man zu einer neuen Codebasis stößt, wieder dieselben drei Debatten über die Sortierung von Imports.

  • Schön zu sehen, dass nun offenbar Nutzung ohne Konfiguration empfohlen wird. In einer neuen .ruff.toml reicht dann line-length = 300.