Skip to main content

La guía definitiva para crear un paquete de Python seguro

Escrito por
Headshot of Gourav Singh Bais

Gourav Singh Bais

feature assert in python

8 de mayo de 2024

0 minutos de lectura

Crear un paquete de Python implica varios pasos, como definir una estructura de directorios adecuada, crear los archivos del paquete y configurar sus metadatos antes de implementarlo. También hay otros pasos que debes seguir, como crear un subdirectorio para las pruebas y redactar documentación clara. Una vez que el paquete esté listo, puedes distribuirlo en distintos repositorios. Así, otras personas podrán instalarlo y usarlo.

Esta guía detalla todos los pasos para crear un paquete moderno de Python. Puedes encontrar el código necesario para seguir este tutorial en este repositorio de GitHub. Al terminar este tutorial, verás que crear un paquete de Python puede ser tan sencillo como usar uno existente. Pero primero, veamos cómo usar paquetes en Python.

Cómo crear un paquete de Python

Antes de empezar a crear un paquete de Python, debes revisar los distintos métodos de instalación e importación, así como la estructura general de los paquetes. 

Instalación de paquetes en Python

Para usar paquetes de Python, puedes recurrir a los paquetes públicos de PyPI o a índices privados que contengan código propietario de una organización. 

A continuación, se muestran dos formas distintas de instalar un paquete de Python:

Usar PyPI

PyPI es el repositorio oficial que contiene todos los paquetes públicos de Python.

$ pip install package_name

Usar un índice privado

En algunos casos, quizá necesites usar paquetes públicos disponibles en PyPI y paquetes privados disponibles en un índice privado. Los índices privados suelen usarse en grandes organizaciones para proyectos propietarios cuyo código fuente no se compartirá públicamente. Estos índices permiten almacenar y distribuir paquetes de Python de forma interna dentro de la organización. 

Para instalar paquetes desde un índice privado, puedes usar el comando pip install con la opción -- index-url, seguido de la URL del índice privado y el nombre del paquete. Este es un ejemplo del comando que debes ejecutar para instalar un paquete:

$ pip install -- index-url <private-index-url> package_name

Por motivos de seguridad, se recomienda usar Transport Layer Security (TLS) en cualquier índice de paquetes privados. Si tu índice privado no tiene un certificado TLS válido, tendrás que especificar la opción  -- trusted-host en el comando pip install para instalar paquetes desde él. Esto se debe a que pip considerará que el índice no es confiable si no tiene un certificado TLS válido.

Importar un paquete de Python

Puedes importar un paquete en Python con la instrucción import. Luego, el intérprete de Python buscará el paquete en la caché, las bibliotecas integradas y las rutas de instalación. Si no lo encuentra, generará un ImportError.

También puedes importar módulos específicos de un paquete con la palabra clave from, como se muestra a continuación:

from datetime import date

También puedes importar varios módulos o todos los módulos de un paquete con los siguientes comandos:

from datetime import date, datetime
from datetime import *

También puedes asignarle un alias a un paquete con la palabra clave as. Por ejemplo, puedes usar el paquete `pandas` como pd, de esta manera:

import pandas as pd

Qué conforma un paquete de Python

Los paquetes de Python deben seguir una estructura de directorios que incluya los siguientes archivos y subdirectorios:

  • Un directorio src con un subdirectorio para el nombre del paquete, que incluya un archivo de inicialización __init__.py. El directorio src también contendrá uno o más archivos module.py con el código fuente del paquete.

  • Un directorio tests con scripts para probar distintas funciones del paquete. Tú decides si quieres incluir este directorio en el paquete.

  • Un archivo pyproject.toml con los detalles de configuración y las dependencias del paquete.

  • Un archivo LICENSE que especifica los términos de uso del paquete. Puedes elegir entre muchas licencias o crear la tuya.

  • Un archivo README.md con documentación clara, instrucciones de uso y cualquier otra información útil e interesante para quienes usen el paquete.

