Poetry en Python

Poetry es una herramienta de gestión de dependencias y empaquetado en Python que facilita el desarrollo, la instalación de librerías, la creación de entornos virtuales y la publicación de paquetes. A diferencia de pip y virtualenv, Poetry unifica el manejo de entornos y dependencias en un solo comando, con un archivo central llamado pyproject.toml.

Web oficial: https://python-poetry.org/


✅ ¿Qué es Poetry?

Poetry es una herramienta moderna para:

  • Crear proyectos Python.
  • Gestionar dependencias.
  • Mantener versiones coherentes.
  • Crear entornos virtuales automáticos.
  • Publicar paquetes en PyPI.

Instalación de Poetry

Recomendado: instalar con «pipx»

💡 macOS (Homebrew)

Nota para macOS: Debido a restricciones recientes (PEP 668), la forma recomendada es instalar pipx con Homebrew para evitar errores como externally-managed-environment.

🔎 ¿Qué son brew y pipx?

  • Homebrew (brew): es el gestor de paquetes más popular en macOS. Permite instalar fácilmente herramientas, librerías y utilidades desde la terminal sin tener que compilar o configurar nada manualmente. Si quieres más info y saber como instalarlo deja un comentario o házmelo saber y crearé un post sobre ello.
  • pipx: es una herramienta que permite instalar y ejecutar aplicaciones Python aisladas globalmente, sin interferir con tu instalación principal de Python. Ideal para herramientas como poetry, black, httpie, etc. Ahora veremos como instalarlo.

Instalando pipx y agregando path

brew install pipx
pipx ensurepath

Reinicia la terminal o ejecuta este comando:

source ~/.zshrc

Para instalar Poetry:

pipx install poetry

🐳 Linux

Instalando pipx, agregando path y instalado Poetry

python3 -m pip install --user pipx
python3 -m pipx ensurepath
pipx install poetry

🪦 Windows (PowerShell)

Instalando pipx, agregando path y instalado Poetry

python -m pip install --user pipx
python -m pipx ensurepath
pipx install poetry

Para verifica que todo esté correcto:

poetry --version

Comandos más útiles

ComandoDescripción
poetry new nombre_proyectoCrea un nuevo proyecto
poetry initInicializa un proyecto en un directorio existente
poetry installInstala dependencias desde pyproject.toml
poetry add paqueteAñade una dependencia
poetry remove paqueteElimina una dependencia
poetry updateActualiza todas las dependencias
poetry update paqueteActualiza solo un paquete
poetry lockActualiza el archivo poetry.lock
poetry shellEntra al entorno virtual
poetry run comandoEjecuta un comando dentro del entorno virtual
poetry exportExporta las dependencias a requirements.txt

⚠️ A partir de Poetry 1.8, poetry shell ya no está disponible por defecto. Es necesario instalar el plugin plugin-shell si deseas seguir utilizando este comando para acceder al entorno virtual:

poetry self add poetry-plugin-shell

📁 Crear Proyecto

Accederemos a la carpeta donde queremos crear el proyecto y ejecutamos:

poetry init

Poetry te hará una serie de preguntas sobre el nombre, versión, autor, licencia, Python requerido, y dependencias.

Al finalizar la instalacion nos habrá creado 2 archivos:project.toml y pypoetry.lock (si hemos instalado dependencias)

project.toml:
- Es el archivo principal de configuración del proyecto. Contiene:
	•	Nombre, versión y descripción del proyecto.
	•	Autor y licencia.
	•	Versión mínima de Python requerida.
	•	Dependencias ([tool.poetry.dependencies])
	•	Dependencias de desarrollo ([tool.poetry.dev-dependencies])
	•	Configuraciones de publicación y scripts.

poetry.lock:
- Este archivo se genera automáticamente tras instalar dependencias. Sirve para:
	•	Fijar versiones exactas de todas las dependencias (incluidas subdependencias).
	•	Garantizar que todos los desarrolladores usen las mismas versiones (ideal para equipos y CI/CD).
	•	Evitar que una actualización automática rompa tu proyecto.

📌 Consejo:

  • Puedes editar project.tom manualmente o dejar que poetry lo modifique por ti con poetry add, poetry remove, etc.
  • No edites poetry.lock a mano. Déjalo siempre bajo control de Poetry.

