Poetry: Adiós a los malditos entornos virtuales

Históricamente en el mundo Python uno de los mayores dolores de cabeza ha sido la creación y gestión de entornos virtuales para poder controlar qué versión de Python ejecutar e instalar nuestras dependencias. Por suerte esto es cosa del pasado gracias a herramientas como Poetry.

¿Qué es Poetry?

Poetry es una herramienta para manejar y controlar las dependencias de nuestro proyecto. Muy similar a npm, Poetry nos va a generar un fichero poetry.lock que nos va a permitir “congelar” las versiones de nuestras dependencias.

Su principal ventaja, para mí, es que de forma completamente transparente crea un entorno virtual con la versión de Python específica que le indiquemos donde instala todas las dependencias dejando nuestro ordenador limpio sin cientos de paquetes instalados en nuestro local.

Cómo instalar Poetry

Simplemente ejecutando el siguiente comando instalaremos Poetry en nuestra máquina:

curl -sSL https://install.python-poetry.org | python3 -

Una vez finalizada la instalación podemos comprobar que todo ha ido bien con el siguiente comando poetry --version.

Crear un proyecto con Poetry

Crear un proyecto con Poetry es realmente sencillo, basta con escribir en nuestra terminal poetry new weather_app.

Cuando la creación haya finalizado tendremos una carpeta weather_app con la siguiente estructura:

|-- README.md
|-- pyproject.toml
|-- tests
|   |-- __init__.py
|-- weather_app
    |-- __init__.py

Posteriormente hablaremos del pyproject.toml el fichero más importante de Poetry.

Añadir Poetry a un proyecto existente

Si por el contrario el proyecto ya existe es muy sencillo añadirle Poetry, simplemente ejecutando desde nuestra terminal y dentro del directorio del proyecto poetry init y respondiendo una serie de preguntas se generará el fichero pyproject.toml.

pyproject.toml

Configurar Poetry

Como decía antes toda la configuración de Poetry está alojada en el fichero pyproject.toml.

En dicho fichero vamos a poder configurar:

  • Nuestra versión de Python.
  • Nuestras dependencias con sus versiones correspondientes.
  • Nuestras herramientas como yapf o flake8.

Os dejo por aquí como sería una estructura básica de este fichero:

[tool.poetry]
name = "weather app"
version = "0.1.0"
description = ""
authors = []
...

[tool.poetry.dependencies]
python = "^3.10"
...

[tool.poetry.dev-dependencies]
pytest = "^7.2.0"
...

[tool.flake8]
...

[tool.yapf]
...

[build-system]
...

Como podemos ver esta forma de centralizar todo nuestra configuración es mucho más mantenible y fácil de consultar respecto a tener múltiples ficheros de configuración, uno por herramienta como se hacía históricamente.

Cómo añadir dependencias

La gestión de dependencias siempre es un problema cuando estás trabajando en un proyecto, tanto por mantenerlas como por actualizaciones inesperadas.

Por ello Poetry nos permite instalar dependencias de una forma supersencilla tanto para código de producción poetry add kubernetes como para dependencias únicamente para la fase de desarrollo poetry add -D pytest.

Para no tener problemas con las versiones de nuestras dependencias Poetry usa semantic versioning y además nos genera un fichero llamado poetry.lock que podremos incluir en nuestro repositorio garantizando que cualquier persona que se clone el repo use las mismas versiones en nuestras dependencias.

No se vosotras pero para mi tener que crear un requirements.txt siempre me pareció un horror y una mala práctica a la hora de trabajar, además de que solo podías utilizar versiones concretas lo que hacía que nuestros proyectos se quedarán obsoletos rápidamente.

Cómo usar Poetry

Una vez que tenemos Poetry instalado puede ser un poco confuso como ejecutar algunas librerías que tenemos instaladas (como por ejemplos pytest o mypy). Simplemente necesitamos ejecutar poetry run XXXX siendo XXXX nuestro comando:

 poetry run pytest . -ra
 poetry run mypy .
 poetry run flake8 .
 poetry run pylint ./*
 poetry run yapf --diff --recursive **/*.py

Con poetry run vamos a tener acceso a todas las dependencias como si estuviéramos dentro de nuestro entorno virtual.

Dockerizar una app con Poetry

Un caso muy habitual para quienes desarrollamos en Python es tener múltiples proyectos cada uno con una versión diferente, uno con 3.8 y otro con 3.10. Si queremos tener instaladas múltiples versiones en la misma máquina existen herramientas como ASDF que nos facilitan la vida un montón.

Pero mi recomendación es ir un paso más allá y no tener que instalar nada en nuestra máquina haciendo uso de Docker. De esta forma podemos dockerizar nuestra app creando un fichero Dockerfile usando la imagen de Python con la versión concreta, instalar Poetry y dejar todas nuestras dependencias encapsuladas dentro de nuestra imagen:

FROM python:3.10

WORKDIR /code

COPY pyproject.toml /code

RUN pip install poetry

RUN poetry install

COPY . /code

Usar Poetry en un CI/CD (ex: GitHub Actions)

De forma muy similar a como dockerizamos nuestra app podemos generar una pipeline con nuestro CI/CD de confianza, por simplicidad voy a usar GitHub Actions.

Os dejo por aquí un ejemplo muy sencillo, sacado de este repositorio

name: weather_app
on:
  push:
    branches: [ "main" ]
permissions:
  contents: read
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: Set up Python 3.10
      uses: actions/setup-python@v3
      with:
        python-version: "3.10"
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install poetry
        poetry install        
    - name: Check style, format and lint
      run: |
        poetry run mypy .
        poetry run yapf --diff --recursive **/*.py
        poetry run flake8 .                                                     
        poetry run pylint ./*         
    - name: Tests
      run: poetry run pytest -n auto

Si queréis ver un proyecto más complejo (usando docker-compose) os dejo por aquí otro fichero de configuración.

Conclusiones

Como hemos podido ver en la actualidad existen alternativas como Poetry para manejar de forma mucho más cómoda el estado de nuestros proyectos, permitiéndonos no tener que instalar cientos de paquetes en nuestras máquinas y asegurando que nuestro proyecto se ejecuta siempre de la misma forma en diferentes escenarios como puede ser una pipeline o la máquina de otro miembro del equipo.

Un saludo!