Los archivos y directorios mencionados son los requisitos básicos para crear tu paquete. Es posible que encuentres estructuras más complejas con archivos, submódulos o pruebas adicionales.

Empaquetar tu código

Para que un paquete de Python esté disponible públicamente, primero debes crearlo. En este tutorial, te guiaremos para crear un programa sencillo de Python que convierte kilómetros (km) a millas (mi).

Elegir el nombre de tu paquete de Python

Para ponerle nombre a tu paquete de Python, debes consultar PyPI para comprobar si alguien ya usa el nombre que elegiste. Por lo general, los desarrolladores usan el mismo nombre en PyPI y para el paquete, pero puedes elegir uno distinto si lo prefieres. 

Al instalar un paquete, debes usar el nombre de PyPI. Al importar un paquete, debes usar el nombre del paquete.

Configurar tu paquete de Python

Debes estructurar el paquete según la estructura de directorios requerida que vimos anteriormente. Coloca este código en el archivo km2miles.py. Puedes crear un archivo __init__.py con este código.

El archivo km2miles.py contiene una función que convierte kilómetros a millas con la biblioteca NumPy. El archivo __init__.py importa el módulo principal y especifica la versión del paquete. 

Antes de publicar el paquete, debes configurar el archivo pyproject.toml, donde también especificarás el sistema de compilación, como veremos a continuación. 

Configurar el sistema de compilación

Necesitas un sistema de compilación para procesar los archivos que publiques en el paquete de Python. Puedes usar una interfaz de compilación, como pip, o un backend de compilación, como setuptools, Flit, Hatchling o PDM.

En este caso, usarás setuptools porque es una herramienta de compilación que resuelve automáticamente las dependencias de los paquetes de Python existentes. Esto te será útil cuando crees un paquete de Python para un caso de uso real. 

El sistema de compilación usa el archivo pyproject.toml, que contiene las instrucciones para que las herramientas del backend creen los paquetes de distribución del proyecto. Las especificaciones del sistema de compilación se definen en este archivo:

# pyproject.toml
[build-system]
requires = ["setuptools>=61.0.0", "wheel"]
build-backend = "setuptools.build_meta"

Quizá encuentres tutoriales en línea que no usan el archivo pyproject.toml y, en su lugar, tienen un archivo setup.py para configurar sus paquetes de Python. Esto se debe a que, anteriormente, setuptools usaba el archivo setup.py para ejecutar el proceso de instalación de paquetes complejos. Sin embargo, en los proyectos modernos, por lo general se recomienda usar el archivo pyproject.toml para configurar proyectos de Python en lugar de los archivos setup.py o setup.cfg.

Configurar el paquete de Python

Una vez configurado el sistema de compilación, debes configurar la información específica del paquete. Estos detalles de configuración se colocan debajo de los del sistema de compilación en el archivo .toml:

[project]
name = "milesConverter"
version = "1.0.0"
description = "Convert KMs to Miles"
readme = "README.md"
authors = [{ name = "name", email = "name@gmail.com" }]
license = { file = "LICENSE" }
classifiers = [
 "License :: OSI Approved :: MIT License",
 "Programming Language :: Python",
 "Programming Language :: Python :: 3",
 "Operating System :: MacOS :: MacOS X",
 "Operating System :: Microsoft :: Windows",
 "Intended Audience :: Education"
]
keywords = ["converter", "miles", "kms"]

La configuración de un proyecto suele incluir los siguientes datos y, por lo general, se recomienda incluirlos en todos los paquetes. Sin embargo, no todos son obligatorios:

  • Nombre: (obligatorio) nombre del paquete que se usará en PyPI.

  • Versión: (obligatorio) versión actual del paquete, que también se muestra en la página principal del paquete en PyPI.

  • Descripción: (obligatorio) información adicional sobre el paquete que se mostrará en su página de PyPI.

  • Autores: (obligatorio) información sobre quienes desarrollaron el paquete.

  • Readme: (opcional) descripción del paquete en formato Markdown, con información sobre cómo usarlo, que se mostrará en su página principal de PyPI.

  • Licencia: (opcional, pero muy recomendable) información sobre cómo otras personas pueden usar el paquete.

  • Clasificadores: (opcional) categorías que describen el público objetivo, la compatibilidad con plataformas y el nivel de madurez del paquete, lo que facilita su búsqueda en PyPI. Puedes consultar aquí la lista completa de clasificadores aceptados.

  • Palabras clave: (opcional) lista de palabras que ayuda a las personas a encontrar el paquete en PyPI mediante búsquedas de términos relacionados. Por ejemplo, alguien que busque un paquete para convertir millas a kilómetros podría buscar palabras clave como "converter", "miles" y "kms".

