Python con poesía parte 2

6 min(s) Fecha: 2022-02-16

Una forma sencilla de gestionar las dependencias en tu proyecto.

En la entrada anterior, realizamos la instalación de Poetry, definimos la configuración para nuestro proyecto, así como las depedencias principales, de desarrollo, y las opcionales.

Teniendo el archivo pyproject.toml, podemos integrar en el mismo, diferentes configuraciones, para diversas herramientas, a continuación, algunas de ellas.



Configuración para Black

Black es un formateador que cumple con PEP 8 y tiene su propio estilo. Hace que la revisión del código sea más rápida, al producir las menores diferencias posibles. El código tendrá el mismo aspecto independientemente del proyecto que estés leyendo.

Podemos utilizar nuestro archivo pyproject.toml, para agregar la configuración de Black.

Ejemplo:

# definimos la seccion apropiada
[tool.black]
line-length = 120
target-version = ['py39']
required-version = "21.10b0"
# fix this
# skip-string-normalization = true
exclude = '''
^/(
  (
      \.eggs         # exclude a few common directories in the
    | \.git          # root of the project
    | \.idea
    | \.vscode
    | \.hg
    | \.mypy_cache
    | \.tox
    | \.venv
    | \.env
    | venv
    | env
    | test
    | _build
    | buck-out
    | build
    | dist
    | tools
  )/
  | foo.py
)
'''

Configuración para isort

isort es una utilidad/biblioteca de Python para ordenar las importaciones alfabéticamente, y automáticamente separadas en secciones y por tipo. Requiere Python 3.6+.

Podemos utilizar nuestro archivo pyproject.toml, para agregar la configuración de isort.

Ejemplo:

# definimos la seccion apropiada
[tool.isort]
py_version = 39
profile = "black"
# multi_line_output = 0
line_length = 120
lines_between_sections = 0
include_trailing_comma = true
ensure_newline_before_comments = true

Configuración para Bandit

Bandit es una herramienta diseñada para encontrar problemas de seguridad comunes en el código de Python. Para ello, Bandit procesa cada archivo, construye un AST a partir de él, y ejecuta los plugins apropiados contra los nodos del AST. Una vez que Bandit ha terminado de escanear todos los archivos, genera un informe.

Podemos utilizar nuestro archivo pyproject.toml, para agregar la configuración de Bandit.

Ejemplo:

# definimos la seccion apropiada
[tool.bandit]
# tests = ["B201", "B301"]
# skips = ["B101", "B601"]
skips = ["B105"]

Scripting con Poetry

