All articles
3 min read

Mastering Python Virtual Environments with Pipenv

Pipenv combines pip and virtualenv into a single workflow. Learn how to manage Python project dependencies, lock files, dev packages, and virtual environments with Pipenv.

Managing Python dependencies without a proper tool leads to version conflicts, missing packages on other machines, and security vulnerabilities that go unnoticed. Pipenv solves this by combining pip and virtualenv into a single workflow — one tool that creates your virtual environment, tracks exact dependency versions in a lockfile, and flags security issues in your dependency tree.

Why Pipenv over pip + venv?

The traditional pip install + venv workflow works, but it has gaps:

  • requirements.txt doesn't distinguish between direct dependencies and their transitive dependencies
  • There's no built-in separation between dev-only packages and production packages
  • Reproducing an exact environment on another machine requires careful manual steps

Pipenv addresses all three with Pipfile (what you want) and Pipfile.lock (what you actually have).

Installation

pip install pipenv

On macOS with Homebrew:

brew install pipenv

Creating a New Project

In an empty directory, install your first package to automatically create a virtual environment and Pipfile:

mkdir myproject && cd myproject
pipenv install requests

Pipenv creates:

  • A virtual environment in ~/.local/share/virtualenvs/
  • Pipfile — human-readable TOML tracking your direct dependencies
  • Pipfile.lock — records exact versions and hashes of every installed package

Working with the Virtual Environment

Activate the shell:

pipenv shell

Run a single command without activating:

pipenv run python script.py
pipenv run pytest

Exit the virtual environment:

exit

Managing Packages

Install a package:

pipenv install requests

Install a dev-only package (excluded from production):

pipenv install pytest --dev

Install a specific version range:

pipenv install "requests>=2.28,<3.0"

Uninstall a package:

pipenv uninstall requests

Install all dependencies from an existing Pipfile:

pipenv install        # production only
pipenv install --dev  # including dev packages

Migrate from requirements.txt:

pipenv install -r requirements.txt

Understanding Pipfile vs Pipfile.lock

Pipfile uses TOML format and is meant to be edited by humans:

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

[packages]
requests = ">=2.28"

[dev-packages]
pytest = "*"

[requires]
python_version = "3.11"

Pipfile.lock is machine-generated and records the exact version and cryptographic hash of every package — direct and transitive. Commit both files to version control. The lockfile ensures everyone on your team and in your CI pipeline installs identical packages.

Locking and Deployment

Generate or update the lockfile:

pipenv lock

Install from the lockfile exactly (for production deployments):

pipenv install --ignore-pipfile

This ignores version ranges in Pipfile and installs exactly what Pipfile.lock specifies — byte-for-byte reproducible across environments.

Security Auditing

Pipenv can scan your dependencies for known CVEs:

pipenv check

This checks your installed packages against the PyPI Safety database and reports any known vulnerabilities.

Inspecting Your Environment

View the full dependency graph:

pipenv graph

List all installed packages with exact versions:

pipenv lock -r

Verify which Python interpreter is in use:

pipenv run python -c "import sys; print(sys.executable)"

Conclusion

Pipenv is a practical solution for managing Python project dependencies when you want reproducibility without a heavyweight build system. Use Pipfile to declare what you need, commit Pipfile.lock to pin exact versions, and run pipenv check before deploying to catch vulnerabilities early.

For projects that also need to publish packages to PyPI, consider Poetry as an alternative that handles both dependency management and package publishing.