Especificar las dependencias

Para especificar dependencias en el archivo `pyproject.toml`) de un proyecto, agrega un campo dependencies después de la descripción del proyecto:

dependencies = ["numpy >= 1.20.3"]

Esto especifica que el proyecto depende de la biblioteca NumPy. Cuando se instale el proyecto, pip resolverá estas dependencias. Ten en cuenta lo siguiente al especificar dependencias:

  • No especifiques dependencias que ya estén cubiertas por paquetes de terceros.

  • Usa >= para especificar un límite inferior para las dependencias necesarias para las nuevas funciones agregadas en una versión específica.

  • Usa < para especificar un límite superior para las dependencias que podrían causar problemas de compatibilidad durante una actualización de versión principal.

Puedes agregar dependencias opcionales con el campo project.optional-dependencies:

[project.optional-dependencies]
dev = ["pip-tools", "pytest"]

Estas dependencias no se instalan automáticamente, pero puedes especificarlas al instalar el proyecto con la sintaxis pip install package_name[optional_dependency_name].

Asignar una versión a tu paquete

Como se explicó antes en este tutorial, puedes asignar una versión a tu paquete de Python usando un número de versión e incluyéndolo en la documentación del paquete. El número de versión debe seguir el formato de versionado semántico de uso común MAJOR.MINOR.PATCH, donde MAJOR representa cambios importantes en el proyecto, MINOR representa nuevas funciones y PATCH representa correcciones de errores o mejoras de rendimiento. 

A continuación, se muestra cómo agregar una versión en el archivo de configuración:

version = "1.0.0"

Esto especificará la versión del proyecto como 1.0.0. Luego, puedes actualizar el número de versión cuando publiques nuevas versiones del proyecto.

Probar tu paquete

Puedes probar tu paquete de Python de varias maneras. En proyectos pequeños, a veces bastan las pruebas manuales, pero siempre es recomendable contar con otros tipos de pruebas automatizadas para garantizar que el código cumpla cierto estándar sin intervención manual. La disponibilidad de pruebas automatizadas aumenta la confianza en la confiabilidad de un paquete.

  • Pruebas unitarias: ayudan a probar partes específicas del código de forma aislada con un framework de pruebas como `robot`, `pytest`, `testify` o `nose`.

  • Pruebas de integración: prueban cómo funcionan juntas distintas partes del paquete mediante la configuración de entornos de prueba y la ejecución del código. Para implementar pruebas de integración, puedes usar un paquete como `tox`.

  • Pruebas manuales: ejecuta el código y comprueba que produzca los resultados esperados. Puedes usar cualquier cliente de Python para realizar pruebas manuales.

Puedes colocar todas estas pruebas en la carpeta tests que describimos anteriormente en el tutorial. Los proyectos complejos suelen requerir análisis, informes de visibilidad e informes de cobertura de las pruebas. Para esto, puedes usar paquetes como coverage.py y `pytest-cov`.

Agregar archivos de recursos a tu paquete

Para incluir en tu paquete de Python archivos que no sean código fuente, como archivos de datos, binarios, manuales y archivos de configuración, puedes crear un archivo de recursos llamado MANIFEST.in. Puedes usar reglas en este archivo para especificar qué archivos incluir o excluir al compilar el paquete. 

Por ejemplo, el siguiente código incluirá todos los archivos .toml ubicados en el directorio src/mileConverter:

