Curso de Python / Empaquetar un programa y verificar su instalación

Un proyecto Python puede declarar sus metadatos de construcción en pyproject.toml y generar archivos distribuibles como un wheel. Instala el wheel en un entorno limpio y prueba desde fuera de la carpeta fuente para detectar dependencias accidentales del código local.

Separar nombre de distribución y paquete importable

La distribución local de este ejercicio se llama expensekit-demo; el paquete que importa Python se llama expensekit. Un nombre de distribución con guion no tiene por qué coincidir con el nombre del módulo. La estructura src ayuda a detectar instalaciones incompletas en vez de importar casualmente desde la raíz del proyecto.

Estos nombres se utilizan solo para practicar localmente. Crear los archivos no demuestra disponibilidad ni propiedad del nombre en un repositorio público. El flujo siguiente construye e instala sin publicar una versión en PyPI. Necesitas Python 3.12 o posterior.

Crear todos los archivos del proyecto

Crea src/expensekit/__init__.py con la función de suma:

def total(amounts):
    if any(type(x) is not int or x < 0 for x in amounts):
        raise ValueError("invalid amount")
    return sum(amounts)

Crea src/expensekit/cli.py con la entrada de línea de comandos:

import argparse
from . import total

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("amounts", type=int, nargs="+")
    args = parser.parse_args()
    print(total(args.amounts))

if __name__ == "__main__":
    main()

En la raíz del proyecto guarda pyproject.toml:

[build-system]
requires = ["setuptools>=68"]
build-backend = "setuptools.build_meta"

[project]
name = "expensekit-demo"
version = "0.1.0"
description = "Local expense sum example"
requires-python = ">=3.12"

[project.scripts]
expense-total = "expensekit.cli:main"

[tool.setuptools.packages.find]
where = ["src"]

El apartado de construcción elige setuptools; los metadatos declaran nombre, versión y rango de Python. project.scripts conecta el alias expense-total con main. El archivo de configuración se muestra como texto porque TOML no forma parte de los lenguajes resaltados del sitio.

Construir e instalar en un entorno independiente

Desde la carpeta que contiene pyproject.toml, ejecuta:

python -m pip install build
python -m build

La carpeta dist debe contener un wheel y una distribución de código fuente. Crea un entorno de verificación separado:

python -m venv .venv-check

En Windows PowerShell instala el wheel con el intérprete nuevo:

.\.venv-check\Scripts\python.exe -m pip install dist/expensekit_demo-0.1.0-py3-none-any.whl

En macOS o Ubuntu utiliza:

.venv-check/bin/python -m pip install dist/expensekit_demo-0.1.0-py3-none-any.whl

Activa ese entorno o conserva la ruta absoluta de su intérprete. Después cambia a una carpeta externa al proyecto y realiza estas comprobaciones:

python -c "from expensekit import total; print(total([3500,8000,2500]))"
python -m expensekit.cli 3500 8000 2500

Ambas órdenes deben mostrar 14000. El alias expense-total 3500 8000 2500 llama a la misma función cuando la carpeta de ejecutables del entorno está en PATH. Si el alias no se encuentra, revisa la activación; ejecutar el módulo con el intérprete explícito permite separar problemas de instalación y de búsqueda del comando.

Inspeccionar el paquete y evitar falsos positivos

Si la importación solo funciona dentro del proyecto, revisa el intérprete seleccionado, la distribución instalada, la búsqueda de paquetes y el contenido del wheel. Una instalación previa de desarrollo puede ocultar un error; por eso la separación mediante entornos virtuales es parte de la comprobación.

El resultado esperado tiene esta estructura:

project/
  pyproject.toml
  src/
    expensekit/
      __init__.py
      cli.py
  dist/
    expensekit_demo-0.1.0-py3-none-any.whl
    expensekit_demo-0.1.0.tar.gz

Declara dependencias necesarias en ejecución dentro de project.dependencies y comprueba su compatibilidad con el rango anunciado. Las herramientas de desarrollo no se convierten automáticamente en dependencias del programa. Mantén el punto de entrada protegido para que importar un paquete no inicie argumentos ni otras operaciones.

Distinguir instalación local y publicación

Una publicación pública necesita revisar nombre, licencia, autoría real, README, archivos incluidos, secretos y autenticación del repositorio. Superar una instalación local no autoriza una subida. La guía oficial de empaquetado de Python describe esas fases; aquí la finalización consiste en construir, instalar limpiamente y comprobar importación, módulo y alias fuera del código fuente.