Managing Python dependencies sometimes is a pain, especially when upgrading a library. The upgraded library could bring more dependencies which cause version conflicts with current onces.

In order to avoid or quickly catch the version conflict issue, we can use pip-compile to freeze all requirements before building any Python application.

Activate a virtual environment and install pip-compile by pip-tools package

$ virtualenv .venv
$ source .venv/bin/activate
$ pip install pip-tools==6.12.1 

requirements.in file contains only the packages added by the developer

falcon==3.1.1
gunicorn==20.1.0
gevent==21.12.0

Freeze all requirements with pip-compile

$ pip-compile --annotation-style=line --output-file=requirements.txt requirements.in

requirements.txt file is generated by the above command. It contains all requirements and their dependencies with pinned versions

#
# This file is autogenerated by pip-compile with Python 3.9
# by the following command:
#
#    pip-compile --annotation-style=line --output-file=requirements.txt requirements.in
#
falcon==3.1.1             # via -r requirements.in
gevent==21.12.0           # via -r requirements.in
greenlet==1.1.3.post0     # via gevent
gunicorn==20.1.0          # via -r requirements.in
zope-event==4.6           # via gevent
zope-interface==5.5.2     # via gevent

# The following packages are considered to be unsafe in a requirements file:
# setuptools

Note: pip-compile generates a requirements.txt file using the latest versions that fulfil the dependencies you specify in the supported files.

If pip-compile finds an existing requirements.txt file that fulfils the dependencies then no changes will be made, even if updates are available.

Learn more about pip-tools at here