Coffee bytes

Apuntes de desarrollo web con GNU/Linux, Python y Javascript

Pipenv: El administrador de entornos virtuales que NO conoces

El sábado, 15 de agosto de 2020

por Eduardo Zepeda

Tiempo de lectura: 9 minutos

Desde que empecé a usar Python uso virtualenv y pip para manejar los entornos virtuales. Pero al leer Django for Professionals me enteré de que existía una herramienta mejor que pip y virtualenv, llamada Pipenv (no se complicaron mucho con el nombre). Pipenv tiene características que la hacen mucho más robusta y sencilla de utilizar que virtualenv. En este tutorial de Pipenv paso a paso, te voy a explicar la instalación, uso, manejo de archivos y comandos básicos de esta herramienta.

Primero, si ya has oído hablar de los entornos virtuales pero no sabes para que sirven dale una leída a esta entrada. Por otro lado, si el nombre de virtualenv te suena medio esotérico dale una revisada a esta entrada y te lo explico.

Pipenv vs virtualenv

Seguramente ya sabes que pip es usado para manejar paquetes, pero generalmente deseamos tener los paquetes de cada una de nuestras aplicaciones aislados del resto del sistema, por lo que normalmente lo combinamos con virtualenv.

Pip y virtualenv se usan en conjunto para mantener las dependencias de un entorno virtual, pero pip puede llegar a producir entornos diferentes, incluso con un mismo archivo requirements.txt, esto es algo que queremos evitar. El creador de pipenv diseño su herramienta intentando resolver esa problemática.

Pipenv se encarga de unir a pip y a virtualenv en una sola herramienta, además de asegurarse de que el archivo donde se listan las dependencias que se generan produzca exactamente la misma configuración de paquetes, pipenv también permite cargar archivos variables de entorno directamente a partir de archivos .env que se encuentren en la carpeta de trabajo donde nos encontremos.

Instalación y uso de pipenv

Si estás en Debian o alguna distribución derivada (como Ubuntu) puedes probar suerte intentando instalarlo directamente de los repositorios.

sudo apt install pipenv

Si no se encuentra en los repositorios también podemos hacer uso de pip, que ya viene instalado en la mayoría de las distribuciones.

sudo pip install pipenv

Una vez instalado podemos empezar a instalar paquetes usando la opción install, para este ejemplo probemos con una versión específica de Django.

pipenv install django===3.0.1

Si hacemos un ls podremos notar que se nos crearon dos archivos Pipfile y Pipfile.lock.

ls
Pipfile  Pipfile.lock

¿Qué tienen estos archivos? Te lo explico a continuación. Primero vamos con el archivo Pipfile.

Pipfile

Empecemos viendo el contenido del archivo Pipfile. Si tienes alguna dificultado con el uso de la linea de comandos te sugiero revisar las entradas donde hablo de los comandos más comunes de GNU/Linux.

cat Pipfile
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]

[packages]
django = "===3.0.1"

[requires]
python_version = "3.7"

Analizando el contenido apreciamos este archivo nos muestra varias categorias

  • source: la fuente de nuestros paquetes, con su nombre, url y si se usó encripción
  • dev-packages: los paquetes de desarrollo, en este momento se encuentra vacio
  • packages: los paquetes que hemos instalado y que se usarán en el proyecto
  • requires: la versión de Python requerida para el proyecto, se especifica automáticamente o puedes hacerlo tu mismo
pipenv install --dev pytest

Si hacemos un cat nuevamente veremos que debajo de la sección [dev-packages] ya nos aparece pytest como una dependencia.

cat Pipfile
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]
pytest = "*"

[packages]
django = "===3.0.1"

[requires]
python_version = "3.7"

Pipfile.lock

Ahora hagamos un cat a Pipfile.lock

