In the world of Python development, managing dependencies and project-specific environments is a foundational skill. It ensures that your projects are isolated, reproducible, and free from conflicts. For years, the standard has been venv combined with a requirements.txt file. In this post, we’ll revisit this classic approach and then explore how pip-tools can significantly improve the workflow.
The Classic: venv and requirements.txt
The venv module, included in Python 3, allows us to create lightweight virtual environments. Each environment has its own Python interpreter and installed packages, isolated from other projects and the system-wide Python installation.
The workflow is straightforward:
Create an environment:
1
python3 -m venv venv
Activate it:
- On macOS/Linux:
source venv/bin/activate - On Windows:
.\venv\Scripts\activate
- On macOS/Linux:
Install packages:
1
2pip install requests
pip install flaskFreeze dependencies:
1
pip freeze > requirements.txt
This last step creates a requirements.txt file, which contains a list of all the packages installed in the environment, including their exact versions and all their sub-dependencies. While this ensures reproducibility, it has a major drawback: it doesn’t distinguish between the packages you directly need (like requests) and the packages they depend on (like charset-normalizer, idna, urllib3, and certifi).
This makes the requirements.txt file difficult to manage. If you want to update a direct dependency, you have to manually update it and then re-freeze, hoping you don’t break anything. If you want to remove a dependency, you have to manually clean up its sub-dependencies.
A Better Way: pip-tools
This is where pip-tools comes in. It introduces a more declarative and manageable approach to dependencies with two key files: requirements.in and requirements.txt.
The pip-tools Workflow:
Install
pip-tools:1
pip install pip-tools
Define your direct dependencies:
Create a file namedrequirements.inand list only the packages your project directly depends on. You can specify versions if you need to.1
2
3# requirements.in
flask
requestsCompile your dependencies:
Run thepip-compilecommand:1
pip-compile requirements.in
This command takes your requirements.in file, resolves all the necessary sub-dependencies, and generates a comprehensive requirements.txt file. This file is beautifully commented, showing which top-level package each sub-dependency belongs to.
1 | # |
- Sync your environment:
Instead ofpip install -r requirements.txt, you usepip-sync:The1
pip-sync
pip-synccommand ensures that your virtual environment has exactly the packages listed inrequirements.txt. It will install missing packages, update incorrect versions, and even remove packages that are not part of the requirements.
Conclusion
By separating your direct dependencies (requirements.in) from the full, locked dependency list (requirements.txt), pip-tools provides a much cleaner and more maintainable workflow. It makes updating packages safer and keeps your project’s dependency tree transparent and easy to understand. If you’re still using pip freeze, give pip-tools a try—it’s a simple change that can make a big difference.