1 puntos por GN⁺ 1 시간 전 | 1 comentarios | Compartir por WhatsApp
  • Ruff v0.16.0, el linter y formateador de Python escrito en Rust, aumenta las reglas activadas por defecto de 59 a 413, lo que permite detectar con mayor amplitud errores de sintaxis y fallos de ejecución inmediatos sin configuración adicional
  • Ahora admite el formateo de bloques de código Python marcados en Markdown como python, py, pyi, pycon, etc., y también puede aplicarse a notebooks de Quarto
  • Se agregan ruff: ignore y ruff: file-ignore, que permiten suprimir diagnósticos para líneas lógicas de código o archivos completos, y --add-ignore puede insertar comentarios automáticamente
  • check y format --check muestran por defecto los cambios de corrección como diff debajo del diagnóstico, y la verificación del formateador también admite salida JSON y formatos para comentarios de CI de GitHub y GitLab
  • La mayoría podrá actualizar sin grandes cambios, pero conviene revisar el impacto de las reglas predeterminadas ampliadas y de los cambios en la salida JSON, donde algunos valores ahora pueden ser null, sobre la configuración existente y las herramientas de automatización

Reglas predeterminadas ampliadas a 413

  • Ruff v0.16.0 es un linter y formateador rápido de Python escrito en Rust, y puede instalarse desde PyPI o con uv tool install ruff@latest
  • El total de reglas de Ruff creció de 708 en v0.1.0 a 968, pero las reglas activadas por defecto se habían mantenido en 59 hasta ahora
  • v0.16 amplía las reglas predeterminadas a 413, detectando sin configuración adicional problemas graves, incluidos errores de sintaxis y fallos de ejecución inmediatos
    • Incluye reglas de las categorías B de flake8-bugbear, UP de pyupgrade y RUF del propio Ruff, entre otras
    • La lista completa puede consultarse en la documentación de Default Rules
  • Los proyectos que ya usan select o extend-select también pueden descubrir reglas útiles que antes no conocían gracias a estas nuevas reglas predeterminadas
  • Para volver a las reglas predeterminadas anteriores, se configura así
[lint]
select = ["E4", "E7", "E9", "F"]
  • Este cambio está relacionado con la tarea de largo plazo de reclasificación de reglas, y ese trabajo seguirá avanzando

Formateo de bloques de código Markdown

  • ruff format formatea los bloques de código Python con cercas incluidos en archivos Markdown
  • Las cadenas de información admitidas son python, py, python3, py3, pyi, pycon
    • pyi se trata como formato de archivo stub
    • pycon se trata como formato de sesión REPL
    • El resto se formatea como archivos normales de Python
  • También reconoce el nombre del lenguaje cuando está entre llaves, como {python}, por lo que puede usarse en notebooks de Quarto
    • Si se usa la extensión .qmd, puede ser necesaria una configuración de mapeo de extension
  • Dentro de los bloques de código, se puede suprimir parte del formateo con fmt: off y fmt: on
  • También es posible excluir secciones completas del documento Markdown con los comentarios HTML <!-- fmt: off --> y <!-- fmt: on -->
  • Para excluir todos los archivos Markdown, se puede indicar un glob como *.md en extend-exclude
  • El comportamiento detallado puede consultarse en la documentación de Markdown code formatting

Nuevos comentarios para suprimir diagnósticos

  • Después de la supresión por rango con ruff: disable y ruff: enable de la v0.15, la v0.16 añade ruff: ignore y ruff: file-ignore
  • ruff: ignore puede suprimir diagnósticos en la misma línea, como noqa, o escribirse como comentario independiente para aplicarse a toda la siguiente línea lógica
    • En encabezados de funciones escritos en varias líneas, desde def hasta los dos puntos se considera una sola línea lógica
  • ruff: file-ignore suprime diagnósticos específicos en todo el archivo, como ruff: noqa
  • En cada comentario de supresión se puede escribir la razón de aplicación después del código de regla
  • La opción de CLI --add-ignore agrega automáticamente los comentarios ruff: ignore necesarios
  • En modo preview, también pueden usarse nombres de regla como unused-import en lugar de códigos como F401
  • La especificación completa de comentarios está resumida en la documentación del linter de Ruff

Diff de correcciones y formatos de salida

  • check y format ya admitían --diff, pero funcionaba por separado del diagnóstico normal, por lo que no se mostraba junto con el diagnóstico que explicaba la razón de la corrección
  • La salida full predeterminada de v0.16 muestra las posibles correcciones del linter y del formateador como diff debajo del diagnóstico
  • format --check también puede usar todos los formatos de salida que admite el linter
    • Puede generar JSON legible por máquinas
    • Puede emitir formatos que GitHub y GitLab renderizan como comentarios en CI
  • Los formatos admitidos pueden consultarse en la ayuda del CLI y en la documentación de output format