En muchos proyectos de Python, es común utilizar la línea de comandos para generar crud`s (scaffold), formatear el codigo, generar la documentación, hacer un despliegue en una plataforma cloud etc...

Poetry nos ayuda en estas labores con una sección llamada tool.poetry.scripts

Para este ejemplo tenemos una carpeta llamada tools, pero puede llamarse como quieras, en ella podemos tener varios archivos de código Python que ejecutan diversas tareas, la estructura actual de nuestro proyecto se ve así:

awesome-project
├── main.py
├── pyproject.toml
└── tools
    └── scripts.py

Agregamos las entradas para nuestros scripts, al archivo pyproject.toml.

Ejemplo:

[tool.poetry]
# la llave "packages" permite a Poetry encontrar
# nuestra carpeta "tools" y los scripts que contenga.
packages = [
    { include="tools", from="." },
]

[tool.poetry.scripts]
fmt = "tools.scripts:format"
sort = "tools.scripts:sort"
audit = "tools.scripts:audit"
test = "tools.scripts:test"

Para invocar a un script, se debe seguir el formato: "carpeta.archivo:funcion"

Ejemplo:

fmt = "tools.scripts:format"

Nuestro archivo scripts.py luce de la siguiente manera:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import subprocess
import sys

def test():
    print("hola mundo desde Poetry")
    print("esto es un script de ejemplo.")
    print()

def format():
    try:
        cmd = ["black", "main.py"]
        subprocess.run(cmd)
    except KeyboardInterrupt as e:
        print("> Command killed by keyboard interrupt...")
    except Exception as e:
        print(e.output)
        sys.exit(1)

def sort():
    try:
        cmd = ["isort", "--profile", "black", "--atomic", "."]
        subprocess.run(cmd)
    except KeyboardInterrupt as e:
        print("> Command killed by keyboard interrupt...")
    except Exception as e:
        print(e.output)
        sys.exit(1)

def audit():
    try:
        cmd = ["bandit", "-r", "main.py"]
        subprocess.run(cmd)
    except KeyboardInterrupt as e:
        print("> Command killed by keyboard interrupt...")
    except Exception as e:
        print(e.output)
        sys.exit(1)

Las funciones "format", "sort" y "audit", hacen uso del modulo subprocess de Python para ejecutar los comandos black, isort y bandit con variedad parámetros, para cada herramienta respectivamente.

En este punto, llevamos lo siguiente:

Recuerda que actualmente (Poetry 1.x) no es posible ejecutar librerías o herramientas directamente desde la configuración de scripts.

Ejemplo:

[tool.poetry.scripts]
# esto genera un error al ejecutarse
serve = "flask run"

Si se intenta ejecutar el comando:

 poetry run serve

Generará un error.

En el caso de querer lanzar un servidor local, como gunicorn o flask, basta con crear un script como los vistos anteriormente.
-- un visitante del blog

...Ó seguir leyendo 👇



Ejecutar scripts desde Poetry

Para hacer uso de los scripts creados con anterioridad, escribimos lo siguiente:

 poetry run [script]

Ejemplo:

# Ejecutar el script fmt (black)
 poetry run fmt

# Ejecutar el script sort (isort)
 poetry run sort

# Ejecutar el script audit (bandit)
 poetry run audit

# Ejecutar el script test (hola mundo)
 poetry run test

Podemos naturalmente ejecutar estas herramientas ó librerías que instales en tu proyecto, directamente, haciendo uso de Poetry:

 poetry run [librería/herramienta] [lista de parámetros]

Ejemplo:

# Ejecutar Black directamente.
 poetry run black main.py

# Ejecutar isort directamente
 poetry run isort --profile black --atomic .

# Ejecutar bandit directamente.
 poetry run bandit .

# Ejecutar Flask directamente.
 poetry run flask run

Diseñar scripts para ejecutar con Poetry, al menos en un inicio, puede que no sea la mejor solución, pero cuando tengas muchas cosas por hacer, después de programar tu proyecto, verás la utilidad y potencia que ofrece, entre otras cosas:

Sí conoces nodeJS, los scripts en Poetry son el equivalente a npm scripts


En resumen, el archivo pyproject.toml es mágico, pero... algunos sistemas y plataformas (heroku, gcp etc...) aún siguen dependendiendo del archivo 'requirements.txt' ¿en ese caso que debería hacer?
-- un visitante del blog

Leer la siguiente sección 👇 de este artículo. 😄


Poetry y el archivo requirements.txt

Para el caso en que necesites el archivo requirements.txt, Poetry provee un comando para generarlo a partir de la sección tool.poetry.dependencies del archivo pyproject.toml.

Ejemplo:

# Generar el archivo requirements.txt
 poetry export -f requirements.txt --output requirements.txt --without-hashes

# ó de la forma
 poetry export -f requirements.txt --output requirements.txt

Una vez generado, te recomendamos, que no lo modifiques directamente, si agregaste librerías, las actualizaste ó las eliminaste de tu proyecto, utiliza:

 poetry install
# ó
 poetry update

para actualizar la lista de dependencias y luego, volver a generar el archivo.


En la próxima entrada, hablaremos sobre el uso de plugins, y librerías de utilidad y otras hierbas.


Referencias