Published 2021-04-05.
    Last modified 2021-04-14.
            
Time to read: 2 minutes.
 
django collection.
  Django-oscar defines PIP dependencies with a
  setting called install_requires.
install_requires = [
    'django>=2.2,<3.2',
    # PIL is required for image fields, Pillow is the "friendly" PIL fork
    'pillow>=6.0',
    # We use the ModelFormSetView from django-extra-views for the basket page
    'django-extra-views>=0.13,<0.14',
    # Search support
    'django-haystack>=3.0b1',
    # Treebeard is used for categories
    'django-treebeard>=4.3,<4.5',
    # Babel is used for currency formatting
    'Babel>=1.0,<3.0',
    # For manipulating search URLs
    'purl>=0.7',
    # For phone number field
    'phonenumbers',
    'django-phonenumber-field>=3.0.0,<4.0.0',
    # Used for oscar.test.newfactories
    'factory-boy>=2.4.1,<3.0',
    # Used for automatically building larger HTML tables
    'django-tables2>=2.3,<2.4',
    # Used for manipulating form field attributes in templates (eg: add
    # a css class)
    'django-widget-tweaks>=1.4.1',
]
  According to the documentation,
  pip-tools/ uses install_requires
  to maintain requirements.txt.
$ pip install pip-tools
Layer 1: requirements.in
  I could not get the
  install_requires
  setting to work with pip-tools.
  Instead, I was able to create a file called requirements.in to hold top-level dependencies,
  and pip-tools happily used it:
boto3==1.17.27 django django-cors-headers==3.7.0 django-extensions django-oscar>=3.0.2,<4.0.0 django_storages==1.11.1 django-grappelli==2.14.3 pip pip-tools psycopg2-binary==2.8.6 pycountry==20.7.3 python-decouple==3.4 sorl-thumbnail==12.6.3
  Notice that pip-tools is an unpinned requirement :)
  With requirements.in in place, a new requirements.txt can be generated using the pip-compile
  command provided by pip-tools.
  Here is the pip-compile help message:
(aw) $ pip-compile -h Usage: pip-compile [OPTIONS] [SRC_FILES]... Compiles requirements.txt from requirements.in specs. Options: --version Show the version and exit. -v, --verbose Show more output -q, --quiet Give less output -n, --dry-run Only show what would happen, don't change anything -p, --pre Allow resolving to prereleases (default is not) -r, --rebuild Clear any caches upfront, rebuild from scratch -f, --find-links TEXT Look for archives in this directory or on this HTML page -i, --index-url TEXT Change index URL (defaults to https://pypi.org/simple) --extra-index-url TEXT Add additional index URL to search --cert TEXT Path to alternate CA bundle. --client-cert TEXT Path to SSL client certificate, a single file containing the private key and the certificate in PEM format. --trusted-host TEXT Mark this host as trusted, even though it does not have valid or any HTTPS. --header / --no-header Add header to generated file --emit-trusted-host / --no-emit-trusted-host Add trusted host option to generated file --annotate / --no-annotate Annotate results, indicating where dependencies come from -U, --upgrade Try to upgrade all dependencies to their latest versions -P, --upgrade-package TEXT Specify particular packages to upgrade. -o, --output-file FILENAME Output file name. Required if more than one input file is given. Will be derived from input file otherwise. --allow-unsafe / --no-allow-unsafe Pin packages considered unsafe: distribute, pip, setuptools. WARNING: Future versions of pip-tools will enable this behavior by default. Use --no- allow-unsafe to keep the old behavior. It is recommended to pass the --allow-unsafe now to adapt to the upcoming change. --generate-hashes Generate pip 8 style hashes in the resulting requirements file. --reuse-hashes / --no-reuse-hashes Improve the speed of --generate-hashes by reusing the hashes from an existing output file. --max-rounds INTEGER Maximum number of rounds before resolving the requirements aborts. --build-isolation / --no-build-isolation Enable isolation when building a modern source distribution. Build dependencies specified by PEP 518 must be already installed if build isolation is disabled. --emit-find-links / --no-emit-find-links Add the find-links option to generated file --cache-dir DIRECTORY Store the cache data in DIRECTORY. [default: /home/mslinn/.cache/pip-tools] --pip-args TEXT Arguments to pass directly to the pip command. --emit-index-url / --no-emit-index-url Add index URL to generated file -h, --help Show this message and exit.
  Now I was able to update requirements.txt from requirements.in,
  and then upgrade all PIP packages like this:
(aw) $ pip-compile -U (aw) $ pip install --upgrade -r requirements.txt
This could be written as one line.
(aw) $ pip-compile -U && \
pip install --upgrade -r requirements.txt
Layer 2
  I wanted to take advantange of the pip-tools
  layered requirements feature.
  Overtop the basic dependencies listed in requirements.in,
  I also wanted to manage development dependencies in dev.requirements.in
  and deployment dependencies in prod.requirements.in.
  The dev and prod layers are siblings.
Layer dev
  There is no need to pin django-debug-toolbar because it is constrained by the
  django dependency in the lower layer.
  Jack Cushman, a pip-tools contributor,
  explains why the --generate-hashes option is important.
-c requirements.txt django-debug-toolbar docutils json5 pytest-django PyYAML
(aw) $ pip-compile dev.requirements.in --generate-hashes --allow-unsafe
  This produces dev.requirements.txt:
#
# This file is autogenerated by pip-compile
# To update, run:
#
#    pip-compile dev.requirements.in
#
asgiref==3.3.4
    # via
    #   -c requirements.txt
    #   django
django-debug-toolbar==3.2
    # via
    #   -c requirements.txt
    #   -r dev.requirements.in
django==3.1.8
    # via
    #   -c requirements.txt
    #   django-debug-toolbar
pytz==2021.1
    # via
    #   -c requirements.txt
    #   django
sqlparse==0.4.1
    # via
    #   -c requirements.txt
    #   django
    #   django-debug-toolbar
--allow-unsafe seems unnecessary –
  I believe that --allow-unsafe should be the default behavior for pip-compile.
  I spent some time digging into the reasons that
pip-tools considers some packages “unsafe,”
  and as best I can tell it is because it was thought that pinning those packages could potentially break pip itself,
  and thus break the user's ability to recover from a mistake.
  This seems to no longer be true, if it ever was. Instead, failing to use
--allow-unsafe is unsafe,
  as it means different environments will end up with different versions of key packages despite installing from identical
  requirements.txt files.
Layer prod
  I added gunicorn as a production dependency, and was surprised to find that it declares
  lists a specific version of the Pyton setuptools as a transitive dependency.
-c requirements.txt gunicorn json5
(aw) $ pip-compile prod.requirements.in --generate-hashes --allow-unsafe
  This produces prod.requirements.txt:
#
# This file is autogenerated by pip-compile
# To update, run:
#
#    pip-compile --allow-unsafe --generate-hashes prod.requirements.in
#
gunicorn==20.1.0 \
    --hash=sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8
    # via -r prod.requirements.in
# The following packages are considered to be unsafe in a requirements file:
setuptools==56.0.0 \
    --hash=sha256:08a1c0f99455307c48690f00d5c2ac2c1ccfab04df00454fef854ec145b81302 \
    --hash=sha256:7430499900e443375ba9449a9cc5d78506b801e929fef4a186496012f93683b5
    # via gunicorn