Compatibilidad y estabilización

  • Los cambios incompatibles de v0.16 son pocos, por lo que la mayoría podrá actualizar sin modificar mucho el código o la configuración
  • En la salida JSON, filename, location, end_location, fix.edits[].location y fix.edits[].end_location ahora pueden ser null en lugar de usar como valor predeterminado una cadena vacía o la línea 1, columna 1
    • Actualmente son muy pocos los diagnósticos afectados, pero esto podría volverse más común en reglas futuras
  • 12 reglas pasan de preview a estado estable
    • Compatibilidad de firmas de funciones de Airflow 3 AIR303, aviso de copyright CPY001, conversión de float FURB164, min/max ordenados FURB192
    • Concatenación de cadenas en literales de colección ISC004, registro de excepciones fuera de un exception handler LOG004, tipo de retorno bool incorrecto PLE0304
    • Exceso de argumentos posicionales PLR0917, retorno de StopIteration PLR1708, posición de None dentro de Union RUF036
    • Acceso a annotation en diccionarios de clase RUF063, elementos duplicados en __all__ RUF068
  • También se aplican por defecto los comportamientos estabilizados de algunas reglas existentes
    • BLE001 también se suprime cuando se registra una excepción con métodos de logging distintos de critical, error y exception
    • FA102 revisa APIs adicionales compatibles con PEP 585 como collections.abc
    • INT001·INT002·INT003 también revisan patrones de uso comunes como asignar gettext a builtins._
    • S310 interpreta bindings locales de literales de cadena para reducir falsos positivos
    • S508·S509 admiten la API recomendada en las versiones recientes de PySNMP
    • UP019 reconoce no solo typing.Text, sino también typing_extensions.Text
  • Todos los cambios pueden consultarse en el lanzamiento de GitHub

