Skip to main content

Invokes Python dev tools at once.

Project description

Invoke Lint

Test CodeQL Code Coverage Maintainability Dependabot Python versions Twitter URL

Invokes Python dev tools at once.

Attention

  • The development status of this package is Beta now. It may not be able to keep backward compatibility. Be careful to use, especially for CI.
  • Currently, each commands require to run in project home directory.

Advantage

  1. Covers major development tools and they are optional to install
  2. Quick response for developer, slow but detail for CI
  3. Available to place commands into your selected namespace

1. Covers major development tools and they are optional to install

You can choose which dev tool you'll install, this package doesn't force to install each of dev tools. It helps you to avoid conflicts or breaking your project's dependencies.

Supporting tools:

Linters:

Formatters:

For test and coverage:

Package build:

2. Quick response for developer, slow but detailed for CI

The commands for each kind of tasks are designed as unified into 2 main commands:

  • For developer: Runs only quick responsive dev tools at once
  • For CI (or final check): Runs slow but detailed responsive tools at once

3. Available to place commands into your selected namespace

This doesn't pollute your comfortable namespaces of command line tools. Thanks to Invoke, you can place commands into your selected namespace. (See: Quickstart)

Representative commands

(Note that the namespaces of following commands can be changed as you like. See: Quickstart)

inv style

Formats code by following tools at once:

  1. docformatter
  2. Ruff format
  3. Ruff check --fix

Optionally, you can use autoflake, isort, and Black instead of Ruff format by inv style --no-ruff:

  1. docformatter
  2. autoflake
  3. isort
  4. Black
  5. Ruff check --fix
  • inv style --check can only check.
  • inv style --ruff can skip ruff check --fix.

inv lint

Runs following fast linters at once:

  1. Xenon (Optional)
  2. Ruff
  3. Bandit
  4. dodgy
  5. Flake8
  6. pydocstyle (Optional)

The format task (described later) also run before running above linters. You can skip them by --skip-format option. Use --xenon to enable Xenon, and --pydocstyle to enable pydocstyle.

inv lint.deep

Runs following slow but detailed linters at once:

  1. mypy
  2. Pylint
  3. Semgrep

inv lint.radon

Reports radon both code complexity and maintainability index. (Requires radon to be installed)

inv test

Runs fast tests (which is not marked @pytest.mark.slow) by pytest.

See:

inv test.all

Runs all tests including those marked @pytest.mark.slow by pytest.

inv test.cov

Runs all tests and report those coverage by pytest and Coverage.py.

It also can dump the coverage as XML or HTML format.

inv dist

Builds source and wheel packages into dist/ directory by build.
(Currently, not support in Windows)

See:

Quickstart

1. Install

We should use uv or one of the dependency management tools to resolve dependencies of many dev tools.

For example, in case when we use the uv, then, pyproject.toml is like below:

