Guía de estilo, formato y linters de código de paquetes de Python#
Resumen
pyOpenSci requiere que los autores sigan las directrices de formato de código PEP 8
Configurar formateadores de código como Black e isort le ayudará a cumplir con las directrices de estilo PEP 8 y también con un formato de código consistente y legible
Algunas herramientas comúnmente utilizadas son: Black, Isort, flake8, Ruff
También puede configurar pre-commit hooks que ejecutará los formateadores de código localmente cada vez que realice un commit.
precommit.ci es un bot que puede agregar a su repositorio de GitHub. Aplicará automáticamente el formato de código a cada PR usando las herramientas especificadas en su archivo pre-commit-config.yaml. Puede ahorrar mucho tiempo y facilitar las contribuciones para los nuevos colaboradores.
¡La automatización es buena! Al hacer que las herramientas de calidad de código se ocupen de su código, puede centrarse en tareas estructurales y de gran valor.
Un formato y estilo de código consistentes son útiles tanto para su paquete como para todo el ecosistema de Python científico, ya que el uso de formatos similares hace que el código sea más fácil de leer.
Por ejemplo, si viera una frase como esta sin espacios ni puntuación, le llevaría más tiempo procesarla.
forinstanceifyousawasentencelikethisonewithoutany...
El proceso de revisión por pares de pyOpenSci requiere que siga las reglas de formato estándar establecidas por Python PEP 8 lo máximo posible.
pyOpenSci no requiere que utilice una herramienta de formato de código específica. Sin embargo, buscamos consistencia y legibilidad en el estilo de código. A continuación, encontrará una discusión sobre:
Los beneficios de linters y herramientas de formato de código en su flujo de trabajo
Algunas herramientas comúnmente utilizadas en el espacio de Python científico
Configuración de pre-commit hooks y el bot pre-commit.ci para facilitar el uso de herramientas de formato de código en flujos de trabajo diarios y en pull requests en GitHub.
Use una herramienta de formato de código (o herramientas) para facilitar el desarrollo#
Sugerimos que utilice una o varias herramienta de formato de código porque aplicar manualmente todas las normas PEP 8 consume mucho tiempo para los mantenedores y puede ser un obstáculo para los nuevos colaboradores. Los formateadores de código reformatearán automáticamente su código, siguiendo las normas PEP 8 y aplicando decisiones de estilo consistentes en todo el código.
Establecer un conjunto de herramientas de formato de código hará que:
Ahorre tiempo, a usted y a su equipo de mantenimiento, en corregir inconsistencias de PEP 8.
Asegurese de que el formato y el estilo sean consistentes en todo su código.
Evite discusiones largas con colaboradores y otros mantenedores sobre preferencias de formato de código personalizadas durante las revisiones.
Evite ediciones puramente visuales en el código para que las revisiones de código se centren en el valor añadido
Muchos paquetes utilizan un conjunto de herramientas para aplicar reglas de formato de código, eliminando la necesidad de implementar manualmente los requisitos de formato de código.
Un formato de código consistente en los paquetes dentro del ecosistema de Python (científico) también hará que el código sea más fácil de entender y contribuir.
Linting vs. format and style#
Antes de profundizar, aclaremos algunas definiciones.
Code linting#
Un linter de código es una herramienta que revisará su código e identificará errores o problemas. Un linter normalmente no modifica su código. Le dirá cuál es el error y en qué línea se descubrió. Flake8, discutido a continuación, es un ejemplo de un linter de código comúnmente utilizado.
Code formatters (and stylers)#
Los formateadores de código reformatearán su código por usted. Aquellos formateadores de código enfocados en Python suelen seguir las normas PEP 8. Sin embargo, también toman decisiones estilísticas sobre la consistencia del código.
Black es un ejemplo de un formateador de código comúnmente utilizado. Black aplica tanto las normas PEP 8 como otras decisiones sobre cosas como el uso consistente de comillas dobles para cadenas de caracteres y el espaciado de elementos en listas.
Aprenderá más sobre Black a continuación.
Linting, formateo y estilización de código#
Black#
Black es un formateador de código. Black corregirá automáticamente (y sin disculpas) problemas de espaciado y asegurará que el formato del código sea consistente en todo su paquete. Black también se adhiere de forma general a las directrices de estilo PEP 8 con algunas excepciones. A continuación, se muestran algunos ejemplos de esas excepciones:
Black se ajusta por defecto a una longitud de línea de 88 (79 + 10%) en lugar de la especificación de 79 caracteres de
PEP 8. Sin embargo, la longitud de línea es una configuración que se puede sobrescribir manualmente en su configuración de Black.Black no ajustará la longitud de línea en sus comentarios o docstrings.
Esta herramienta no revisará ni corregirá el orden de importación (necesita
isortoruffpara hacerlo - ver más abajo).
Truco
Si está interesado en ver cómo Black formateará su código, puede utilizar el playground de Black.
Usar un formateador de código como Black le dejará más tiempo para trabajar en la función del código en lugar de preocuparse por el formato.
Flake8#
Para adherirse a las normas de formato pep8 de Python, es posible que desee agregar flake8 a su caja de herramientas de formato de código.
flake8 hará lo siguiente:
Marcará cada línea de su código que se extienda más allá de 79 caracteres (incluyendo las de docstrings y comentarios)
Marcará problemas de espaciado que entran en conflicto con las directrices PEP 8, como la falta de espacios después de las comas
Flake8 también marcará importaciones no utilizadas y variables declaradas no utilizadas en sus módulos.
A continuación, puede ver la salida de ejecutar flake8 filename.py en la línea de comandos para un archivo de Python dentro de un paquete llamado stravalib.
La longitud de línea estándar para PEP 8 es de 79 caracteres.
Observe que flake8 devuelve una lista de problemas que encontró en el módulo model.py a través de la línea de comandos. El archivo Python no se modifica. Utilizando esta salida de la terminal, puede arreglar cada problema manualmente, línea a línea.
(stravalib-dev) username@computer stravalib % flake8 stravalib/model.py
stravalib/model.py:8:1: F401 'os' imported but unused
stravalib/model.py:29:80: E501 line too long (90 > 79 characters)
stravalib/model.py:34:80: E501 line too long (95 > 79 characters)
stravalib/model.py:442:80: E501 line too long (82 > 79 characters)
stravalib/model.py:443:39: E231 missing whitespace after ','
stravalib/model.py:493:20: E225 missing whitespace around operator
stravalib/model.py:496:80: E501 line too long (82 > 79 characters)
Isort#
Llamamos importaciones de Python a los paquetes de Python que un módulo en su paquete requiere. Las importaciones siempre deben estar ubicadas en la parte superior de cada módulo de Python en su paquete.
PEP 8 tiene estándares específicos para el orden de estas importaciones. Estos estándares se enumeran a continuación:
Las importaciones deben agruparse en el siguiente orden:
Importaciones de la librería estándar.
Importaciones de terceros.
Importaciones específicas de la aplicación/librería local.
Si bien flake8 identificará importaciones no utilizadas en su código, no corregirá ni identificará problemas con el orden de las importaciones de paquetes.
isort identificará dónde están desordenadas las importaciones en su código. Luego modificará su código, reordenando automáticamente todas las importaciones. Esto le deja con una cosa menos en la que pensar al limpiar su código.
Ejemple de uso de isort#
Importaciones de código antes de ejecutar isort:
A continuación, pandas es un paquete de terceros, typing es un paquete de la librería estándar distribuido con Python, y examplePy.temperature es un módulo propio, lo que significa que pertenece al mismo paquete que el archivo que realiza la importación. También observe que no hay espacios en las importaciones listadas a continuación.
from typing import Sequence
import pandas
from examplePy.temperature import fahrenheit_to_celsius
Desde la raíz del proyecto, ejecute:
isort src/examplePy/temporal.py
Importaciones del archivo de Python temporal.py después de ejecutar isort
from typing import Sequence
import pandas
from examplePy.temperature import fahrenheit_to_celsius
Ruff#
Ruff es una nueva incorporación al ecosistema de calidad de código, que ha ganado cierta tracción desde su lanzamiento. ruff es tanto un linter como un formateador de código para Python, con el objetivo de reemplazar varias herramientas detrás de una única interfaz. Como tal, ruff puede ser utilizado como reemplazo de todas las demás herramientas mencionadas aquí, o en complemento a algunas de ellas.
ruff tiene algunas características interesantes que lo distinguen de otros linters:
Configuración del linter en
pyproject.tomlCientos de reglas incluidas, muchas de las cuales son automáticamente corregibles
Explicación de las reglas, consulte F403 para un ejemplo
Tiempo de ejecución rápido, permite un ciclo de retroalimentación rápido incluso en proyectos grandes.
Aquí hay una configuración simple para comenzar con ruff. Se colocaría en su pyproject.toml.
[tool.ruff]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes. "E" + "W" + "F" + "C90" (mccabe complexity) is equivalent to flake8
"I", # isort
]
Dependiendo de su proyecto, es posible que desee agregar lo siguiente para ordenar las importaciones correctamente:
[tool.ruff.isort]
known-first-party = ["examplePy"]
Como usar un formateador de código en su flujo de trabajo local#
Linters, formateadores de código y sus herramientas de codificación favoritas#
Los linters se pueden ejecutar como una herramienta de línea de comandos como se muestra arriba. También se pueden ejecutar dentro de su editor de código favorito (por ejemplo, VScode, pycharm, etc). Por ejemplo, es posible que prefiera que herramientas como Black e isort se ejecuten cuando guarda un archivo. En algunos editores también puede configurar accesos directos que ejecuten sus herramientas de formato de código favoritas bajo demanda.
Use pre-commit hooks para ejecutar formateadores de código y linters en los commits#
También puede configurar un pre-commit hook en el repositorio de su paquete.
Un pre-commit hook es una herramienta que permite ejecutar una acción (o acciones) en el momento en que se aplica un commit a su repositorio de git.
Ejemlo de flujo de trabajo de pre-commit hook#
El flujo de trabajo de precommit se ve así: escriba y ejecute:
git commit -m "mensaje aquí" en la línea de comandos
Una vez que presione enter, pre-commit ejecutará cualquier herramienta que haya configurado en un archivo .pre-commit-config.yaml.
Si las herramientas configuradas en el pre-commit hook se ejecutan con éxito sin realizar cambios o encontrar errores en su código, el commit se aplicará al repositorio.
Si las herramientas configuradas en el hook encuentran errores en sus archivos, el commit NO se aplicará al repositorio. Recuerde de la discusión anterior que un formateador de código como Black se ejecutará y reformateará su código. Un linter como flake8 le proporcionará una salida que detalla dónde hay problemas de sintaxis en su código. Luego deberá corregir esos problemas, manualmente.
Una vez que se aplican todas las correcciones, puede volver a agregar (stage) los archivos para ser commiteados. Y vuelva a ejecutar su commit.
The pre-commit workflow begins with you adding files that have changes to be staged in git. Next, you’d run git commit. When you run git commit, the pre-commit hooks will then run. In this example, Black, the code formatter and flake8, a linter both run. If all of the files pass Black and flake8 checks, then your commit will be recorded. If they don’t, the commit is canceled. You will have to fix any flake8 issues, and then re-add / stage the files to be committed. Image Source#
Importante
Si tiene una base de código de Python y varios mantenedores trabajando activamente en el código, y tiene la intención de ejecutar una herramienta como Black, asegúrese de coordinarse con su equipo. Un commit inicial que aplique Black a todo su paquete probablemente cambiará una cantidad significativa de su código. Esto podría llevar a conflictos de fusión en solicitudes de cambio abiertas y nuevas antes de que los nuevos cambios se fusionen.
Comprobaciones generales de pre commit#
Además de llamar a herramientas, Pre-commit también tiene un conjunto de hooks de formato integrados que puede llamar. Algunos, como trailing-whitespace, también pueden ser útiles para agregar a su flujo de trabajo de pre-commit para garantizar archivos de código limpios y simplificados.
A continuación se muestra un archivo pre-commit-config.yaml con ejemplos de cómo se configura.
Pre-commit.ci#
Pre-commit.ci es un bot que puede convertirse en su nuevo mejor amigo. Este bot, cuando se configura en un repositorio, se puede configurar para hacer lo siguiente:
Comprobará cada solicitud de cambio utilizando todas las configuraciones de pre-commit hook
Si lo desea, también enviará una solicitud de cambio a su repositorio con correcciones de pre-commit, ahorrándole a usted y a los nuevos colaboradores el tiempo de reformatear una solicitud de cambio que tenga problemas de formato.
También puede llamar al bot en cualquier solicitud de cambio para ejecutar / y corregir el código.
El bot pre-commit.ci utiliza el mismo archivo pre-commit-config.yaml que utiliza para configurar pre-commit localmente.
Configurar un bot como este puede ser valioso porque:
Puede hacer que sea más fácil para los mantenedores, ya que ya no tienen que preocuparse por corregir el formato del código. El bot hará el trabajo por ellos.
Puede hacer que sea más fácil para los recién llegados, ya que nunca tienen que configurar pre-commit localmente o preocuparse por el linting de su código. Incluso pueden hacer pequeñas correcciones al código directamente en GitHub sin preocupaciones.
Configuración de un pre-commit hook de git#
Para configurar pre-commit localmente, necesita hacer 3 cosas:
Instalar pre-commit (e incluirlo como un requisito de desarrollo en su repositorio)
python -m pip install pre-commit
# or
conda install -c conda-forge pre-commit
Cree un archivo .pre-commit-config.yaml en la raíz del directorio de su paquete.
A continuación se muestra un ejemplo de archivo .pre-commit-cofig.yaml que se puede utilizar para configurar el pre-commit hook y el bot pre-commit.ci si elige implementarlo también.
repos:
- repo: https://github.com/PyCQA/isort
rev: dac090ce4d9ee313d086e2e89ab1acb8c2664fa1 # frozen: 9.0.0a3
hooks:
- id: isort
files: \.py$
# Misc commit checks using built in pre-commit checks
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0
# ref: https://github.com/pre-commit/pre-commit-hooks#hooks-available
hooks:
# Autoformat: Makes sure files end in a newline and only a newline.
- id: end-of-file-fixer
# Lint: Check for files with names that would conflict on a
# case-insensitive filesystem like MacOS HFS+ or Windows FAT.
- id: check-case-conflict
- id: trailing-whitespace
# Linting: Python code (see the file .flake8)
- repo: https://github.com/PyCQA/flake8
rev: c48217e1fc006c2dddd14df54e83b67da15de5cd # frozen: 7.3.0
hooks:
- id: flake8
# Black for auto code formatting
- repo: https://github.com/psf/black
rev: c6755bb741b6481d6b3d3bb563c83fa060db96c9 # frozen: 26.3.1
hooks:
- id: black
language_version: python3.10
# Tell precommit.ci bot to update code format tools listed in the file
# versions every quarter
# The default it so update weekly which is too many new pr's for many
# maintainers (remove these lines if you aren't using the bot!)
ci:
autoupdate_schedule: quarterly
Este archivo especifica un hook que se activará automáticamente antes de cada git commit; en este caso, especifica flake8 con la versión 6.0.0.
Instale su pre-commit hook(s) usando
pre-commit install. Esto instalará todos los hooks especificados en el archivo yaml de pre-commit en su entorno.
Una vez que haya hecho lo anterior, estará listo para comenzar a trabajar en su código. Pre-commit se ejecutará cada vez que ejecute git commit.
Resumen#
pyOpenSci sugiere configurar un linter y un formateador de código para su paquete, independientemente de si utiliza pre-commit hooks, CI u otra infraestructura para administrar el formato de código. Configurar estas herramientas le dará comentarios automáticos sobre la estructura de su código a medida que usted (o un colaborador) lo escribe. Y usar una herramienta como black que formatee el código por usted, reduce el esfuerzo que necesita hacer en torno a las decisiones sobre el formato y estilo del código.