💡 Ambos archivos deben incluirse en control de versiones (Git) para garantizar reproducibilidad.

Inicialización rápida:

poetry init --no-interaction


Ejemplos comunes para la pregunta de licencia y versión

Licencia:

EntradaDescripción
MITMuy permisiva. Puedes usar, copiar, modificar y distribuir con pocos requisitos.
Apache-2.0Similar a MIT, pero con protección de patentes.
GPL-3.0Licencia copyleft. Los derivados deben también ser código abierto.
BSD-3-ClausePermisiva, usada en entornos académicos y empresas.
ProprietaryPara proyectos cerrados con derechos reservados.
(vacío)No defines licencia, por tanto legalmente nadie podrá usar tu proyecto.

Versiones compatibles de Python:

EntradaSignificado
^3.9Compatible con 3.9.x, 3.10.x, 3.11.x, etc., pero no con 4.0
>=3.9Requiere al menos Python 3.9 (puede ser compatible con 4.0 en adelante)
>=3.9,<3.12Compatible desde 3.9 hasta (pero sin incluir) 3.12
~3.10Solo versiones 3.10.x
==3.10.*Igual: exactamente versiones 3.10.x
>=3.10,!=3.11.0Desde 3.10, excluyendo la versión 3.11.0
>=3.9,<4.0Muy común: desde Python 3.9 hasta justo antes de Python 4.0

Añadir, actualizar, eliminar dependencias

➕ Añadir un paquete

poetry add requests

🔄 Actualizar un paquete específico

poetry update requests

🔄 Actualizar todo

poetry update

🔄 En caso de tener las dependencias en el archivo toml podemos instalarlo todo a la vez

poetry install

➖ Eliminar un paquete

poetry remove requests

Creación y uso del entorno virtual

El entorno virtual se crea cuando instalas una dependencia por primera vez, ya sea con:

poetry add paquete   |   poetry install    o   bien al introducirlas cuando hacemos el init

(si ya hay dependencias definidas en el archivo pyproject.toml), Poetry crea automáticamente un entorno virtual aislado para tu proyecto.

Puedes trabajar dentro de ese entorno virtual de dos formas:

  1. Entrar en el entorno interactivo: (recuerda que hay que instalar su plugin como hemos visto más arriba)
poetry shell

Una vez dentro, puedes ejecutar scripts normalmente con:

python3 main.py
  1. Ejecutar comandos directamente sin activar el entorno:
poetry run python3 main.py

Esto es útil en entornos automatizados o si prefieres no cambiar de shell.

📂 Ubicación de los entornos virtuales

Poetry guarda los entornos virtuales en distintas rutas según el sistema operativo:

  • macOS / Linux:/Users/<usuario>/Library/Caches/pypoetry/virtualenvs/
  • Windows:%APPDATA%\pypoetry\virtualenvs\

Puedes listar todos los entornos detectados con:

poetry env list

Y eliminar uno con:

poetry env remove python

Si algún entorno no aparece en la lista, pero sabes que ya no se usa, puedes eliminar la carpeta manualmente desde la ruta correspondiente.


Exportar dependencias

Para compartir el proyecto con pip, puedes generar un archivo requirements.txt:

poetry export -f requirements.txt --output requirements.txt --without-hashes

Agrega –dev si quieres incluir dependencias de desarrollo.


Importar desde requirements.txt

Si ya tienes un archivo requirements.txt, puedes convertirlo fácilmente a dependencias en Poetry.

Opción rápida (línea de comandos)

poetry add $(cat requirements.txt)

