urban-meal-delivery/noxfile.py
Alexander Hess bb6de05709
Add a code formatting tool chain
- (auto-)format code with:
  + autoflake => * remove unused imports and variables
                 * remove duplicate dict keys
                 * expand star imports
  + black => enforce an uncompromising code style
  + isort => enforce a consistent import style
             (complying with Google's Python Style Guide)
- implement the nox session "format" that runs all these tools
- add the following file:
  + setup.cfg => holds configurations for the develop tools
2020-08-03 21:39:49 +02:00

133 lines
3.7 KiB
Python

"""Configure nox for the isolated test and lint environments."""
import os
import tempfile
from typing import Any
import nox
from nox.sessions import Session
MAIN_PYTHON = '3.8'
# Keep the project is forward compatible.
NEXT_PYTHON = '3.9'
# Paths with *.py files.
SRC_LOCATIONS = 'noxfile.py', 'src/'
# Use a unified .cache/ folder for all tools.
nox.options.envdir = '.cache/nox'
# All tools except git and poetry are project dependencies.
# Avoid accidental successes if the environment is not set up properly.
nox.options.error_on_external_run = True
# Run only CI related checks by default.
nox.options.sessions = (
'format',
'lint',
f'test-{MAIN_PYTHON}',
f'test-{NEXT_PYTHON}',
)
@nox.session(name='format', python=MAIN_PYTHON)
def format_(session: Session):
"""Format source files with autoflake, black, and isort."""
_begin(session)
_install_packages(session, 'autoflake', 'black', 'isort')
# Interpret extra arguments as locations of source files.
locations = session.posargs or SRC_LOCATIONS
session.run('autoflake', '--version')
session.run(
'autoflake',
'--in-place',
'--recursive',
'--expand-star-imports',
'--remove-all-unused-imports',
'--ignore-init-module-imports', # modifies --remove-all-unused-imports
'--remove-duplicate-keys',
'--remove-unused-variables',
*locations,
)
session.run('black', '--version')
session.run('black', *locations)
session.run('isort', '--version')
session.run('isort', *locations)
@nox.session(python=MAIN_PYTHON)
def lint(session: Session):
"""Lint source files."""
_begin(session)
@nox.session(python=[MAIN_PYTHON, NEXT_PYTHON])
def test(session: Session):
"""Test the code base."""
_begin(session)
@nox.session(name='pre-commit', python=MAIN_PYTHON, venv_backend='none')
def pre_commit(session: Session):
"""Source files must be well-formed before they enter git."""
_begin(session)
session.notify('format')
session.notify('lint')
@nox.session(name='pre-merge', python=MAIN_PYTHON, venv_backend='none')
def pre_merge(session: Session):
"""The test suite must pass before merges are made."""
_begin(session)
session.notify('test')
def _begin(session: Session) -> None:
"""Show generic info about a session."""
if session.posargs:
print('extra arguments:', *session.posargs)
session.run('python', '--version')
# Fake GNU's pwd.
session.log('pwd')
print(os.getcwd())
def _install_packages(
session: Session, *packages_or_pip_args: str, **kwargs: Any
) -> None:
"""Install packages respecting the poetry.lock file.
This function wraps nox.sessions.Session.install() such that it installs
packages respecting the pinnned versions specified in poetry's lock file.
This makes nox sessions even more deterministic.
Args:
session: the Session object
*packages_or_pip_args: the packages to be installed or pip options
**kwargs: passed on to nox.sessions.Session.install()
"""
if session.virtualenv.reuse_existing:
session.log(
'No dependencies are installed as an existing environment is re-used',
)
return
session.log('Dependencies are installed respecting the poetry.lock file')
with tempfile.NamedTemporaryFile() as requirements_txt:
session.run(
'poetry',
'export',
'--dev',
'--format=requirements.txt',
f'--output={requirements_txt.name}',
external=True,
)
session.install(
f'--constraint={requirements_txt.name}', *packages_or_pip_args, **kwargs,
)