cat Pipfile.lock
{
    "_meta": {
        "hash": {
            "sha256": "c2bf0d0008c675fc08df79a9cdb6b94773be0defa60d2c5b8aae0142358aa574"
        },
        "pipfile-spec": 6,
        "requires": {
            "python_version": "3.7"
        },
        "sources": [
            {
                "name": "pypi",
                "url": "https://pypi.org/simple",
                "verify_ssl": true
            }
        ]
    },
    "default": {
        "asgiref": {
            "hashes": [
                "sha256:7e51911ee147dd685c3c8b805c0ad0cb58d360987b56953878f8c06d2d1c6f1a",
                "sha256:9fc6fb5d39b8af147ba40765234fa822b39818b12cc80b35ad9b0cef3a476aed"
            ],
            "markers": "python_version >= '3.5'",
            "version": "==3.2.10"
        },
        "django": {
            "hashes": [
                "sha256:315b11ea265dd15348d47f2cbb044ef71da2018f6e582fed875c889758e6f844",
                "sha256:b61295749be7e1c42467c55bcabdaee9fbe9496fdf9ed2e22cef44d9de2ff953"
            ],
            "index": "pypi",
            "version": "===3.0.1"
        },
...

El archivo puede parecer muy apantallante, pero son únicamente los hashes de los paquetes que instalamos, así como sus dependencias, de esta manera nos aseguramos de que las versiones que instalamos sean las correctas y además nos permitirá obtener exactamente la misma configuración de paquetes si tomamos estos archivos y los llevamos a otra computadora.

¿Cómo visualizar las dependencias de manera gráfica?

Si ahora usamos el comando pipenv graph nos generará una representación detallada y visualmente amigable de las dependencias que tenemos instaladas

pipenv graph
--Django==3.0.1
--  - asgiref [required: ~=3.2, installed: 3.2.10]
--  - pytz [required: Any, installed: 2020.1]
--  - sqlparse [required: >=0.2.2, installed: 0.3.1]
--pytest==5.4.3
--  - attrs [required: >=17.4.0, installed: 19.3.0]
--  - importlib-metadata [required: >=0.12, installed: 1.7.0]
--    - zipp [required: >=0.5, installed: 3.1.0]
--  - more-itertools [required: >=4.0.0, installed: 8.4.0]
--  - packaging [required: Any, installed: 20.4]
--    - pyparsing [required: >=2.0.2, installed: 2.4.7]
--    - six [required: Any, installed: 1.15.0]
--  - pluggy [required: >=0.12,<1.0, installed: 0.13.1]
--    - importlib-metadata [required: >=0.12, installed: 1.7.0]
--      - zipp [required: >=0.5, installed: 3.1.0]
--  - py [required: >=1.5.0, installed: 1.9.0]
--  - wcwidth [required: Any, installed: 0.2.5]

Generar un archivo Pipfile.lock

También podemos generar un archivo Pipfile.lock a partir de un archivo Pipfile. Borremos el archivo Pipfile.lock y generemos uno nuevo

rm Pipfile.lock

Ahora ejecutemos el comando pipenv lock

pipenv lock
--Locking [dev-packages] dependencies…
--Building requirements...
--Resolving dependencies...
--✔ Success! 
--Locking [packages] dependencies…
--Building requirements...
--Resolving dependencies...
--✔ Success! 
--Updated Pipfile.lock (88888)!

Al terminar el proceso tendremos nuevamente nuestro archivo Pipfile.lock en la misma carpeta

Encontrar un entorno virtual con pipenv

Todo bien hasta este momento, pero aún no estamos dentro de nuestro entorno virtual, es más, solamente tenemos los archivos Pipfile y Pipfile.lock en nuestra carpeta actual. ¿Y el entorno virtual? Bueno, pipenv coloca el entorno virtual en otra ubicación, para averiguarla podemos usar la opción –venv

pipenv --venv
--/home/usuario/.local/share/virtualenvs/proyecto-HHqROqC2

Y ahora, en lugar de localizar el archivo activate manualmente en la ruta anterior, como hacíamos con virtualenv, podemos activar el entorno virtual usando el comando pipenv shell y esto se hará por nosotros automáticamente

pipenv shell

De igual manera que con virtualenv podemos apreciar que el prompt cambiará, indicándonos que estamos dentro del entorno virtual

Variables de entorno con Pipenv

Una de las características que hacen diferente a pipenv es que te permite cargar variables de entorno directamente a partir de un archivo .env cuando entramos en un entorno virtual. Salgamos del entorno virtual un momento para crear el archivo y cargar variables de entorno.

exit

Ahora que el prompt regresó a la normalidad, crearemos un archivo .env con variables de entorno. Lo haré en un solo paso usando el comando echo y redirigiendo el resultado al archivo, pero si te sientes más cómodo usando el comando touch y luego abriéndolo para agregar el contenido también puedes hacerlo y es correcto.

echo "SPAM=eggs" > .env

Si hacemos un ls podremos ver que ahora tenemos nuestro archivo .env en la misma carpeta que están Pipfile y Pipfile.lock

ls -a
--.  ..  .env  Pipfile  Pipfile.lock

Ahora volvamos a cargar nuestro entorno virtual

pipenv shell
--Loading .env environment variables…
--Launching subshell in virtual environment…
-- . /home/usuario/.local/share/virtualenvs/proyecto-HHqROqC2/bin/activate

El prompt cambiará nuevamente, y, si ejecutamos el comando printenv podemos ver que nuestra variable de entorno se agregó perfectamente.

printenv
--...
--SPAM=eggs
--...

Desinstalar paquetes en pipenv

Para desinstalar paquetes usaremos el comando pipenv uninstall y el nombre del paquete.

pipenv uninstall pytest
--Uninstalling pytest…
--Found existing installation: pytest 5.4.3
--Uninstalling pytest-5.4.3:
--  Successfully uninstalled pytest-5.4.3
--
--Removing pytest from Pipfile…
--Locking [dev-packages] dependencies…
--Locking [packages] dependencies…
--Building requirements...
--Resolving dependencies...
--✔ Success! 
--Updated Pipfile.lock (3f348b)!

Si queremos desinstalar todos los paquetes y dejar nuestro entorno como nuevo podemos usar la opción –all en lugar de especificar un nombre de paquete. Esto borrará todos los archivos del entorno virtual pero dejará el Pipfile completamente a salvo.

pipenv uninstall --all
--Un-installing all [dev-packages] and [packages]…
--Found 13 installed package(s), purging…
--...
--Environment now purged and fresh!

Si usamos la opción –all-dev eliminará todas las dependencias de desarrollo, tanto como del entorno virtual como de nuestro Pipfile

pipenv uninstall --all-dev
--Un-installing [dev-packages]…
--Locking [dev-packages] dependencies…
--Locking [packages] dependencies…
--Building requirements...
--Resolving dependencies...
--✔ Success! 
--Updated Pipfile.lock (65a03c)!

Ejecutar comandos en entorno virtual con pipenv

También podemos ejecutar comandos directamente en el entorno virtual sin estar dentro. Salte del entorno virtual si estás dentro y asegurate de que el prompt haya regresado a la normalidad antes de ejecutar el siguiente comando.

pipenv run pip install requests
--Loading .env environment variables…
--Collecting requests
--  Downloading requests-2.24.0-py2.py3-none-any.whl (61 kB)
--     |████████████████████████████████| 61 kB 53 kB/s 
--Collecting idna<3,>=2.5
--  Downloading idna-2.10-py2.py3-none-any.whl (58 kB)
--     |████████████████████████████████| 58 kB 641 kB/s 
--Collecting chardet<4,>=3.0.2
--  Using cached chardet-3.0.4-py2.py3-none-any.whl (133 kB)
--Collecting certifi>=2017.4.17
--  Downloading certifi-2020.6.20-py2.py3-none-any.whl (156 kB)
--     |████████████████████████████████| 156 kB 6.1 MB/s 
--Collecting urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1
--  Using cached urllib3-1.25.9-py2.py3-none-any.whl (126 kB)
--Installing collected packages: idna, chardet, certifi, urllib3, requests
--Successfully installed certifi-2020.6.20 chardet-3.0.4 idna-2.10 requests-2.24.0 urllib3-1.25.9

El comando anterior nos dejó con el paquete requests y sus dependencias instaladas en nuestro entorno virtual, sin embargo el archivo Pipfile y Pipfile.lockno se actualizaron. Para borrar todos aquellos paquetes instalados que no se encuentren en los dos archivos anteriores existe pipenv clean.

Limpiar nuestro entorno virtual en pipenv

También podemos limpiar nuestro entorno virtual de todos aquellos paquetes que no estén especificados dentro de nuestro archivo Pipfile.lock usando clean.

pipenv clean
--Uninstalling urllib3…
--Uninstalling idna…
--Uninstalling requests…
--Uninstalling chardet…
--Uninstalling certifi…

Listo, nuestro entorno no contiene ningún paquete instalado, sin embargo aún existe.

Borrar un entorno virtual en pipenv

Para borrar un entorno virtual usaremos la opción –rm seguida del comando pipenv. Nota que pipenv detectará el entorno virtual a remover extrayendo la información de la carpeta donde nos encontremos, por lo que asegúrate dos veces que estás en la carpeta correcta.

pipenv --rm
--Removing virtualenv (/home/usuario/.local/share/virtualenvs/prueba-HHqROqC2)

¡Listo! El entorno virtual ha quedado eliminado completamente.

Si quieres conocer más funciones de pipenv puedes visitar su documentación oficial.

¿Quieres aprender más?

Sígueme en Twitter y te aviso cuando tenga nuevo contenido disponible. ¡Es gratis!

¿Te pareció útil esta información?

Recibe más contenido como este en tu correo electrónico. Suscríbete, te toma unos segundos, es gratis, y puedes cancelar cuando quieras

* Campo obligatorio