1 comentarios

 
GN⁺ 1 시간 전
Comentarios en Hacker News
  • Actualicé un proyecto de Python de unas 3 mil líneas de v0.15.x a la nueva versión; no tomó mucho tiempo, y además encontró muchos problemas que la versión anterior pasaba por alto, así que también mejoró la calidad del código
    Correcciones manuales según las sugerencias: https://github.com/nickjj/plutus/commit/9af66d31f98bef841588...
    Reactivar la regla de longitud de línea: https://github.com/nickjj/plutus/commit/21789f89bbcee37913c1...
    Forzar prefijo _ en variables no usadas: https://github.com/nickjj/plutus/commit/a272c77b932e1c78558a...
    Correcciones automáticas de Ruff: https://github.com/nickjj/plutus/commit/6fe69cf88385ebdf9b8c...

  • Da gusto ver que Ruff, ty y uv siguen desarrollándose activamente incluso después de que Astral fuera adquirida por OpenAI

    • Tenía expectativas con ty, pero se quedó muy por detrás de basedpyright, así que al final dejé de usarlo. Más que la falta de chequeos, lo grave eran los falsos positivos, y además tampoco tenía una función de línea base (baselining) muy útil para codebases grandes
      uv y Ruff son excelentes, y ojalá ty llegue a ese nivel algún día
  • Sorprende ver tanto entusiasmo por unas herramientas de policía sintáctica que implementan reglas arbitrarias entre sí y ni siquiera logran ponerse de acuerdo sobre qué es buen código Python
    Juntan en una sola línea diccionarios multilínea y arruinan la intención de los comentarios, mientras solo corrigen detalles triviales de formato como dos espacios o comillas dobles. El problema real no son los espacios al final de línea ni el orden de los imports, sino una list comprehension de 10 líneas difícil de entender, y esas herramientas no la detectan
    En el trabajo usábamos pylint, flake8, black y Ruff, y cada cambio generaba cientos de commits; esa energía habría sido mejor gastarla en otra cosa

    • El objetivo de estas herramientas es centrarse en los problemas reales. Si automatizas las decisiones del linter, no hace falta malgastar energía mental discutiendo formato en los PR
      Si simplemente aceptas el resultado automático, puedes desentenderte de esas discusiones, pero en las organizaciones sin estas herramientas sí terminábamos gastando tiempo real pensando y debatiendo sobre el formato
    • El rechazo a la idea de que los linters hacen perder tiempo parece ir más allá de lo razonable. En la práctica hay suficiente evidencia de que más bien ahorran tiempo; al final, el punto parece ser solo que a veces el linter hace cambios que no te gustan
      En desarrollo en equipo, en vez de imponer con fuerza preferencias personales, hace falta escuchar a los demás y revisar las prioridades entre colaboración y artesanía
    • Ruff reconoce los comentarios al final de línea y mantiene cada elemento en una línea separada, añadiendo solo comas finales y espacios. Si hay una coma final, tampoco junta las líneas; el resultado mostrado parece venir de Black
      Si hay comentarios de línea, juntar las líneas parece inapropiado. Las comillas dobles son simplemente una elección de estilo en Python, y si dentro de comillas simples hay comillas dobles, Ruff también lo deja tal cual
    • Estas herramientas más bien ahorran energía al equipo. Sin ellas, cada desarrollador tiene criterios distintos sobre formato, calidad del código y legibilidad, así que las discusiones se vuelven interminables; mejor dejarlo en manos de Ruff
    • Se juntó en una sola línea porque faltó una coma después del último elemento. Al menos en Black, si mantienes la coma final los elementos no se compactan, pero como rompe muy seguido el formato intencional, ya no lo conecto a mi código
  • Ojalá existiera una herramienta como Ruff para Go. Hay grandes herramientas en varios lenguajes, pero el ecosistema de Go está disperso y no hay nada que se sienta tan pulido como Ruff, Oxc, Biome o Mago

    • Go tiene un Go Analysis Framework mejor: https://pkg.go.dev/golang.org/x/tools/go/analysis
      Es relativamente nuevo y menos conocido, pero es la base de go fix y go vet, y parece que el equipo de Go está trabajando para que los autores de módulos puedan definir fácilmente pases de análisis personalizados que se ejecuten automáticamente al correr go fix
      Con la estructura analysis.Analyzer puedes acceder a información de AST, tipos y SSA, y combinar información entre analizadores; si lo compilas como binario y se lo pasas a go fix, la toolchain se encarga de cosas complejas como el caché. Como lo hizo directamente el equipo de Go y está incluido en la toolchain, es muy probable que herramientas como golangci-lint también terminen integrándose a este framework a largo plazo
      También podrías pedirle a un agente de IA que escriba un analizador de Go Analysis y lo ejecute con go fix; en mis proyectos también lo uso para imponer automáticamente varias reglas de forma determinista, en vez de depender de instrucciones imprecisas en Markdown
    • Hasta hace poco el ambiente era exactamente el contrario: la comunidad de Python sufría por la falta de herramientas y todos querían un gofmt para Python. Ruff no es un formateador sino un linter, pero da gusto ver la dirección reciente del ecosistema Python
    • No termino de entender eso de que el ecosistema de Go esté disperso. Go tiene herramientas oficiales de formateo y linting, y el propio lenguaje está intencionalmente restringido para que, incluso si lo escribe un principiante, quede con una forma relativamente consistente
      A diferencia de Python o TypeScript, donde las herramientas oficiales no imponen cierto estilo, en Go es difícil que aparezca un efecto tan dramático como cuando usas Ruff o Biome por primera vez
    • Go tiene uno de los mejores ecosistemas de herramientas de lenguaje que se pueden usar sin un IDE enorme, y golangci-lint también es bastante completo. La propia distribución de Go ya resuelve gran parte del problema
    • golangci-lint existe desde hace mucho y se usa ampliamente
  • Activar 413 reglas por defecto es un buen cambio porque la mayoría de los proyectos podrá recibir linting útil sin tocar la configuración

    • No está claro si es realmente útil que en proyectos existentes aparezcan de golpe 413 advertencias potenciales; parece más adecuado para proyectos nuevos
      Hoy en día probablemente podrías pedirle a un agente que corrija todas las advertencias de lint según criterios definidos y dejarlo unas horas hasta que lo resuelva, pero aun así se agradece el diagnóstico detallado que ofrece Ruff
  • Ruff también necesita algo como stateVersion de Nix para decidir el conjunto de valores predeterminados que se aplicará. Si actualizas Ruff en varios repositorios, cada vez que se agregan nuevas reglas por defecto tienes que desactivarlas de inmediato o corregir las infracciones, así que el resultado se vuelve difícil de predecir
    También podrías poner en una lista de permitidos todas las reglas que quieras activar, pero es mejor mantener la configuración simple y subir la versión de estado solo cuando todos puedan dedicarle unas horas

    • Fijar la versión de Ruff deseada en pyproject.toml para cada proyecto parece más apropiado. Así cada proyecto puede subir de versión cuando esté listo, sin necesidad de coordinar varios proyectos al mismo tiempo, y aunque uno se retrase no bloquea a los demás
    • Según el texto original, el conjunto de reglas por defecto de Ruff no había cambiado en más de 2 años, y el último cambio fue en v0.1.0
      https://github.com/astral-sh/ruff/releases/tag/v0.1.0
  • En la era de la codificación con agentes, un linting fuerte es más importante que nunca, y me gustaría ver herramientas como forbidigo en más lenguajes

    • Estoy actualizando proyectos, pero tengo sentimientos encontrados. Cuando escribía código directamente, podía decidir por intuición cuándo saltarme o ignorar una regla, pero en proyectos con la mayoría de las reglas de pylint activadas, el código terminó siendo más difícil de leer por los atajos usados solo para satisfacer a pylint
      Los agentes de código también gastan muchos tokens corrigiendo problemas menores o incluso lo desactivan por completo cuando fallan las pruebas. He llegado a confiar en la precisión general de los resultados de la IA, pero sigue siendo difícil confiar en su criterio sobre la calidad del código
  • Aunque haya 413 reglas, cada vez que me sumo a una nueva base de código terminamos repitiendo las mismas tres discusiones sobre cómo ordenar los imports

  • Me alegra que ahora parezca recomendarse el uso sin configuración. En un nuevo .ruff.toml bastaría con dejar line-length = 300