Preferred packages#

We have preferences for requirements in order to:

  • Limit the number of packages with which developers need to be familiar.

  • Reuse code (like Click) instead of writing new code (with argparse).

For applications, we prefer all-inclusive and opinionated packages, because they:

  • Promote greater similarity and code reuse across projects. Django encourages developers to use its authentication mechanism. With Flask, each developer can choose a different mechanism.

  • Are more robust to changes in scope. For example, you might not need the Django admin site on day one, but you’ll be happy to have it when it becomes a requirement.

Maintainers can find dependencies with:

find . \( -name 'setup.cfg' -or -name 'requirements.in' \) -exec echo {} \; -exec cat {} \;

Preferences#

Web framework

Django LTS, unless a newer version has desirable features. Do not use Flask, except in limited circumstances like generating a static site with Frozen-Flask.

API

Django REST Framework or FastAPI. Do not use Django Tastypie, which has fallen behind on Django and Python versions.

Command-line interface

Click, unless a framework provides its own, like Django or Scrapy. Do not use argparse.

Object Relational Mapper (ORM)

Django. If you don’t need an ORM, use psycopg2. Do not use SQLAlchemy, except with FastAPI or in low-level libraries with limited scope where an ORM is needed.

Note

Use psycopg2 in production, not psycopg2-binary, as recommended. See instructions.

HTTP client

Requests, unless a framework uses another, like Scrapy (Twisted).

HTML parsing

lxml. Do not use BeautifulSoup.

Markdown parsing

markdown-it-py. Do not use commonmark, which is deprecated.

Templating

Jinja. Do not use CSS framework integration packages like django-bootstrap*, as they tend to lag the most recent releases. See HTML and CSS.

Asset management

Do not use django-compressor or django-pipeline, which are always behind NPM packages. See Preferences for JavaScript.

Translation

gettext and Babel, unless a framework provides an interface to these, like Django or Sphinx.

Logging

logging

Testing

pytest, unless a framework uses another, like Django (unittest).

Coverage

Coveralls

Documentation

Sphinx. Its Markdown extensions should only be used for OCDS documentation.

Tip

Jazzband packages are a good choice.

Criteria#

A preferred package should meet the following criteria:

  • It is properly released: its readme is rendered on PyPI, a changelog is maintained, tags are used, etc.

  • It supports the most recent version of Python and frameworks (if relevant), like Django or Sphinx.

    • Simple libraries might not need new releases for new Python versions.

  • It meets the QASP criteria of published, tested and documented.

    • Published: Find its repository and check its open source license.

    • Tested: Check its CI badges, GitHub Actions tab, or CI configuration.

    • Documented: Check its documentation website.

  • Its issue tracker demonstrates that the maintainers are responsive.

    • The repository is not described as archived or unmaintained.

    • The maintainer’s other repositories can be considered if the repository is new or unpopular.

Snyk Open Source Advisor might also be used to answer the above.

License compliance#

To ease license compliance and code reuse, avoid software distributed under strong copyleft licenses.

Note

This does not apply to software that is only used as a utility and is not linked to the code, like libsass.

To list the licenses under which installed packages are distributed:

  • Install the packages

  • Install pip-licenses:

    pip install pip-licenses
    
  • List the licenses:

    pip-licenses --with-urls
    

If you have virtual environments for multiple repositories, you can do a bulk operation:

  • Install pip-licenses in all virtual environments. For example, if using pyenv-virtualenv (fish shell):

    for env in (pyenv virtualenvs --skip-aliases --bare)
        pyenv activate $env
        pip install pip-licenses
    end
    
  • Initialize a CSV file as the output file:

    echo Venv,Name,Version,License,URL > licenses.csv
    
  • Append licenses to the output file:

    for env in (pyenv virtualenvs --skip-aliases --bare)
        pyenv activate $env
        pip-licenses --format=csv --with-urls | tail -n +2 | sed "s`^`$env,`" >> licenses.csv
    end
    
  • Run this script from the standard-maintenance-scripts repository:

    ./manage.py check-licenses licenses.csv