[dependency-groups]
dev = [
    "autoflake",
    "bandit; python_version >= '3.7'",
    # If you want to use not Ruff but Black for formatting
    "black; python_version >= '3.7'",
    "build",
    "bump-my-version",
    "cohesion",
    # The coverage==3.5.3 is difficult to analyze its dependencies by dependencies management tool,
    # so we should avoid 3.5.3 or lower.
    # - Command: "pipenv install --skip-lock" fails 
    #   since it tries to parse legacy package metadata and raise InstallError
    #   · Issue #5595 · pypa/pipenv
    #   https://github.com/pypa/pipenv/issues/5595
    "coverage>=3.5.4",
    # The dlint less than 0.14.0 limits max version of flake8.
    # - dlint/requirements.txt at 0.13.0 · dlint-py/dlint
    #   https://github.com/dlint-py/dlint/blob/0.13.0/requirements.txt#L1
    "dlint>=0.14.0",
    # To the docformatter load pyproject.toml settings:
    # docformatter < 1.7.8 depends on untokenize==0.1.1, whose setup.py uses ast.Constant.s removed in Python 3.14.
    # docformatter >= 1.7.8 dropped untokenize but requires Python >= 3.10.
    "docformatter[tomli]>=1.7.8; python_version >= '3.10' and python_version < '3.11'",
    "docformatter>=1.7.8; python_version >= '3.11'",
    "dodgy",
    # The hacking depends flake8 ~=6.1.0 or ~=5.0.1 or ~=4.0.1.
    # We should avoid the versions that is not compatible with the hacking,
    # considering the speed of dependency calculation process
    "flake8!=6.0.0,!=5.0.0,>=4.0.1; python_version >= '3.6'",
    # To replace E501 in pycodestyle with B950 in flake8-bugbear:
    # - Using Black with other tools - Black 25.1.0 documentation
    #   https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html#bugbear
    "flake8-bugbear",
    # To use flake8 --radon-show-closures
    "flake8-polyfill",
    # To use pyproject.toml for Flake8 configuration
    "Flake8-pyproject",
    # Latest hacking depends on legacy version of flake8, and legacy hacking doesn't narrow flake8 version.
    # When unpin hacking, it has possibility to install too legacy version of hacking.
    "hacking>=5.0.0; python_version >= '3.8'",
    "invokelint; python_version >= '3.7'",
    # If you want to use not Ruff but isort for formatting
    "isort",
    "mypy",
    # If you want to use
    "pydocstyle; python_version >= '3.6'",
    "pylint",
    "pytest",
    # If you want to use Radon for code complexity and maintainability index checking
    # Note that the version should be less than 6.0.0 because of the issue of Radon with pytest log format.
    # - Radon can't run when use pytest log fornat: `$()d` · Issue #251 · rubik/radon
    #   https://github.com/rubik/radon/issues/251
    "radon<6.0.0",
    "ruff; python_version >= '3.7'",
    # If we simply lock as "semgrep; python_version >= '3.9' or python_version>='3.6' and platform_system=='Linux'",
    # the semgrep 1.121.0 is installed in Python 3.14 and cause following error:
    #     File "/root/.local/share/uv/python/cpython-3.14.5-linux-aarch64-gnu/lib/python3.14/importlib/__init__.py", line 88, in import_module
    #       return _bootstrap._gcd_import(name[level:], package, level)
    #              ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    #   TypeError: Metaclasses with custom tp_new are not supported.
    "semgrep; python_version >= '3.9' and python_version < '3.10' or python_version>='3.6' and platform_system=='Linux'",
    "semgrep>=1.122.0;python_version >= '3.10'",
    # To resolve type checking for `from invoke import Collection` in tasks.py
    "types-invoke",
    # If you want to use not Ruff but Xenon for complexity checking
    "xenon",
]

then:

uv sync
source .venv/bin/activate

2. Implement

Create tasks.py in project directory:

"""Tasks for maintaining the project.

Execute 'invoke --list' for guidance on using Invoke
"""
from invoke import Collection

from invokelint import dist, lint, path, style, test

ns = Collection()
ns.add_collection(dist)
ns.add_collection(lint)
ns.add_collection(path)
ns.add_collection(style)
ns.add_collection(test)

Commands may be explicitly place with a different name than they were originally given via a name kwarg (or the 2nd regular arg):

ns.add_collection(lint, 'namespace-you-wish')

See: Constructing namespaces — Invoke documentation

3. Check installation

inv --list

4. Setup target package

This package reuses setuptools settings for package discovery for linting, formatting, and measuring coverage. You can check which package are discovered by setuptools and your project's settings, by following command:

$ inv path
Setuptools detected packages: ['invokelint', 'invokelint.path']
Root packages: ['invokelint']
Setuptools detected Python modules: ['setup', 'tasks']
Existing test packages: ['tests']
Python file or directories to lint: ['invokelint', 'setup.py', 'tasks.py', 'tests']
Python file or directories to lint excluding test packages: ['invokelint', 'setup.py', 'tasks.py']

