A Cheatsheet for Python's Pipenv

Python’s Pipenv and Pyenv make a strong team for creating a consistent development environment for exact specifications. Pyenv allows you to choose from any Python version for your project. Pipenv attempts to improve upon the original virtual environment (venv) and requirements.txt file. It does some things well, including integration of virtual environment with dependecy management, and is straight-forward to use. Unfortunately, it doesn’t always live up to the originally-planned, ambitious, goals. This cheatsheet provides commands for functionality that works, and some work-arounds when it doesn’t.

Getting Started

Pyenv takes care of the python binary and all related tools. It stores everything under $PYENV_ROOT.

Pipenv takes care of:

  • calculating the complete set of dependencies;
  • (in installation mode) telling virtualenv and pip where to install the dependencies;
  • (in runtime mode) making available an environment with the right version of the Python interpreter (via pyenv) and the right set of packages (via virtualenv).

Note: Pipenv can interact with setup.py, but it is easy to confuse Pipenv functionality with that for package distribution and installation. The two are separate, and packaging is out of scope for Pipenv.

Pipenv creates a directory that maintains your dependencies. From the repo listed functionality, it has interesting interaction with existing technology:

  • Enables truly deterministic builds, while easily specifying only what you want.
  • Generates and checks file hashes for locked dependencies.
  • Automatically install required Pythons, if pyenv is available.
  • Automatically finds your project home, recursively, by looking for a Pipfile.
  • Automatically generates a Pipfile, if one doesn’t exist.
  • Automatically creates a virtualenv in a standard location.
  • Automatically adds/removes packages to a Pipfile when they are un/installed.
  • Automatically loads .env files, if they exist.

You may hear some harping over non-deterministic builds. This is because Python’s requirement.txt file does not dependency resolution of required modules, sub-dependency resolution (at least not well). This can cause big problems during automated deployments.

Pipenv solves these problems with Pipfile (which is meant to replace requirements.txt) and the Pipfile.lock (which enables deterministic builds). Pipenv uses pip and virtualenv under the hood but simplifies their usage with a single command line interface.

Prepare Python

Pyenv is about ensuring your have the correct python interpreter. This is similar to Node Version Manager (NVM) for Javascript.

Install pyenv

brew uninstall pyenv

Install Python version

pyenv install <version>    #such as, 3.7.2

Available versions

pyenv install --list

Change global / local (dir) versions

pyenv global <version>   #such as, 3.6.6
pyenv local <version>   #such as, 3.6.6

Prepare Development Environment

Pipenv creates all your virtual environments in a default location. If you want to change Pipenv’s default behavior, there are some environmental variables for configuration.

Install pipenv

pip3 install pipenv

Initiate directory

mkdir project; cd project;
pipenv --three   #or --two

Develop from existing repo

When an exact version isn’t specified in the Pipfile, the install command gives the opportunity for dependencies (and sub-dependencies) to update their versions.

pipenv install --dev

Activate

pipenv shell

Locate Virtualenv

pipenv --where          #output project home information.
pipenv --venv           #output virtualenv information.
whereis python

Check python version

python --version

Check path

python
>>> import sys
>>> sys.executable
quit()

Run with pipenv

pipenv run <command>    #such as, `python manage.py runserver`

Add scripts to Pipfile

#Pipfile
[scripts]
server = "python manage.py runserver"

And run with the following

pipenv run server

Managing Dependencies

Pipenv generates the file Pipfile.lock, which is used to produce deterministic builds. Issues can occur with Pipfile.lock, so it is sometimes prudent to create a requirements.txt file from your Pipenv, as a backup.

You specify the locations similarly to how you’d do so with pip. For example, to install the requests library from version control, do the following. Note the -e argument above to make the installation editable, this is required for Pipenv to do sub-dependency resolution.

Install from VCS

pipenv install -e git+https://github.com/requests/requests.git#egg=requests

Install from requirements.txt

pipenv install -r ./requirements.txt

Check local packages

pipenv lock -r

Create requirements.txt

You may want to generate requirements.txt file from existing Pipfile.lock without locking. When you run pipenv lock -r it ignores existing Pipfile.lock and does locking process again. There are issues around running this that are documented in this github issue.

pipenv run pip freeze > requirements.txt

Another approach is to use:

pipenv lock --keep-outdated -d -r > requirements.txt

Install and uninstall a package

pipenv install <module>    #such as, camelcase
pipenv uninstall <module>    #such as, camelcase
pipenv uninstall <module>
pipenv uninstall --all
pipenv uninstall --all-dev
pipenv install <module> --dev    #only for development, such as, nose

Check dependency graph

If there are conflicting dependencies (package_a needs package_c>=1.0, but package_b needs package_c<1.0), Pipenv will not be able to create a lock file and wil output an error like the following:

Warning: Your dependencies could not be resolved. You likely have a mismatch in your sub-dependencies.
  You can use \\( pipenv install --skip-lock to bypass this mechanism, then run \\) pipenv graph to inspect the situation.
Could not find a version that matches package_c>=1.0,package_c<1.0

As the warning says, you can also show a dependency graph to understand your top-level dependencies and their sub-dependencies. This command will print out a tree-like structure showing your dependencies.

pipenv graph
pipenv graph --reverse

You can reverse, --reverse, the tree to show the sub-dependencies with the parent that requires it.

Ignore pipfile

pipenv install --ignore-pipfile

Prepare Deployment

Let’s say you’ve got everything working in your local development environment and you’re ready to push it to production. To do that, you need to lock your environment so you can ensure you have the same one in production.

This will create/update your Pipfile.lock, which you’ll never need to (and are never meant to) edit manually. You should always use the generated file.

Set lockfile - before deployment

pipenv lock

Production deployment

Now, once you get your code and Pipfile.lock in your production environment, you should install the last successful environment recorded.

This tells Pipenv to ignore the Pipfile for installation and use what’s in the Pipfile.lock. Given this Pipfile.lock, Pipenv will create the exact same environment you had when you ran pipenv lock, sub-dependencies and all.

The lock file enables deterministic builds by taking a snapshot of all the versions of packages in an environment (similar to the result of a pip freeze).

pipenv install --ignore-pipfile

Env variables

Pipenv supports the automatic loading of environmental variables when a .env file exists in the top-level directory. That way, when you pipenv shell to open the virtual environment, it loads your environmental variables from the file. The .env file just contains key-value pairs:

#.env
SOME_ENV_CONFIG=some_value
SOME_OTHER_ENV_CONFIG=some_other_value

Check security vulnerabilities

Check for security vulnerabilities (and PEP 508 requirements) in your environment.

pipenv check

Package Distribution and Installation

How does Pipenv work with setup.py files? There are a lot of nuances to that question. First, a setup.py file is necessary when you’re using setuptools as your build/distribution system. This has been the de facto standard for a while now, but recent changes have made the use of setuptools optional.

Remember: Pipenv can help in distribution, but that is not within project scope.

Install module’s into virtual environment

pipenv install -e .

Install module onto machine

pip install .

Wrapping Up

Exiting the virtualenv

exit

Remove the virtualenv

pipenv --rm