Logo
Published on

Better Python Development Workflow with Pipenv

5 min read
Authors

In my previous post, I have briefly describe my current python development workflow using a combination of virtualenv, pyenv, and pyenv-virtualenv. It's been great for me and serve my workflow very well. But recently, my colleague at work, introduce me with a package call pipenv. I look into the website and, it seems promising in terms of makes my workflow more convenient.

As per its website says, the problem that pipenv trying to solve is [1]:

  • You no longer need to use pip and virtualenv separately. They work together.
  • Managing a requirements.txt file can be problematic, so Pipenv uses Pipfile and Pipfile.lock to separate abstract dependency declarations from the last tested combination.
  • Hashes are used everywhere, always. Security. Automatically expose security vulnerabilities.
  • Strongly encourage the use of the latest versions of dependencies to minimize security risks arising from outdated components.
  • Give you insight into your dependency graph (e.g. $ pipenv graph).
  • Streamline development workflow by loading .env files.

This post is basically an updated version from my previous one. In this post I will describe the process to integrate pipenv into your python development workflow, and hopefully can make you more productive with it. I assume you have pyenv and version of python installed in your machine. (I suggests to install python from pyenv, because it can save you with any issue related to priviledge access).

Install pipenv

Installing pipenv is pretty straightforward (:fingercross).

$ pip install pipenv

Even though at their website, for OSX they using homebrew, I found out that it can't find python installation from pyenv and it tried to install python again. The other thing that I found is, when you install pipenv, it will also install virtualenv for you.

Use pipenv for your package management

New Project

If you start with clean project pipenv usage will be straight forward. For example, let say you want to create a new django project.

$ mkdir YourAwesomeProject
$ cd YourAwesomeProject
$ pipenv install django

When you run above command on terminal:

  • pipenv first will create virtualenv with name convention YourAwesomeProject-<random string>
  • Create Pipfile for the project
  • install django package and generate Pipfile.lock

You'll most likely see a result like below:

Creating a virtualenv for this project…
Pipfile: <root folder>/YourAwesomeProject/Pipfile
Using <root folder>/.pyenv/versions/3.6.6/bin/python3.6 (3.6.6) to create virtualenv…
⠇Already using interpreter <root folder>/.pyenv/versions/3.6.6/bin/python3.6
Using base prefix '<root folder>/.pyenv/versions/3.6.6'
New python executable in <root folder>/.virtualenvs/YourAwesomeProject-ensK0QMs/bin/python3.6
Also creating executable in <root folder>/.virtualenvs/YourAwesomeProject-ensK0QMs/bin/python
Installing setuptools, pip, wheel...done.

Virtualenv location: <root folder>/.virtualenvs/YourAwesomeProject-ensK0QMs
Creating a Pipfile for this project…
Installing django…
Collecting django
  Downloading https://files.pythonhosted.org/packages/d1/e5/2676be45ea49cfd09a663f289376b3888accd57ff06c953297bfdee1fb08/Django-2.1.3-py3-none-any.whl (7.3MB)
Collecting pytz (from django)
  Using cached https://files.pythonhosted.org/packages/f8/0e/2365ddc010afb3d79147f1dd544e5ee24bf4ece58ab99b16fbb465ce6dc0/pytz-2018.7-py2.py3-none-any.whl
Installing collected packages: pytz, django
Successfully installed django-2.1.3 pytz-2018.7

Adding django to Pipfile's [packages]…
Pipfile.lock not found, creating…
Locking [dev-packages] dependencies…
Locking [packages] dependencies…
Updated Pipfile.lock (85c883)!
Installing dependencies from Pipfile.lock (85c883)…
  🐍   ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ 2/2 — 00:00:00
To activate this project's virtualenv, run pipenv shell.
Alternatively, run a command inside the virtualenv with pipenv run.

If you look at Pipfile, you'll see something like below:

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

[packages]
Django = "*"

[dev-packages]

[requires]
python_version = "3.6"

That * version in Django package will make sure that you use the latest version. Pipenv will make sure that it will always compatible with all of packages associated with Django

Existing Project

If you have a python project that previously using requirements.txt, pipenv also provide an easy way to migrate from it. You only need to run this command: pipenv install -r requirements.txt. The command above, will read requirements.txt file and generate Pipfile with Pipfile.lock.

If you look at your Pipfile, you'll see something like this.

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

[packages]
Django = "==2.1.3"

[dev-packages]

[requires]
python_version = "3.6"

That version of django will similar to the one you have in requirements.txt file. I recommend to change it to *, so it will always get the latest one and let pipenv handle all of the dependencies.

Install package only for development

Without pipenv, I usually have several requirements.txt file to differentiate between development and production environment. I usually have:

- common.txt            # all of package that is common on dev and prod
- requirements.txt      # common package + package only for production
- requirements-dev.txt  # common package + package only for development

With pipenv I can easily install package that only exist for development and everything still manageable with Pipfile. Let say I want to have pytest for running my unittest.

$ pipenv install pytest --dev

That command line argument --dev above will install pytest package only when you specify that argument.

Pipenv for development

After you setup your project for pipenv, the next thing is easy.

$ pipenv shell # activate the virtualenv
$ exit # if you have finished and want to exit virtualenv

Conclusion

That's all then. I have been used it for a couple of months and it's improve the way I manage my python project package since then.