Use PipEnv instead of requirements.txt
Years ago when I first starting looking at Python the default package manager recommended was pip and it was a weak
experience and felt less complete compared to other languages such as ruby’s bundler, C#’s nuget or node’s npm. Compared
to other languages pip felt bare bones and fiddly to configure. Weirdly the method to indicate package dependencies using
pip was to create a requirements.txt
, unlike the other languages you had to explicitly state what version of each
package upfront and does not “lock” packages when installed.
Ideally a package manager should have the following features
- Package Locking and version management
- Environment definition or requirements
- Configuration overrides
Pipenv provides good access to the above features and the following sections will describe how to use Pipenv to realize the features.
Getting started
Pipenv is not installed as part of the python runtime by default, but pip is normally installed as part of the python runtime. So the easiest way to install Pipenv is to just run
$ pip install --user pipenv
Once installed the next step is just run the following command in your project directory
$ pipenv install
Or if you have a previous pip requirements file you can easily convert to Pipenv by running
$ pipenv install -r requirements.txt
And that will enable pipenv for your project. Once run you will notice that a few new files will be created, the Pipfile and the Pipfile.lock.
Package Locking
Assuming you need to use the requests package, with pip and the requirements.txt you would normally just add the line
requests
to the requirements.txt and run the command pip install -r requirements.txt
. A few problems occur with this approach
to installing packages. Unless you explicitly set the version of the package in the requirements.txt it will install
the current latest version of the requests package when the command is run. The biggest problem with that scenario is
that the version management needs to be explicitly set and when not set it is not possible to know which version of the
package was used during the development.
With Pipenv a Pipfile.lock file is created when the package are installed. Assuming the Pipfile looks as follows
[packages]
requests = "*"
When the command pipenv install
is run it will install the latest version and flag that version in the Pipfile.lock
file. Keeping that file in the source repository will ensure that other developers will at least have the same version
when they also run pipenv install
. The Pipfile.lock not only stores the version information, it also stores the
hashes of the packages, which ensures that packages are not only the same version, but contain the same information.
The hashes will help ensure that no sneak updates happen to the packages, which is not likely to be a problem with most
public packages, but helps a lot when packages are sourced from private repositories.
Environment definitions
A really cool feature of pipenv is that the Python version can also be stored as part of the Pipfile. By default you can indicate the required version by adding the following section
[requires]
python_version = "3.8"
The requires section serves as double duty in Pipenv. The first check is to ensure that the version of Python is
available, but Pipenv also helps manage virtualenv configuration as well. When running the pipenv install
command
it will automatically create a new virtual environment for the project as well. Finally, Pipenv will also download and
install the version of python if pyenv
or asdf
installed.
Configuration Management
The final feature of Pipenv that a major enhancement over standard pip is that configuration can be stored as part of the Pipfile. Ironically the requirements.txt has this feature already, but it is really a shorthand configuration for what flags the pip command accepts. In the Pipfile you can define overall configuration using the following configuration
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
Updating the settings to point to local repositories if that is a requirement. Pip specifiers are also supported if you need to limit packages to particular operating systems.