Cómo subir un paquete a PyPI

Recientemente un compañero mío quería distribuir una pequeña librería en Python que había escrito. En lugar de forzar a los desarrolladores a clonar su repositorio, quería que pudieran instalarse la librería con un sólo comando “pip install“. Planteamiento muy interesante que se formaliza con el concepto de gestor de paquetes.

Un gestor permite un uso más inteligente, ágil y seguro de las dependencias y ayuda tanto al que distribuye el paquete como al que lo usa, resolviendo problemas como el versionado, la cadena de dependencias dentro del paquete, la instalación…

Quisiera a fin de fomentar el uso del gestor de paquetes de Python llamado pip, que usa como repositorio oficial PyPI (Python Package Index), generar una guía lo más completa posible. ⚠️ Me gustaría remarcar que con ello no quiero ni pretendo incentivar que cualquiera se dedique a subir de cualquier forma lo primero que se lo ocurra. Aludo a la responsabilidad de cada uno, para no convertir PyPI en un vertedero de paquetes de dudosa calidad. ⚠️

¿Qué es PyPI?

Desde la web oficial:

PyPI — Índice de Paquetes Python

El Índice de Paquetes Python es un repositorio de software para el lenguaje de programación de Python.

¿Has programado algo genial? ¿Quieres que otros puedan instalarlo con easy_install o pip? Pon tu código en PyPI. Es una gran lista de paquetes de Python donde debes enviar tu paquete para que pueda instalarse fácilmente con uno sólo comando.

La buena noticia es que enviar un paquete a PyPI en la teoría es muy simple: registrarte y cargar tu código, todo de manera gratuita. La mala noticia es que en la práctica es un poco más complicado que eso 😅. La otra buena noticia es que he escrito esta guía 😁 y que, si estás atascado, siempre puedes consultar la documentación oficial 😉.

He escrito esta guía con los siguientes supuestos:

  •      El módulo / biblioteca / paquete que está enviando se llama mypackage.
  •      mypackage está alojado en GitHub.

Crear tu cuenta

Crea una cuenta en PyPI Live y también en PyPI Test. Debes crear una cuenta para poder cargar tu código. Te recomiendo usar el mismo correo electrónico y contraseña para ambas cuentas, sólo para hacerte la vida más fácil cuando llegue el momento de subir tu código. Ambas plataformas son idénticas, siendo la de test una réplica de la oficial para que pruebes a subir tus paquetes.

Crear un archivo de configuración .pypirc

Este archivo contiene su información para la autenticación con PyPI, tanto la versión en vivo como la versión de prueba.

[distutils]
index-servers =
  pypi
  pypitest

[pypi]
repository=https://upload.pypi.org/legacy/
username=your_username
password=your_password

[pypitest]
repository=https://test.pypi.org/legacy/
username=your_username
password=your_password

Esto es sólo para hacer tu vida más fácil, para que cuando llegue el momento de subir el código de de tu paquete no tengas que recordar/escribir tu nombre de usuario y contraseña. Asegúrate de poner este archivo en tu carpeta de inicio de tu sistema GNU/Linux; tu ruta debe ser:

~/.pypirc

Debido a que este archivo contiene tu nombre de usuario y contraseña, es posible que desees cambiar sus permisos para que sólo tú puedas leerlos y escribirlos. Desde la terminal, ejecuta:

chmod 600 ~/.pypirc

Si por el contrario te encuentras en un sistema Windows genera el archivo .pypirc en la carpeta de tu usuario en C:\Users\usuario con el siguiente comando:

type nul > your_file.txt
👁️ Notas sobre usernames / passwords

En Python 3, si tu contraseña incluye un % sin procesar, debes generar una secuencia de escape duplicándolo. El analizador de configuración de .pypirc interpola las cadenas de texto. Por ejemplo, si tu contraseña es hello%world:

[pypi]
repository=https://pypi.python.org/pypi
username=myusername
password=hello%%world

Nunca me he encontrado con este problema, pero si te ocurre, esto podría ayudar. 😉

Este comportamiento de escape ha sido parcheado y ya no es necesario hacerlo, pero si ves un error con un código de respuesta de:

 403: Invalid or non-existent authentication information 