# MANIFEST.in
include src/mileConverter/*.toml

También puedes especificar un archivo .toml aparte para este propósito.

Elegir una licencia para tu paquete

Para incluir una licencia en tu paquete de Python y permitir que otras personas lo usen, crea un archivo llamado LICENSE en el directorio del paquete y elige una licencia existente de una lista de opciones, como las que ofrece choosealicense.com. 

En el archivo pyproject.toml, debes especificar el nombre del archivo LICENSE en la variable license para que la licencia aparezca en la página de PyPI, tal como hiciste cuando empezaste a configurar el archivo pyproject.toml del paquete.

Algunas de las licencias de código abierto más populares son la MIT License, la Apache License y la GNU General Public License (GPL), pero hay muchas más entre las que puedes elegir.

Instalar el paquete localmente

Llegado este punto, ya creaste todos los archivos necesarios e hiciste todas las configuraciones requeridas para tu paquete. Antes de publicarlo en PyPI, deberías probar instalarlo localmente para comprobar si hay errores. Puedes hacerlo con pip. Solo tienes que especificar la etiqueta -e o - editable, que indica que quieres instalar el paquete localmente, como se muestra en el siguiente comando:

$ pip install -e .

Ahora que instalaste el paquete localmente, está listo para publicarse en PyPI.

Publicar el paquete en PyPI

Para publicar tu paquete, primero debes compilarlo.

Compilar el paquete

Usa el paquete build para generar un archivo wheel para tu paquete de Python. Para instalar la interfaz de compilación, ejecuta el siguiente comando:

$ pip install build

Luego, ve al directorio que contiene el archivo pyproject.toml y ejecuta el siguiente comando:

$ python -m build

Esto creará una carpeta dist/ en la misma ubicación que el archivo .toml, con dos archivos:

dist/
├── milesConverter-1.0.0-py3-none-any.whl
└── milesConverter-1.0.0.tar.gz

El archivo `.whl` es una distribución compilada y el archivo tar.gz es una distribución de código fuente. Estos archivos se pueden subir a PyPI, pero primero debes probarlos.

Probar el paquete

Para probar tu paquete, debes instalar `twine`, una biblioteca que también te ayudará a subirlo a PyPI. Puedes instalarla con el siguiente comando:

$ pip install twine

Una vez instalado twine, puedes comprobar con este comando si la descripción de tu paquete se mostrará correctamente en PyPI:

$ twine check dist/*

El resultado del comando anterior será similar a este:

(base) gouravbais@Gouravs-Air Python_Package % twine check dist/*
Checking dist/milesConverter-1.0.0-py3-none-any.whl: PASSED
Checking dist/milesConverter-1.1.0-py3-none-any.whl: PASSED
Checking dist/milesConverter-1.0.0.tar.gz: PASSED
Checking dist/milesConverter-1.1.0.tar.gz: PASSED

Si todas las pruebas se completan correctamente, tu paquete está listo.

Subir el paquete al servicio TestPyPI

Antes de poner tu paquete a disposición del público, debes probarlo y verificarlo con el servicio TestPyPI. Puedes hacerlo subiéndolo a TestPyPI, pero primero tendrás que crear una cuenta en el sitio web de PyPI. Luego, puedes usar twine para subir tu paquete ejecutando el siguiente comando; necesitarás tu nombre de usuario y contraseña de PyPI:

$ twine upload -r testpypi dist/*

Cuando el comando se ejecute correctamente, recibirás una URL para consultar los resultados. También podrás buscar tu paquete en el sitio web de TestPyPI. Para descargarlo desde el servicio TestPyPI, usa el siguiente comando:

$ pip install -i https://test.pypi.org/simple/ milesConverter==1.0.0

Una vez descargado, podrás probar tu paquete para asegurarte de que funciona correctamente.

Subir el paquete al servicio oficial de PyPI

Subir el paquete al servicio oficial de PyPI es similar a hacerlo en TestPyPI. Después de validar tu paquete en el paso anterior, puedes subirlo al servicio oficial de PyPI. Nuevamente, usa la herramienta twine para subir los archivos de tu paquete a PyPI.

En la terminal, ejecuta el siguiente comando y reemplaza dist/* por la ruta a los archivos de tu paquete:

twine upload dist/*

Después de subir el paquete, puedes visitar el sitio web y buscarlo.

Instalar el paquete

Ahora debes probar el paquete. Puedes descargarlo con el siguiente comando:

$ pip install milesConverter

Una vez instalado el paquete, abre cualquier editor de Python o entorno de ejecución de Python y comprueba si funciona:

from milesConverter import km2miles
a = km2miles.km2miles(16)
print(a)

El resultado debería verse así:

9.941936

Tu paquete ya está en PyPI y está disponible mediante pip en cualquier entorno de Python.

Publicar el paquete en un índice privado de Python

Antes aprendiste por qué algunas organizaciones pueden querer distribuir sus paquetes mediante un índice privado de Python. Además, publicar tu paquete de Python en un índice privado es fácil. Solo tienes que seguir estos pasos:

  • Configura la estructura de directorios de tu paquete con archivos .whl o .tar.gz para distribuirlo.

  • Configura tu servidor web para que sirva el directorio raíz del paquete con la indexación automática habilitada.

  • Usa twine para subir tu paquete al índice privado, con la URL de tu servidor web privado $ twine upload - repository-url your_web_server milesConverter/*.

  • Para instalar el paquete desde el índice privado, usa el comando pip con la URL de tu servidor web ($ pip install - extra-index-url your_web_server milesConverter).

Cuando completes estos pasos, tú y tu equipo podrán usar el paquete desde tu servidor web privado.

Prácticas recomendadas de seguridad para crear paquetes de Python

Ahora que sabes cómo publicar tu paquete, hay algunas prácticas recomendadas que deberías considerar antes de hacerlo para asegurarte de que sea seguro y funcione correctamente.

Presta atención a las dependencias

Antes de usar una dependencia de terceros en tu proyecto, revisa su documentación para ver si incluye controles de seguridad o de vulnerabilidades. Muchas bibliotecas tienen una sección completa en su documentación sobre problemas de seguridad conocidos y cómo abordarlos. Contar con esta documentación te dará más confianza al elegir un paquete de terceros.

Si no hay documentación, una herramienta como Snyk puede ayudarte a identificar vulnerabilidades en las bibliotecas de terceros que estés considerando usar.

Ten cuidado con las cadenas de texto y el SQL sin procesar

Se recomienda tener mucho cuidado al trabajar con cadenas de texto en Python, especialmente cuando usas SQL sin procesar en tu código. Un manejo inadecuado puede exponerte a ataques de inyección de cadenas de texto o de SQL. Un atacante puede insertar código malicioso en una cadena de texto o, más probablemente, en una consulta SQL, lo que puede hacer que se ejecuten comandos SQL inesperados y arbitrarios en tu base de datos.

Puedes prevenir estos ataques prestando mucha atención a cómo das formato a las cadenas de texto, sobre todo cuando recibes datos del usuario. Lo mejor es usar bibliotecas estándar para darles formato mediante la funcionalidad f-string.

En cuanto al SQL sin procesar, debes usar consultas parametrizadas en lugar de concatenar directamente los datos del usuario en la cadena SQL. Esto puede ayudar a prevenir ataques de inyección SQL, ya que garantiza que los datos del usuario se escapen, se entrecomillen y se verifiquen correctamente.

Deserializa con cuidado y de forma segura

El módulo pickle de Python no es muy seguro y puede ser vulnerable a ataques como la bomba pickle. Para asegurarte de que tu paquete sea seguro, puedes considerar una biblioteca alternativa para la deserialización, como `dill`, que básicamente es un contenedor seguro para pickle.

Una de las vulnerabilidades de pickle es que puede permitir la ejecución de código arbitrario durante la deserialización. En este caso, un atacante puede insertar un archivo pickle malicioso que contenga código que se ejecutará cuando se deserialice el archivo. dill resuelve este problema al permitirte especificar qué módulos o funciones se pueden deserializar, lo que evita la ejecución de código arbitrario durante la deserialización y hace que dill sea más seguro que pickle.

No olvides eliminar los secretos

Al trabajar con datos confidenciales, es sumamente importante almacenar los secretos de forma segura. Si no los almacenas de manera segura, un atacante podría acceder a ellos y usarlos para obtener acceso no autorizado a tus sistemas y datos. Puedes evitarlo con una biblioteca de administración de secretos, como keyring, passlib, pycryptodome o pycrypto, que los almacenará en un formato cifrado.

Además, debes cambiar tus secretos periódicamente. Esto reduce el riesgo de que se vean comprometidos. Algunas de las herramientas mencionadas ofrecen esta funcionalidad, al igual que otras como HashiCorp Vault.

Usa una herramienta para analizar tu código periódicamente y detectar vulnerabilidades

Como viste aquí, es importante conocer las distintas amenazas que pueden afectar tu proyecto de Python, especialmente en la administración de dependencias, el formato de cadenas de texto, las consultas SQL sin procesar y la deserialización. Quizá puedas resolver todos estos problemas por tu cuenta, pero no es lo recomendable. Es mejor usar una herramienta que analice tu proyecto y resuelva los problemas de vulnerabilidades.

Una herramienta como Snyk puede facilitarte el trabajo al analizar tu código periódicamente para detectar vulnerabilidades. Con Snyk, puedes identificar y corregir posibles problemas en tu proyecto de Python. Esto no solo ayuda a proteger tu proyecto, sino que también reduce la cantidad de errores y problemas de funcionalidad en tu aplicación.

Conclusión

En este tutorial aprendiste a crear un paquete de Python. También aprendiste por qué es útil para organizar y escribir código, y cómo hacerlo permite reutilizarlo y mantenerlo.

En este tutorial también profundizaste en la importancia de conocer las distintas amenazas y vulnerabilidades que pueden afectar tu proyecto. Aprendiste a abordar algunas por tu cuenta escribiendo más código. Sin embargo, no todas las organizaciones o equipos tienen la capacidad para hacerlo y, aunque la tengan, no deberían hacerlo. Es mejor usar un producto integral y estándar que se encargue continuamente de las vulnerabilidades y amenazas para facilitarte el trabajo.

Un producto como Snykr puede ayudarte a mitigar estas amenazas mediante un análisis exhaustivo de tu código para identificar vulnerabilidades y corregir automáticamente algunos de esos problemas.

Empieza a proteger tus aplicaciones de Python

Encuentra y corrige vulnerabilidades en Python gratis con Snyk.

No se requiere tarjeta de crédito.

O regístrate con Azure AD Docker ID Bitbucket

Al usar Snyk, aceptas cumplir nuestras políticas, incluidos nuestros Términos del servicio y nuestra Política de privacidad.

Leer más

illustration hero ai
Blog

PHP corrige dos vulnerabilidades críticas de RCE sin autenticación (CVE-2026-109618, CVE-2026-109619): actualiza ahora

Dos fallas críticas en el ciclo de solicitudes de PHP pueden permitir la ejecución remota de código sin autenticación. Actualiza a una versión corregida o instala una corrección retroportada por un proveedor para las versiones anteriores.

blog feature ai lilac
Blog

Convierte tu backlog de vulnerabilidades en pull requests con la remediación agéntica

Evo Remediation Agent ya está disponible en Open Preview e incorpora la resolución automatizada de dependencias a Evo Agentic AppSec. Ahora, los equipos de AppSec pueden ejecutar campañas de remediación continuas

AI Background min crop
Blog

La IA activa todos los riesgos que has aceptado: siete ideas del panel de seguridad de IA sin filtros

La IA hace que los ataques sean más baratos y rápidos, y deja al descubierto riesgos que las organizaciones ya enfrentan. Cuatro líderes de seguridad explican por qué la defensa a velocidad de máquina y la corrección más rápida son esenciales.