If result is not your expected, follow official documentation of setuptools to configure pyproject.toml (recommended), setup.cfg, or setup.py.

See: Package Discovery and Namespace Packages - setuptools latest documentation

5. Set up CI/CD workflows

This package provides reusable GitHub Actions workflows. Create the following files in .github/workflows/:

.github/workflows/test.yml — runs tests and linters on push/PR to main:

name: Test
on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
permissions:
  contents: read
jobs:
  call-workflow-test:
    uses: yukihiko-shinoda/reusable-workflow-invoke-lint-test/.github/workflows/workflow.yml@v1
    with:
      support-python-versions: "[ '3.14', '3.13', '3.12', '3.11', '3.10' ]"
  call-workflow-lint:
    uses: yukihiko-shinoda/reusable-workflow-invoke-lint-lint/.github/workflows/workflow.yml@v1

.github/workflows/qlty.yml — uploads coverage to Qlty on push to main:

on:
  push:
    branches:
      - main
# For OIDC token exchange with Qlty:
# - Configuring OpenID Connect in cloud providers - GitHub Docs
#   https://docs.github.com/en/actions/how-tos/secure-your-work/security-harden-deployments/oidc-in-cloud-providers#adding-permissions-settings
permissions:
  contents: read
  id-token: write
jobs:
  analyze:
    uses: yukihiko-shinoda/reusable-workflow-invoke-lint-qlty/.github/workflows/workflow.yml@v1
    permissions:
      contents: read
      id-token: write

.github/workflows/deploy.yml — publishes to PyPI on version tag push (v*.*.*):

on:
  push:
    tags:
      - 'v[0-9]+.[0-9]+.[0-9]+'
permissions:
  contents: read
jobs:
  call-workflow:
    uses: yukihiko-shinoda/reusable-workflow-invoke-lint-deploy/.github/workflows/workflow.yml@v1
    secrets:
      pypi_password: ${{ secrets.pypi_password }}

Check the respective repositories for the latest commit hashes if you want to use them for lock:

How do I...

Suppress B101: assert_used in Bandit and assert (S101) in Ruff only in test files?

Set below configuration in pyproject.toml:

[tool.bandit.assert_used]
skips = ["tests/*"]

[tool.ruff.lint.per-file-ignores]
"tests/*" = ["S101"]

Note that invoke-lint executes Bandit with option --configfile=pyproject.toml, so upper configuration will be applied.

See: Configuration — Bandit documentation

Credits

This package was created with Cookiecutter and the yukihiko-shinoda/cookiecutter-pypackage project template.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

invokelint-0.18.0.tar.gz (28.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

invokelint-0.18.0-py3-none-any.whl (17.7 kB view details)

Uploaded Python 3

File details

Details for the file invokelint-0.18.0.tar.gz.

File metadata

  • Download URL: invokelint-0.18.0.tar.gz
  • Upload date:
  • Size: 28.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for invokelint-0.18.0.tar.gz
Algorithm Hash digest
SHA256 723d06088f48522249b4d4674617aa8217438b8e13344bba31085ebd67f36746
MD5 20fcc80bea26aa29f50da66296d96f91
BLAKE2b-256 4e88cf929e8126a418138e9f16bc15580013220f06e5b568e18f25f8cae25c5a

See more details on using hashes here.

File details

Details for the file invokelint-0.18.0-py3-none-any.whl.

File metadata

  • Download URL: invokelint-0.18.0-py3-none-any.whl
  • Upload date:
  • Size: 17.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for invokelint-0.18.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1a487c450e5c225511554a225506209c76b34b8135743a108331816e0c9e52c9
MD5 87607ae64ce07cbbf733a469ed067e78
BLAKE2b-256 a0bca6c47fbd144d4a40cbf6305f1fa27c36163e26f0eabf87a701bf9ffd7c7e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page