Intenta eliminar la secuencia de escape de los signos de porcentaje en tu contraseña.

Si tu contraseña incluye espacios, asegúrese de no entrecomillarla. Por ejemplo, si tu contraseña es me encanta Mascando Bits:

[pypi]
repository=https://pypi.python.org/pypi
username=myusername
password=me encanta MascandoBits

Preparar el paquete

Cada paquete en PyPI necesita tener un archivo llamado setup.py en la raíz del directorio. Si estás utilizando un archivo README en formato markdown, también necesitará un archivo MANIFEST.in. Además, es recomendable elegir una licencia para tu paquete reflejada en un archivo LICENSE que describa lo que se puede hacer con tu código. Entonces, si por ejemplo he estado trabajando en una biblioteca llamada mypackage, la estructura de mi directorio se vería tal que así:

root-dir/   # nombre de directorio de trabajo aleatorio
  setup.py
  MANIFEST.in
  LICENSE.txt
  README.md
  mypackage/
    __init__.py
    foo.py
    bar.py
    baz.py

A continuación un desglose de lo que va en cada archivo:

setup.py

Son los metadatos de la librería necesarios para generar el paquete.

from setuptools import setup

setup(
    name='mypackage',
    packages=['mypackage'], # Mismo nombre que en la estructura de carpetas de arriba
    version='0.1',
    license='LGPL v3', # La licencia que tenga tu paqeute
    description='A random test lib',
    author='RDCH106',
    author_email='contact@rdch106.hol.es',
    url='https://github.com/RDCH106/mypackage', # Usa la URL del repositorio de GitHub
    download_url='https://github.com/RDCH106/parallel_foreach_submodule/archive/v0.1.tar.gz', # Te lo explico a continuación
    keywords='test example develop', # Palabras que definan tu paquete
    classifiers=['Programming Language :: Python',  # Clasificadores de compatibilidad con versiones de Python para tu paqeute
                 'Programming Language :: Python :: 2.7',
                 'Programming Language :: Python :: 3.3',
                 'Programming Language :: Python :: 3.4',
                 'Programming Language :: Python :: 3.5',
                 'Programming Language :: Python :: 3.6',
                 'Programming Language :: Python :: 3.7'],
)

El parámetro download_url es un enlace a un archivo alojado con el código de tu repositorio. Github alojará esto para ti, pero solo si creas una etiqueta git tag. En tu repositorio, escribe en la línea de comandos:

git tag v0.1 -m "Agrega una etiqueta para que podamos poner esto en PyPI"

Luego, escribe el siguiente comando para mostrar la lista de etiquetas:

git tag

Deberías ver v0.1 en la lista.

Por último escribe el siguiente comando para actualizar tu código en Github con la información de etiqueta más reciente:

git push --tags origin master

Github creará archivos comprimidos para descargar en https://github.com/{username}/{package_name}/archive/v{tag}.tar.gz.

MANIFEST.in

Incluye archivos que se empaquetarán en al distribución del paquete.

include LICENSE.txt README.md

LICENSE.txt será el archivo que contiene la licencia y README.md será el fichero que contenga la información básica (instalación, uso…) que quieras distribuir con tu paquete.

Subir tu paquete a PyPI Test

Ejecuta:

python setup.py sdist upload -r pypitest

No debería recibir ningún error si seguiste los pasos hasta este punto, y ahora también deberías poder ver tu paquete en el repositorio PyPI de prueba. Si hay algo que no te gusta este es el momento de hacer los cambios que quieras. Eso sí, para subir el paquete de nuevo tendrás que editar el número de versión para que sea distinto al de la anterior vez.

⚠️ PyPI te permite borrar las versiones del paquete y subir todos las versiones que quieras, pero no editar una versión de tu paquete subida. Esto es para mantener la estabilidad de las dependencias. Es posible retirar una versión, creando la consecuente ruptura de dependencias, pero no se deja modificar para una versión de un paquete por no desvirtuar el sentido mismo del versionado.

Es posible que en algunas guía o documentación hayas visto que también se hace previo al upload un comando register:

python setup.py register -r pypitest

Ya no es necesario y no está soportado:

https://packaging.python.org/guides/migrating-to-pypi-org/#registering-package-names-metadata


Subir tu paquete a PyPI Live

Ejecuta:

python setup.py sdist upload -r pypi

Y ya, !lo has hecho! 🎉🎉🎉 Has publicado tu primer paquete en PyPI y podrá ser instalado con el gestor de paquetes pip o cualquier otro como conda.

Configuración de entorno Python2 y Python3 para uso simultáneo en Debian

Si eres desarrollador de Python o usuario de su ecosistema, te habrás dado cuenta de la brecha existente entre ambos, significando una ruptura de compatibilidad el salto de Python2 a Python3.

Pese a que Python2 tiene fecha de caducidad todavía existen aplicaciones y librerías que bien porque no han sido migradas a Python3, o porque tienen dependencias con librerías que aún no se han migrado a Python3, siguen funcionado exclusivamente sobre Python2. Por eso se hace necesario mantener los dos interpretes de Python con su respectivo respectivo gestor de paquetes pip.

Quizás a la hora de abordar esta situación, la plataforma de Windows tenga la aproximación más razonable e interesante. En la instalación de Windows pueden convivir ambos intérpretes, siendo Python3 el que prevalece por defecto si ambos existen, quedando vinculadas las palabras python y pip al intérprete y gestor de paquetes de Python3 respectivamente.

Si queremos diferenciar la ejecución entre ambos, tenemos py2 para referirnos al intérprete de Python2 y py3 para el de Python3. De la misma manera, si queremos referirnos al gestor de paquetes de Python2 lo haremos con pip2 y para referirnos al de Python3 lo haremos con pip3.

Esta estupenda idea no se da en sistemas Debian y derivados como Ubuntu. A pesar de todo Debian 8 (Jessie) trae Python 3.4 y Debian 9 (Stretch) Python 3.5, en ambos casos junto con Python 2.7, siendo Python 2.7 el intérprete Python por defecto. Para tener ambos entornos vamos a realizar las una serie de configuraciones.

Para ellos vamos a crear los siguientes alias:

alias py2='/usr/bin/python2.7'
alias py3='/usr/bin/python3.4' # Para Debian 8 Jessie
alias py3='/usr/bin/python3.5' # Para Debian 9 Stretch

Con esto si ejecutamos py2 o py3 tendremos configurado ambos intérpretes para usarlos según nos convenga.

Ahora tenemos que instalar los gestores de paquetes pip para Python2 y Python3, para ello ejecutamos los siguientes comandos:

apt-get install python-pip  # python2
apt-get install python3-pip  # python3

Ahora generamos los alias:

alias pip2='/usr/bin/pip' # python2
alias pip3='/usr/bin/pip3' # python3

Si deseamos listar los alias registrar podemos hacerlo escribiendo “alias” o “alias -p“. Si deseamos eliminar un alias sería “unalias nombre_del_alias” o “unalias -a” si queremos borrar todos.

Hasta aquí ya tenemos nuestros dos entornos para funcionar simultáneamente, pero cuidado:

⚠️ Los alias se pierden al cerrar la sesión

Para arreglarlo sólo tenemos que poner los alias que queramos tener en la sesión en el archivo ~/.bash_aliases:

alias py2='/usr/bin/python2.7'
alias py3='/usr/bin/python3.5' # Para Debian 8 Jessie
alias py3='/usr/bin/python3.5' # Para Debian 9 Stretch
alias pip2='/usr/bin/pip'
alias pip3='/usr/bin/pip3'

Para que se carguen al iniciar sesión tenemos que asegurarnos que en el archivo ~/.bashrc existan la siguientes líneas:

if [ -f ~/.bash_aliases ]; then
. ~/.bash_aliases
fi

En caso de no encontrarlas, puedes añadirlas al final del fichero. En el caso de estar usando el usuario root es muy probable que tengas que añadir las líneas.

Para abrir y editar los archivos puedes usar nano:

nano ~/.bashrc
nano ~/.bash_aliases

Llegados a este punto ya tenemos los entornos de Python2 y Python3 completamente configurados pudiendo elegir con qué ejecutar nuestras aplicaciones y pudiendo lanzar ejecuciones con ambos intérpretes de manera simultánea. 😉

 

