Construyendo una librería PIP

Construyendo una librería PIP

La primera vez que vi a mi jefe ejecutar comandos en una terminal Linux quedé totalmente inspirado, acostumbrado a usar Windows toda la vida, me pareció que en esos comandos, lo que le decías al computador, se cumplía al pie de la letra. Es decir, si le decías que querías borrar todos los archivos, los borraba! a pesar que después no pudieras encender la máquina.

Aún uso Windows cada día, y lo considero un sistema operativo genial, pero la realidad es que Windows “protege” al usuario de muchas cosas. Esta “protección” es buena en la mayoría de los casos, pero de alguna manera, hace que tengas menos “poder” sobre el PC.

Uno de los comandos que escribió varias veces era pip install ..., me dijo que era para instalar paquetes en Python. En ese momento, no sabía ni qué era Python, y mucho menos que era pip, pero se me quedó grabado en el cerebro.

La realidad es que no es necesario tener Linux para poder usar pip, funciona perfectamente en Windows o Mac, pero yo lo conocí en ese contexto. Soy ingeniero civil de profesión, pero he aprendido programación por mi cuenta. La programación es una herramienta muy poderosa para cualquier persona, tanto es así que creo debiera estar incorporada en los sistemas de educación básica. De la misma manera que se enseñan matemáticas, debieran explicarse unas bases de programación. Al menos algo sobre estructuras de datos, de cómo funciona internet, de servidores, API’s, etc.

Como dicen por ahí, todo parece imposible hasta que se hace, en ese contexto hace unos días me propuse hacer mi primer proyecto pip.

Ventajas de tener un proyecto PIP

Decidí crear un proyecto pip por una razón fundamental: tengo varios programas en Python que utilizo con frecuencia en distintos proyectos, y necesito poder importarlos sin tener que incorporar el código en cada uno de ellos. Mi objetivo es lograr que cada carpeta de proyecto sea "aislable", es decir, que incluya todos los archivos necesarios para funcionar de manera independiente.

La ventaja de utilizar una librería subida a PyPI en un proyecto es que te permite evitar la repetición de código, además de poder actualizarla y que se actualice automáticamente en todos los proyectos en los que la utilizas. De esta forma, la gestión de tus programas en Python se vuelve mucho más eficiente y cómoda.

Además, considero que es genial poder aportar a la comunidad un programa con código abierto, el cual quizás puedan utilizar y mejorar. El compartir nuestros conocimientos y habilidades en programación no solo nos permite ayudar a otros a resolver problemas, sino que también nos permite crecer como programadores y tener una visión más amplia de las posibilidades que ofrece este fascinante campo.

Programa "conversor_nominas_bancos_chile"

El proyecto Python que seleccioné para crear mi primera librería es bastante sencillo, pero es de gran utilidad para la empresa donde trabajo, ya que lo usamos con mucha frecuencia. Tiene como función tomar una nómina de pagos en formato Excel, siguiendo un formato específico, y transformarla al formato de otros bancos. Una nómina de pagos es un listado de transferencias a distintos destinatarios, lo que permite realizar los pagos de forma más eficiente y evitar tener que hacerlos uno a uno.

A pesar de su simplicidad, esta librería pip puede ser de gran ayuda para cualquier empresa que necesite realizar pagos de manera automatizada. Con solo unos pocos pasos, se puede integrar esta herramienta en cualquier proyecto de Python y mejorar significativamente la eficiencia de los procesos de pago.

La estructura del programa se basa en funciones que requieren una ruta de archivo Excel como parámetro de entrada. Estas funciones utilizan la biblioteca Pandas para llevar a cabo las transformaciones necesarias para producir el output deseado. Además, el programa cuenta con una interfaz de usuario desarrollada con Tkinter que simplifica el uso del código para aquellos que no tienen conocimientos de programación.

Proceso creación librería

  1. Crear una estructura de directorios para el proyecto
nombre_del_proyecto/
|-- nombre_del_proyecto/
|   |-- __init__.py
|   |-- archivo1.py
|   |-- archivo2.py
|-- README.md
|-- LICENSE
|-- setup.py
  1. Agregar código al archivo archivo1.py y archivo2.py. Estos archivos contendrán el código del proyecto.

  2. Crear un archivo README.md para proporcionar una descripción y documentación del proyecto.

  3. Crear un archivo LICENSE para indicar bajo qué términos se puede utilizar el proyecto.

  4. Crear un archivo setup.py para definir los metadatos del proyecto, como su nombre, versión, autor y dependencias. Se puede utilizar el siguiente ejemplo como guía.

from setuptools import setup, find_packages

setup(
    name='nombre_del_proyecto',
    version='0.1',
    author='Tu Nombre',
    author_email='tu_email@ejemplo.com',
    description='Descripción del proyecto',
    packages=find_packages(),
    install_requires=[
        'paquete1',
        'paquete2',
    ],
)

Hice todo el proceso y funcionó, por el camino fueron saliendo ciertas dudas que pude resolver, por ejemplo:

  • El hecho de que el proyecto tenga una interfaz generó la siguiente pregunta: ¿Cómo puedo abrir la interfaz una vez instalado el paquete PIP? Esto se puede resolver añadiendo el siguiente código al archivo setup.py:
