If you are a Python developer you probably have heard about pipenv already. It advertises itself as Python Development Workflow for Humans
, the “for Humans” part should be a hint about who created it.
Pipenv surely has some nice features, but it also has others that I’m not interested. In this post, I intend to present the way I use it. I’ll try to illustrate how it fit in my development workflow by sections.
Python installation
If you have pyenv installed pipenv integrates with it and can install the required version described in the Pipfile
.
Virtualenv creation
If pipenv find itself running inside a virtualenv it’ll take advantage of that and use this environment, which is super cool. Otherwise, it’ll automatically create a new virtualenv for the project.
Virtualenv activation
You can use pipenv run
for running a single command and pipenv will automatically activate the project virtualenv for you.
If you want to keep the virtualenv activated, you can use pipenv shell
.
Pipenv will automatically load .env
files.
In a typical workday, I spend most of my time in PyCharm. If you configure a virtualenv for the active project when you open the terminal window inside it, the virtualenv will be automatically activated, this and the Django task runner makes both commands not super interesting to me.
For the .env loading part, in basically every project I use prettyconf or python-decouple, and both projects already solve this for me.
Managing packages
Here we’ll cover the feature set that makes pipenv an amazing project, for me at least. To fully understand the awesomeness of pipenv in this regard a bit of context is needed.
Deterministic builds
Achieving truly deterministic builds with requirements.txt
alone is a bit of a pain. One way to do this with pip
alone is to install a package and then running pip freeze
redirecting its output to the requirements.txt of your production environment.
One can argue that only pinning the direct dependency of your project will be enough, but I learned the hard way that this is not enough. By only pinning the direct dependency, you let the dependency of your dependency basically go crazy. Most of the time it’s not a problem, but when it becomes a problem it’s a hard one to track.
Kenneth makes a better job than I explaining this here.
Enter Pipfile
To solve this kind of problem and some others a replacement was created, Pipfile and Pipfile.lock
, our “requirements.txt 2.0”.
Pipfile is where you declare the required Python version, the packages used in your production and development environment and their source, e.g. PyPI, PackageCloud, etc.
A simple Pipfile would look like this:
[[source]]
url = "https://pypi.python.org/simple"
verify_ssl = true
name = "pypi"
[packages]
dj-database-url = "*"
Django = "<2"
facebook-sdk = "*"
opbeat = "*"
psycopg2-binary = "*"
python-decouple = "*"
[dev-packages]
selenium = "*"
django-debug-toolbar = "*"
model_mommy = "==1.3.0"
The anatomy is very simple to understand, the syntax resembles a lot of what we already have in our requirements.txt. But instead of having a requirements.txt and a requirements-dev.txt
we can declare it in a single file. Cool!
But wait, why are almost every package not using a pinned version? It’s in Pipfile.lock where all of all packages versions are pinned, along with a bunch of other information, like the hashes
of each package.
Because of this, the Pipfile.lock file tends to be a big one, so I’ll just show the section of a single package.
"django": {
"hashes": [
"sha256:ac4c797a328a5ac8777ad61bcd00da279773455cc78b4058de2a9842a0eb6ee8",
"sha256:22383567385a9c406d8a5ce080a2694c82c6b733e157922197e8b393bb3aacd9"
],
"version": "==1.11.10"
}
Here we have the required pinned version of the package along with its hashes, we’ll talk about the hashes later.
Package installation
To install a package using pipenv is very much like installing it with pip, you just need to replace pip with pipenv.
$ pipenv install django
The cool thing here is that it’ll add it to your Pipfile and already lock it and all its dependencies in Pipfile.lock.
Installing packages for a project
When you clone or update a repository you normally would like to update and install all required packages. The way you do this with pipenv is by running install without extra parameters.
$ pipenv install
Most likely you will want to install the packages required in the development environment, for this just add the --dev
option to the previous command.
$ pipenv install --dev
Tip: if the load created by the concurrent installation of packages are slowing down your machine, you could use an extra option to make it sequential:
$ pipenv install --sequencial
Updating the projects dependencies version
As we saw in our Pipfile you can still pin a specific version of any package but for most of it, we simply have a "*"
instead, which makes sense as the pinned version would be set in the Pipfile.lock file.
Now suppose we decide that its time to update our dependencies to their latest version. It’s very simple, you just need to lock the dependencies again.
$ pipenv lock
It’ll update the Pipfile.lock file with the latest version for each dependency, and related dependencies, while respecting the version requirement informed in the Pipfile. Like, but not limited to, this:
* for the latest version
==1.3.0 for the specific 1.3.0 version
<2 for versions smaller than 2
Security
Remember those hashes thing? Starting with pip 8.0, pip added a new security feature. Informing the hashes for each package you are now protected against package tampering. And the cool thing is that pipenv make this totally transparent.
Pipenv also adds another very cool command to add an extra protective layer for your project, with pipenv check
you can verify if any of your used packages have a known security vulnerability.
Extra sweetness
Pipenv has a few more cool commands, like pipenv graph
for print an dependency graph for you, and a few others, you should check it out in the docs.
Conclusion
For my workflow, some features from pipenv are not required and would even be a problem if I was forced to adopt it entirely and I’m glad that I can use only the ones that make sense to me. As I tried to highlight in the Managing packages section pipenv has too many cool features to be blindly ignored.
I hope you are convinced to give it a try at least, and if you like it don’t forget to say Thanks to Kenneth.