In a nutshell, we need to ensure that the same code is being run everywhere, including the project source, its libraries and the version of Python on which it is run.
Below are some quick notes on one way to achieve this.
pipenv is a package manager that uses pip and virtualenv under the
hood. The project’s direct dependencies are added to a
Pipfile, and the
dependency graph is locked down in
Pipfile.lock, which is generated
automatically and never touched by hand. The lock file is crucial for
reproducible builds, we will see how that is under project syncing.
pyenv makes it a breeze to install and manage multiple versions of Python. You
specify the desired Python version in your
Pipfile and pipenv will use
pyenv to fetch and install the relevant Python version.
- See pyenv installation instructions
- While installing pyenv is pretty simple, however building a brand new Python (which is what pyenv does) may create problems, so make sure to go through pyenv’s wiki entry on common build problems.
- Make sure that you add
eval "$(pyenv init -)"towards the end of your shell’s init file (e.g.
If you use Homebrew or Linuxbrew you can simply run
brew install pipenv
Otherwise you will need to make use of the Python and Pip that already ship with your OS, or get it via pyenv. And then run something like:
pip install --user pipenv
Yeah, installing pipenv itself requires Python and pip. But this only needs to be done once.
See Installing Pipenv for more details.
Make sure that pyenv and pipenv are installed as indicated in the previous section.
Setting up a new project
- Create a project directory e.g.
Setup Python for your project:
pipenv install --python 3. This will create a
Pipfile.lockin the project directory.
If you use this command, by default pipenv will try to pick the Python 3 available on your system. If it doesn’t find one, it will ask if you want it to fetch a Python from pyenv.
If you want a more specific version of Python, use:
pipenv install --python 3.7.
Install the libraries that your project depends on using
pipenv install django~=2.1.5 pipenv install djangorestframework~=3.9.1
You can skip specifying the version, but I won’t recommend doing that. Note the use of the
~=operator. It is the compatible release operator and essentially means that a breaking version of the library won’t be installed when you try to update it. More on this under updating dependencies.
Pipfile.lockto version control. Now you can share your project with the team.
Syncing a project
Fetch the project from version control. Make sure that it contains both
- Go to the project’s directory
That’s it. pipenv will install all your project’s dependencies (including Python, via pyenv) and allow you to start using them.
pipenv sync only looks at
Pipfile.lock, installs the given dependencies
locally and ensures that the hashes match. This is exactly what we need to
ensure that the build is reproducible.
pipenv sync everytime the project’s dependencies are updated.
Running a project
There are two ways to run our project using the newly installed Python and libraries:
The first is to invoke
pipenv shell. This will drop you into a new shell with
sys.path setup so that you get the correct version of
everything. You can exit this shell at any time via
The other way is to use
pipenv run <cmd>. E.g. If you are, say, running
django, all you need to do is
pipenv run python manage.py runserver and
everything should work as expected.
How do you upgrade a library to a newer version?
One way is to simply run
pipenv update name-of-library. If you used the
compatible release operator, which you should, this will update the library to
the newest version allowed by this operator.
For example, if you specified
django~=2.0.0 in your
update django will update django to the highest version available under 2.0.x
but not to a newer version in the 2.1.x series.
And if you specified
django~=2.0, then it will update django to the highest
version available under 2.x but will not go up to 3.x.
If you want to update django to a higher version than the one allowed by the
compatible release operator, you need to use the
install subcommand i.e. do
pipenv install django~=2.1.0.
The other way to do this is to simply update the
Pipfile by hand, and
pipenv install. This will install the specified library
version and also update
Once you get past the installation hurdle, it seems easy and simple enough to use pipenv (with help from pyenv) to manage a project’s dependencies and get reproducible builds.
For more on pipenv, you can go through: