python3 -m venv .venv/
if [[ -f versions.txt ]] && [[ versions.txt -nt requirements.txt ]]; then
pip install --requirement versions.txt
else
pip install --requirement requirements.txt
pip freeze > versions.txt
fi
(I place the files in etc/pip/ in my projects (and check them into git), but I've omitted the paths for clarity. One could embellish this by including python version number in the filename, as package requirements can change between python versions.)I also have a bin/venv-python wrapper which sets PYTHONPATH, PYTHONDONTWRITEBYTCODE before chain calling .venv/bin/python3 with the arguments, and this is how pip above is called. (again, omitted above for clarity.)
This won't cover everyone's usage scenario, but it works for me. YMMV.
https://iam.georgecox.com/2021/09/25/python-3-venv/ explains the details.