⚠️ Este método puede fallar si el archivo contiene comentarios (#) o líneas vacías. Para evitar errores:

grep -vE '^\s*#|^\s*$' requirements.txt > clean-req.txt
poetry add $(cat clean-req.txt)

Opción alternativa con plugin oficial

Puedes usar el plugin poetry-plugin-import:

poetry self add poetry-plugin-import
poetry import requirements.txt

Esto añadirá las dependencias al pyproject.toml de forma segura y limpia.


Con esta guía ya puedes empezar a trabajar con proyectos Python usando Poetry!

Espero que os sirva de ayuda.

Salu2.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Warning: Undefined array key "rerror" in /volume1/web/WebTomoNota/wp-content/plugins/wp-recaptcha-bp/recaptcha.php on line 300 Call Stack: 0.0001 360920 1. {main}() /volume1/web/WebTomoNota/index.php:0 0.0001 361232 2. require('/volume1/web/WebTomoNota/wp-blog-header.php') /volume1/web/WebTomoNota/index.php:17 0.4854 14476104 3. require_once('/volume1/web/WebTomoNota/wp-includes/template-loader.php') /volume1/web/WebTomoNota/wp-blog-header.php:19 0.4919 14644496 4. include('/volume1/web/WebTomoNota/wp-content/themes/yuki/index.php') /volume1/web/WebTomoNota/wp-includes/template-loader.php:106 2.8650 19022848 5. yuki_do_elementor_location($elementor_location = 'single', $template_part = 'template-parts/special', $name = 'single') /volume1/web/WebTomoNota/wp-content/themes/yuki/index.php:20 2.8651 19022848 6. get_template_part($slug = 'template-parts/special', $name = 'single', $args = ???) /volume1/web/WebTomoNota/wp-content/themes/yuki/inc/helpers.php:34 2.8651 19023424 7. locate_template($template_names = [0 => 'template-parts/special-single.php', 1 => 'template-parts/special.php'], $load = TRUE, $load_once = FALSE, $args = []) /volume1/web/WebTomoNota/wp-includes/general-template.php:206 2.8651 19023536 8. load_template($_template_file = '/volume1/web/WebTomoNota/wp-content/themes/yuki/template-parts/special-single.php', $load_once = FALSE, $args = []) /volume1/web/WebTomoNota/wp-includes/template.php:745 2.8654 19029648 9. require('/volume1/web/WebTomoNota/wp-content/themes/yuki/template-parts/special-single.php') /volume1/web/WebTomoNota/wp-includes/template.php:812 2.9588 19032424 10. do_action($hook_name = 'yuki_action_after_single_post') /volume1/web/WebTomoNota/wp-content/themes/yuki/template-parts/special-single.php:58 2.9588 19032800 11. WP_Hook->do_action($args = [0 => '']) /volume1/web/WebTomoNota/wp-includes/plugin.php:517 2.9588 19032800 12. WP_Hook->apply_filters($value = '', $args = [0 => '']) /volume1/web/WebTomoNota/wp-includes/class-wp-hook.php:348 3.0600 19118744 13. yuki_show_post_comments('') /volume1/web/WebTomoNota/wp-includes/class-wp-hook.php:324 3.0602 19118744 14. comments_template($file = ???, $separate_comments = ???) /volume1/web/WebTomoNota/wp-content/themes/yuki/inc/template-functions.php:354 3.0625 19134416 15. require('/volume1/web/WebTomoNota/wp-content/themes/yuki/comments.php') /volume1/web/WebTomoNota/wp-includes/comment-template.php:1631 3.0626 19134416 16. comment_form($args = ['class_form' => 'comment-form yuki-form form-default'], $post = ???) /volume1/web/WebTomoNota/wp-content/themes/yuki/comments.php:66 3.0649 19146136 17. do_action($hook_name = 'comment_form', ...$arg = variadic(1605)) /volume1/web/WebTomoNota/wp-includes/comment-template.php:2896 3.0649 19146512 18. WP_Hook->do_action($args = [0 => 1605]) /volume1/web/WebTomoNota/wp-includes/plugin.php:517 3.0649 19146512 19. WP_Hook->apply_filters($value = '', $args = [0 => 1605]) /volume1/web/WebTomoNota/wp-includes/class-wp-hook.php:348 3.0650 19147264 20. ReCAPTCHAPlugin->show_recaptcha_in_comments(1605) /volume1/web/WebTomoNota/wp-includes/class-wp-hook.php:324
 
Fatal error: Uncaught GuzzleHttp\Exception\ClientException: Client error: `POST https://api.aspose.cloud/connect/token` resulted in a `400 Bad Request` response: {"error":"invalid_client"} in /volume1/web/WebTomoNota/wp-content/plugins/aspose-doc-exporter/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php on line 113 GuzzleHttp\Exception\ClientException: Client error: `POST https://api.aspose.cloud/connect/token` resulted in a `400 Bad Request` response: {"error":"invalid_client"} in /volume1/web/WebTomoNota/wp-content/plugins/aspose-doc-exporter/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php on line 113 Call Stack: 3.0965 19138752 1. shutdown_action_hook() /volume1/web/WebTomoNota/wp-includes/load.php:0 3.0965 19138752 2. do_action($hook_name = 'shutdown') /volume1/web/WebTomoNota/wp-includes/load.php:1304 3.0965 19139128 3. WP_Hook->do_action($args = [0 => '']) /volume1/web/WebTomoNota/wp-includes/plugin.php:517 3.0965 19139128 4. WP_Hook->apply_filters($value = '', $args = [0 => '']) /volume1/web/WebTomoNota/wp-includes/class-wp-hook.php:348 3.0965 19070528 5. AsposeWords\AutoExport->export('') /volume1/web/WebTomoNota/wp-includes/class-wp-hook.php:324 3.1531 21167592 6. AsposeWords\ExportEngine->convert() /volume1/web/WebTomoNota/wp-content/plugins/aspose-doc-exporter/src/AsposeWords/AutoExport.php:26 3.1546 21245536 7. AsposeWords\Util::getWordsApi() /volume1/web/WebTomoNota/wp-content/plugins/aspose-doc-exporter/src/AsposeWords/ExportEngine.php:87 3.2452 29664352 8. Aspose\Words\WordsApi->__construct($clientId = '875775BC-8A52-49BA-8C65-7F4BDFA6802E', $clientSecret = '69f869e481bfde19e81735d12ddc3db4', $baseUrl = 'https://api.aspose.cloud/') /volume1/web/WebTomoNota/wp-content/plugins/aspose-doc-exporter/src/AsposeWords/Util.php:27 3.2565 30335040 9. Aspose\Words\WordsApi->_checkRsaKey() /volume1/web/WebTomoNota/wp-content/plugins/aspose-doc-exporter/vendor/aspose-cloud/aspose-words-cloud/src/Aspose/Words/WordsApi.php:80 3.2565 30335040 10. Aspose\Words\WordsApi->_getKey() /volume1/web/WebTomoNota/wp-content/plugins/aspose-doc-exporter/vendor/aspose-cloud/aspose-words-cloud/src/Aspose/Words/WordsApi.php:50666 3.2565 30335040 11. Aspose\Words\WordsApi->_checkAuthToken() /volume1/web/WebTomoNota/wp-content/plugins/aspose-doc-exporter/vendor/aspose-cloud/aspose-words-cloud/src/Aspose/Words/WordsApi.php:50654 3.2565 30335040 12. Aspose\Words\WordsApi->_requestToken() /volume1/web/WebTomoNota/wp-content/plugins/aspose-doc-exporter/vendor/aspose-cloud/aspose-words-cloud/src/Aspose/Words/WordsApi.php:50648 3.2696 30674712 13. GuzzleHttp\Client->send($request = class GuzzleHttp\Psr7\Request { private $method = 'POST'; private $requestTarget = NULL; private $uri = class GuzzleHttp\Psr7\Uri { private $scheme = 'https'; private $userInfo = ''; private $host = 'api.aspose.cloud'; private $port = NULL; private $path = '/connect/token'; private $query = ''; private $fragment = ''; private $composedComponents = 'https://api.aspose.cloud/connect/token' }; private $headers = ['Host' => [...]]; private $headerNames = ['host' => 'Host']; private $protocol = '1.1'; private $stream = class GuzzleHttp\Psr7\MultipartStream { private $boundary = 'd197335e9dfd6c9dff9dceb7a8c95f0212040ee8'; public $stream = class GuzzleHttp\Psr7\AppendStream { ... } } }, $options = ???) /volume1/web/WebTomoNota/wp-content/plugins/aspose-doc-exporter/vendor/aspose-cloud/aspose-words-cloud/src/Aspose/Words/WordsApi.php:50640 3.8456 30855784 14. GuzzleHttp\Promise\Promise->wait($unwrap = ???) /volume1/web/WebTomoNota/wp-content/plugins/aspose-doc-exporter/vendor/guzzlehttp/guzzle/src/Client.php:123