La guía definitiva para crear un paquete de Python seguro
Gourav Singh Bais
8 de mayo de 2024
0 minutos de lecturaCrear 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.
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:
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:
También puedes importar varios módulos o todos los módulos de un paquete con los siguientes comandos:
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:
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
srccon un subdirectorio para el nombre del paquete, que incluya un archivo de inicialización__init__.py. El directoriosrctambién contendrá uno o más archivosmodule.pycon el código fuente del paquete.Un directorio
testscon scripts para probar distintas funciones del paquete. Tú decides si quieres incluir este directorio en el paquete.Un archivo
pyproject.tomlcon los detalles de configuración y las dependencias del paquete.Un archivo
LICENSEque especifica los términos de uso del paquete. Puedes elegir entre muchas licencias o crear la tuya.Un archivo
README.mdcon 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:
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:
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:
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:
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:
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:
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:
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:
Luego, ve al directorio que contiene el archivo pyproject.toml y ejecuta el siguiente comando:
Esto creará una carpeta dist/ en la misma ubicación que el archivo .toml, con dos archivos:
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:
Una vez instalado twine, puedes comprobar con este comando si la descripción de tu paquete se mostrará correctamente en PyPI:
El resultado del comando anterior será similar a este:
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:
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:
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:
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:
Una vez instalado el paquete, abre cualquier editor de Python o entorno de ejecución de Python y comprueba si funciona:
El resultado debería verse así:
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
.whlo.tar.gzpara distribuirlo.Configura tu servidor web para que sirva el directorio raíz del paquete con la indexación automática habilitada.
Usa
twinepara 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.



