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.txtdoesn'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 dependenciesPipfile.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.