Como crear un paquete en Python

LLevo tiempo desarrollando una serie de herramientas en Python para automatizar mi trabajo. Lo que empezó como un simple script ... Sigue leyendo El artículo Como crear un paquete en Python se publicó primero en Ikkaro.

Ene 16, 2025 - 13:52
 0
Como crear un paquete en Python
crear distribucion python

LLevo tiempo desarrollando una serie de herramientas en Python para automatizar mi trabajo.

Lo que empezó como un simple script con unas funciones ya lleva más de 500 líneas. Cada vez se hace más grande y salen más problemas porque tengo que usarlo en muchos proyectos, cada uno con sus particularidades.

Al final ha ocurrido lo inevitable decenas de scripts parcheados y que cada vez que tengo que modificar , mejorar o arreglar algo, tengo que ir cambiándolo en cada script. Un engorro.

Por eso he decidido hacer un paquete python, un módulo o distribución con mis herramientas, de forma que una vez instalado en local simplemente tenga que importar el módulo para poder usar sus clases y funciones.

Y esto que en principio es algo tremendamente sencillo, me ha costado muchísimo trabajo, porque todos los tutoriales que he encontrado me han dado muchos errores.

Dejo el proceso completo que me ha funcionado, para poder replicarlo cunado lo necesite y si te ayuda pues mato dos pájaros de un tiro.

Crear los archivos y la estructura

Voy a llamar al módulo «mi_paquete». Y lo primero que hay que hacer es la estructura básica de la distribución.

mi_paquete/
├── mi_paquete
│   ├── __init__.py
│   └── main.py
├── pyproject.toml
└── README.md

Una carpeta llamada «mi_paquete» que contiene los archivos pyproject.toml, Readme.md y otra carpeta llamada también «mi_paquete» que a su vez contiene dos archivos __init__.py y main.py

  • pyproject.toml: Archivo de configuración que define cómo construir e instalar tu paquete.
  • README.md: Es opcional, pero es un archivo con la descripción, instrucciones, e información adicional.

mi_paquete/: Carpeta que contendrá el código fuente de tu paquete.

  • __init__.py: Indica a Python que esta carpeta es un package.
  • main.py: Archivo Python con funciones o clases que queremos exponer.

Archivo pyproject.toml

[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "mi_paquete"
version = "0.1.0"
authors = [
    { name="Tu Nombre", email="tucorreo@example.com" }
]
description = "Ejemplo de paquete Python"
readme = "README.md"
license = { text = "MIT" }
requires-python = ">=3.7"
keywords = ["ejemplo", "paquete"]
classifiers = [
    "Programming Language :: Python :: 3",
    "License :: OSI Approved :: MIT License"
]

[project.urls]
"Source Code" = "https://github.com/usuario/mi_paquete"
"Documentation" = "https://mi_paquete.readthedocs.io"

Gestionar dependencias: Si tu paquete tiene dependencias de terceros, añádelas en pyproject.toml dentro de la sección [project], por ejemplo:

[project]
...
dependencies = [
    "requests>=2.0.0"
]

De esta manera, pip instalará automáticamente las librerías requeridas.

Archivo main.py

Copio el ejemplo que me ha dado ChatGPT para no tener error y luego ya modificaré con mis funciones y clases

def saludar(nombre: str) -> str:
    print("¡Hola, {nombre}!")
    return f"¡Hola, {nombre}!"

class Calculadora:
    def __init__(self, x: float, y: float):
        self.x = x
        self.y = y
    
    def sumar(self) -> float:
        return self.x + self.y

Archivo __init__.py

# Podemos importar aquí las funciones o clases que queramos exponer a nivel de paquete
from .main import saludar, Calculadora

__all__ = ["saludar", "Calculadora"]

De esta forma cuando alguien importe el paquete tendrá acceso a saludar y a calculadora.

Crear la distribución

Una distribución puede tener 2 formatos.

  • sdist (Source Distribution)
  • wheel (Binario, si el código se puede compilar o empaquetar de forma portable).

Diferencias entre sdit y wheel

En el ecosistema de empaquetado de Python suelen manejarse dos “tipos” de distribuciones principales:

  1. sdist (Source Distribution): Es el paquete que contiene el código fuente tal cual.
    • Generalmente se genera como un archivo comprimido, por ejemplo:
      mi_paquete-0.1.0.tar.gz
      mi_paquete-0.1.0.zip
    • Dentro de este paquete están todos los archivos .py, el pyproject.toml, documentación, etc.
    • Requiere compilación o preparación adicional al momento de instalar (por eso si tiene extensiones en C/C++ o similares, se compilan en ese momento).
  2. wheel (.whl): Es un formato de distribución “pre-empaquetado” o “precompilado” (donde aplica).
    • Se genera como un solo archivo .whl, por ejemplo:
      mi_paquete-0.1.0-py3-none-any.whl
    • La gran ventaja es que la instalación con pip install mi_paquete-0.1.0-py3-none-any.whl suele ser más rápida y no requiere compilación in situ (a menos que sea un wheel específico para alguna plataforma y haya binarios involucrados).
    • Para la mayoría de paquetes puramente en Python (sin extensiones C), el wheel es universal y sirve en cualquier máquina que cumpla el requisito de versión de Python.

Para construirlos, asegúrate de tener instaladas las siguientes herramientas:

pip install --upgrade setuptools wheel

Luego, desde la raíz del proyecto (donde está pyproject.toml), ejecuta:

python -m build

Si todo está correcto, se creará una carpeta dist/ que contendrá archivos .tar.gz (sdist) y .whl (wheel).

¿Qué es la carpeta dist/?

Cuando ejecutas python -m build (u otra herramienta de empaquetado), se crea una carpeta llamada dist/ donde se guardan los artefactos de distribución generados. Allí encontrarás tanto el sdist (por ejemplo .tar.gz) como el wheel (.whl).

En resumen:

  • La “sdist” (archivos .tar.gz o .zip) es un paquete con el código fuente, útil si alguien quiere compilar/instalar el paquete desde cero o ver el contenido original.
  • El “wheel” (.whl) es un paquete binario (o semi-binario, si es puro Python), pensado para que la instalación sea más rápida y no haya que recompilar nada.
  • dist/ es solo el directorio que contiene esos dos tipos de distribuciones generadas (no es un formato en sí).

Instalar el paquete

Yo voy a utilizar solo el paquete en local, sin compartirlo en ningún sitio. Una forma muy sencilla de instalar tu paquete en tu entorno local es usar:

pip install .

Hay que ejecutarlo en la carpeta donde tenemos pyproject.toml. Esto leerá tu pyproject.toml (y, si existe, setup.py) para instalar el paquete. Después de esto, en tu entorno ya podrás hacer:

import mi_paquete

mi_paquete.saludar("Mundo")

También puedes instalar directamente el wheel resultante en dist/, por ejemplo:

pip install dist/mi_paquete-0.1.0-py3-none-any.whl

Publicar en Pypi

Si en algún momento quieres compartir tu paquete con la comunidad, puedes subirlo a PyPI. Bastaría con:

python -m twine upload dist/*

(Necesitarás una cuenta en PyPI y la herramienta twine).

Problemas de intérprete Bash y Python

He creado un archivo mi_paquete_prueba.py que contiene

import mi_paquete

mi_paquete.saludar("Mundo")

le doy permisos con

chmod +x mi_paquete_prueba.py

Y al ejecutarlo con

./mi_paquete_prueba.py

Pero me devuelve un error

import-im6.q16: attempt to perform an operation not allowed by the security policy PS' @ error/constitute.c/IsCoderAuthorized/413.

Y es por culpa del interprete que intenta ejecutar un archivo en bash en lugar de en Python. Para solucionarlo tenemos dos opciones. O bien lo ejecutamos con el interprete directametne python3 en lugar de ./

python3 mi_paquete_prueba.py

o le añadimos el shebang !/usr/bin/env python3 (dependiendo de la ruta a tu interprete) al script de mi_paquete_prueba.py para poder ejecutarlo con ./

!/usr/bin/env python3

import mi_paquete

mi_paquete.saludar("Mundo")

De esta forma lo interpretará correctamente como código Python.

Cómo actualizar un paquete Python en local

Después de instalar un paquete vas a seguir añadiendo funcionalides o corrigiendo errores y querrás que el paquete que usas en los scripts incorpore estos cambios.

Para hacer esto tenemos 2 opciones.

Instalar el paquete en modo “editable”

Este método es muy cómodo porque cada vez que modificas el código fuente, el entorno detecta los cambios automáticamente sin necesidad de reinstalar todo. Para ello, debes situarte en la carpeta raíz del proyecto (donde está el pyproject.toml o setup.py) y ejecutar:

pip install -e .

Ventajas

  • Cada vez que modifiques tus archivos .py, esos cambios se ven reflejados inmediatamente.
  • No tienes que rehacer la build ni reinstalar manualmente el paquete.

Desventajas

  • Este modo es útil en desarrollo; sin embargo, si quieres empaquetar y distribuir tu código de manera “limpia” (por ejemplo, para un entorno de producción o compartir con otros), deberás generar la distribución final.

Volver a construir la distribución e instalarla

Si prefieres trabajar con el paquete instalado de manera “tradicional” (no editable), cada vez que modifiques el código deberás:

  • Volver a construir la distribución. En la raíz del proyecto:
python -m build

Esto actualizará los ficheros en la carpeta dist/ (el .tar.gz y el .whl).

  • Reinstalar el paquete con la nueva versión:
pip install --upgrade dist/mi_paquete-0.1.0-py3-none-any.whl
pip install --upgrade .

O simplemente:

(si deseas que se tome automáticamente la última build generada en local).

Ventajas

  • Aseguras que el paquete instalado coincide exactamente con el código empaquetado (útil para pruebas de despliegue o integración).
  • Te acostumbras al flujo de generar distribuciones, lo cual es muy útil si planeas publicar en PyPI.

Desventajas

  • Requiere pasos adicionales (build + reinstall) cada vez que cambias el código.

Para pruebas más formales o un flujo de publicación, reconstruye e instala el paquete de manera tradicional (python -m build + pip install --upgrade ...).

Para un desarrollo ágil, usa la instalación en modo editable (pip install -e .).

El artículo Como crear un paquete en Python se publicó primero en Ikkaro.

¿Cuál es tu Reacción?

like

dislike

love

funny

angry

sad

wow