Desbloquear el perfil DUN (Dial Up Networking) para compartir acceso a Internet en Android

¿Alguna vez te ha pasado que no has podido compartir vuestra conexión móvil en cierto dispositivo móvil? ¿Has llamado al soporte de tu compañía y no te han conseguido arreglar el problema? ¿Incluso has mirado foros especializados y nada? Pues la explicación de este problema y como resolverlo creo que te va a interesar.

Las compañías que te proveen Internet móvil se han vuelto muy cómodas e ignorantes y a día de hoy, por lo menos en España, aunque existen varias marcas, realmente son 4 holdings (Movistar, Orange, Vodafone y MásMóvil). La necesidad de crecer las lleva a absorber toda aquella empresa que les pueda hacer competencia o sacar ventaja, y esto da como resultado que la composición de las redes de estos grupos sean bastante heterogéneas en cuanto a su configuración. Esto no sería un problema si no fuera porque no se preocupan más que de que tu teléfono le funcionen los datos y da igual cómo, ignorando los perfiles de comunicación que usa la marca y si estos están activados o no en los dispositivos de manera estándar.

Si has leído hasta aquí habrás intuido que ciertas cosas funcionan por “chiripa” y no vas desencaminado. Si para poder compartir tus datos te han comentado desde el soporte que actives en el APN (en Ajustes->Redes móviles) el perfil de línea conmutada DUN (Dial Up Networking), o lo has visto como solución en un foros especializado, y no te ha funcionado es porque tu móvil no lo tiene convenientemente pre-configurado.

En realidad este perfil viene desactivado en bastante móviles y algunas marcas no utilizan el perfil DUN para posibilitar la compartición de datos desde el móvil.

Si eres curioso por los perfiles del APN que ves te dejo una peque lista de los más comunes:

  • default: configuración por defecto para la conexión a Internet.
  • mms: (Multimedia Messaging System) permite el envío de mensajes MMS.
  • supl: (Secure User Plane Location), permite que el teléfono determine la ubicación geográfica mediante posicionamiento GPS.
  • dun: (Dial Up Networking), permite compartir Internet con otros terminales mediante tethering.
  • wap: (Wireless Application Protocol) y está relacionada con la navegación a través de la red móvil (al seleccionar el perfil default no es necesario marcarla).

Si te has fijado el perfil WAP queda absorbido por default, pero el perfil DUN que debería estar también absorbido por el perfil default en algunas compañías no lo está y es aquí donde se encuentra el problema. Si por alguna razón tu dispositivo móvil no tiene configurado el perfil DUN a nivel de sistema, por mucho que lo configures en el APN no tendrá efecto.

Una vez definido el problema, es cómo solucionarlo. En el caso de Android, la solución puede ser más o menos sencilla dependiendo de si tenemos acceso root o no. Dicho acceso permite acceder a la administración completa desde el dispositivo. dependiendo el dispositivo que tengas y la versión de Android que tenga esto puede ser más o menos sencillo. Yo voy a explicarlo usando la herramienta Kingo Root, la cual me funcionó y es relativamente sencillo hacerlo.

Para ello procedemos a la descarga el instalador .apk en nuestro dispositivo, pero para eso antes deberemos desactivar el “Play Protect“. Vamos al Play Store:

Y en Play Protect, desactivamos “Buscar amenazas de seguridad”:

Esto es a causa de que Google defiende el sistema para evitar que sea “rooteado”.

Instalamos la aplicación. Si no desactivamos “Play Protect” obtendremos mensajes como estos que nos bloquean la instalación:

O que nos invitan desinstalarla:

Si no es nuestro caso, una vez instalada nos arrancará la aplicación y le damos a “ROOT NOW“:

La aplicación nos irá mostrando el progreso realizado:

Si volvemos a la pantalla principal del la aplicación veremos el mensaje “HAVE ROOT” con un círculo verde que nos indica que nuestro dispositivo ha sido rooteado:

En estos momentos ya tenemos posibilidad de gestionar permisos root. Ahora deberemos instalar el gestor de ficheros Es Explorado de Archivos, un explorador de archivos enriquecido con funcionalidades de todo tipo, entre la que se encuentra el la posibilidad de ver los archivos del sistema marcando la opción “Explorador Root“:

Y tras lo cual nos pedirá los permisos root a “Kingo SuperUser“, a lo que damos a “PERMITIR“:

Ahora vamos a necesitar otra aplicación llamada aSQLiteManager. Con ella instalada vamos a movernos a la siguiente ruta con ES Explorador de Archivos:

/data/data/com.android.providers.settings/databases/settings.db

Al abrirlo nos hará como ES Explorador de Archivos y pedirá permisos root a “Kingo SuperUser” para aSQLiteManager:

El propio aSQLiteManager nos volverá a preguntar si queremos dar acceso root:

Por precaución creamos una copia de settings.db (por si algo fuese mal poder restaurar la original) y lo abrimos con aSQLiteManager:

El mensaje que nos aparece es meramente informativo y le damos a “OK“. Tras lo cual nos abrirá finalmente la base de datos:

Aquí tenemos en esta base de datos gran parte de la información  de configuración del funcionamiento de Android. Para arreglar nuestro problema con las interfaces DUN agregaremos una entrada en la base de datos:

Rellenamos únicamente el campo name con tether_dun_required y el campo value con 0. El _id es mejor dejarlo libre ya que se trata de un valor auto-incrementado, cogerá el siguiente disponible. Cuando le demos a “OK” veremos como queda añadida la nueva entrada:

Al salir de la base de datos nos preguntará si queremos hacer “commit” de los cambios realizados a lo que afirmamos de manera afirmativa dándole a “Sí”:

Un vez hemos hecho esto debería funcionar nuestro tethering y podremos compartir nuestros datos con otros dispositivo mediante la generación de un punto WiFi.

Al meter la entrada que hemos metido en la base de datos, se ha desactivado la obligatoriedad del uso requerido del perfil DUN para hacer tethering. Las operadoras que no configuran un perfil DUN en su APN no se ven afectadas por esta configuración ausente en algunos dispositivos y del valor por defecto que se le asigna el sistema operativo que evita que funcione.

Una vez hemos acabado la faena, mantener permisos root y por consiguiente rooteado el dispositivo, personalmente no me parece una buena idea, con lo que procederemos a “desrootear” el mismo. Para ello vamos a Kingo SuperUser y buscamos la sección de “Ajustes“:

Le damos a “Eliminar Root”:

Confirmamos que queremos proceder a su eliminación dando en “CONTINUAR“:

Ahora si accedemos a

En Ajustes buscamos Kingo SuperUser nos mostrará un mensaje “NO ROOT” con un círculo rojo que nos indica que nuestro dispositivo ha sido desrooteado:

Ya hemos terminado y hemos dejado nuestro dispositivo como al inicio pero con la posibilidad de compartir datos haciendo tethering desde el móvil.

 

Feliz Navidad y Próspero Año 2019

Otro año toca a su fin y las vacaciones Navideñas señalan el preludio de su ocaso. Este ha sido un año especialmente duro para mí, repleto de cambios en el ámbito personal y profesional, y no siempre con el tiempo que me hubiera gustado tener para poder publicar. Aunque mi actividad no se ha detenido, sí que he tenido que aminorar bastante el tema de la publicación en detrimento de ciertos proyectos software en GitHub.

Agradezco especialmente el aumento de comentarios durante este año, que me hacen ver que hay gente al otro lado, y especialmente agradecer aquellos comentarios que simplemente eran de agradecimiento por la utilidad de alguna de las entradas. Se agradece un montón y me anima a seguir ahí aunque sea a un ritmo mucho menor 🙂 .

Quizás la canción más representativa de lo que ha sido este año para mí, pueda ser la de Karma que os dejo a continuación:

 

 

A veces nos vemos  afectados por muchas cosas y parece que el Karma no está por ningún lado, pero solo hay una forma correcta de hacer las cosas y es hacerlas bien 😉 .

Sólo me resta desearos con mis mejores deseos ¡Felices Fiestas! (para los agnósticos y atéos), ¡Feliz Navidad! (para los creyentes) y ¡Feliz Año 2019! (para TODOS 😉 ). ¡Nos vemos a la vuelta!