entry_points={
        'console_scripts': [
            'start_menu_conversor_nominas = conversor_nominas_bancos_chile.bank_tkinter_menu:iniciar_menu'
        ]
    },
  • El programa se lograba subir bien a PyPi, pero cuando lo ejecutaba, daba error porque algunas de las librerías indicadas en install_requires faltaban, u otras sobraban porque forman parte de la base de Python.

Cuando probé de instalar la librería me di cuenta que salía un mensaje indicando algo similar a que el uso del archivo setup.py debía ser substituido por otro llamado project.toml. Ese detalle no había sido indicado por GPT, imagino porque está entrenado con datos hasta final del 2021.

Las instrucciones que me había dado GPT para empaquetar el proyecto eran ejecutar los siguientes comandos:

python setup.py sdist bdist_wheel
twine upload dist/*

Sin embargo, encontré este artículo donde indica que no es recomendable invocar setup.py directamente.

Finalmente eliminé el archivo setup.py y configuré el siguiente project.toml:

[tool.poetry]
name = "conversor_nominas_bancos_chile"
version = "1.8.2"
description = "Librería que convierte el formato de nóminas del BCI al formato del resto de bancos."
authors = [
    "Antonio Canada Momblant <xxxx@gmail.com>"
]
license = "MIT"
readme = "README.md"

repository = "https://github.com/tonicanada/conversor_nominas_bancos_chile"

[tool.poetry.dependencies]
python = "^3.10"
pandas = "^1.5.3"
numpy = "^1.24.2"
datetime = "^5.1"
pathlib = "^1.0.1"
tk = "^0.1.0"
openpyxl = "^3.1.2"
xlrd = "^2.0.1"

[tool.poetry.scripts]
start_menu_conversor_nominas = "conversor_nominas_bancos_chile.bank_tkinter_menu:iniciar_menu"


[build-system]
requires = ["poetry>=1.0"]
build-backend = "poetry.masonry.api"

Para subir el paquete a PIP instalé la librería poetry y ejecuté los siguientes comandos:

poetry build
poetry publish

Instalación librería y ejemplo de uso

La instalación de la librería puede hacerse mediante el siguiente comando:

pip install conversor_nominas_bancos_chile

Una vez instalado, se puede abrir la interfaz gráfica simplemente ejecutando start_menu_conversor_nominas.

A continuación les muestro un ejemplo sobre cómo poder usar las funciones de la librería.

from conversor_nominas_bancos_chile import bank_functions
import pkg_resources
import json
from pathlib import Path
import pandas as pd


# ___________________________________________________________________

# A continuación se muestra el uso de las funciones para la conversión de nóminas
# Al ejecutarse, guardarán el archivo output en la ruta indicada.

path_nomina = Path(
    "/home/acm/Coding/acm_pip_packages/conversor_nominas_bancos_chile/conversor_nominas_bancos_chile/planillas_test/input_nomina_bci_ejemplo.xls")
path_datosempresa = Path(
    "/home/acm/Coding/acm_pip_packages/conversor_nominas_bancos_chile/conversor_nominas_bancos_chile/planillas_test/datos_empresas.xlsx")

# Ejemplo de transformación a formato "Banco Chile Nóminas Transferencias Masivas"
bank_functions.bci_to_bancochile_nomina_transferencias(
    path_nomina, "98765432-1", path_datosempresa)

# Ejemplo de transformación a formato "Banco Chile Pagos Masivos"
bank_functions.bci_to_bancochile_pagosmasivos(
    path_nomina, "98765432-1", path_datosempresa, "812", "prov")

# Ejemplo de transformación a formato "Santander"
bank_functions.bci_to_santander_transferenciasmasivas(
    path_nomina, "76234531-2", path_datosempresa)

# Ejemplo de transformación a formato "BICE"
bank_functions.bci_to_bice_nomina(
    path_nomina, "87543201-9", path_datosempresa)


# ___________________________________________________________________


# A continuación se muestra como se puede acceder a ciertos archivos de la librería
def get_file_from_package(path):
    resource = pkg_resources.resource_filename(
        'conversor_nominas_bancos_chile', path)
    return resource


# Archivo JSON 'bancos_codigos.json'
bancos_codigos = get_file_from_package('bancos_codigos.json')
with open(bancos_codigos) as f:
    bancos_codigos = json.loads(f.read())

# Archivo JSON 'bancos_headers_nomina.json' que contiene los distintos encabezados según cada banco
bancos_headers_nomina = get_file_from_package('bancos_headers_nomina.json')
with open(bancos_headers_nomina) as f:
    bancos_headers_nomina = json.loads(f.read())


# Archivo EXCEL con ejemplo de planilla 'datos_empresas.xlsx'
datos_empresas = get_file_from_package('planillas_test/datos_empresas.xlsx')
df = pd.read_excel(datos_empresas)

# Archivo EXCEL con ejemplo de planilla input nómina BCI'
planilla_ejemplo_input = get_file_from_package(
    'planillas_test/input_nomina_bci_ejemplo.xls')
df = pd.read_excel(planilla_ejemplo_input)

¡Espero les pueda servir! Tanto la información de cómo subir una librería PIP, como el programa en sí mismo. Aquí les dejo el link al repositorio del proyecto.

Si te gustó este artículo, ¡favor dale a 👏 y compártelo! Puedes seguirme en mi blog, LinkedIn, Twitter, Facebook, Medium.