Linters and formatters

How to activate and use the six Python linters and formatters bundled with Pants.

Activating linters and formatters

Linter/formatter support is implemented in separate backends so that they are easy to opt in to individually:

  • pants.backend.python.lint.bandit: Supports the Bandit security issue detector.
  • pants.backend.python.lint.black: Supports the Black code formatter.
  • pants.backend.python.lint.docformatter: Supports the Docformatter docstring formatter.
  • pants.backend.python.lint.flake8: Supports the flake8 style checker.
  • pants.backend.python.lint.isort: Supports the isort import statement formatter.
  • pants.backend.python.lint.pylint: Supports the pylint style and error checker.

To enable linters/formatters, add the appropriate ones in pants.toml:

[GLOBAL]
...
backend_packages = [
  'pants.backend.python',
  'pants.backend.python.lint.black',
  'pants.backend.python.lint.isort',
]

You'll now see lint and fmt goals in the output of ./pants goals.

$ ./pants goals

Goals
-----

Use `./pants help $goal` to get help for a particular goal.


...

fmt           Autoformat source code.

lint          Run all linters and/or formatters in check mode.

...

πŸ“˜

How to activate MyPy

MyPy is run with the typecheck goal, rather than lint.

Configuring the tools and adding plugins

Most of the formatters and linters allow you to configure

  • --version: the version to use.
  • --args: any command-line arguments you want to pass to the tool.
  • --extra-requirements: any additional libraries to install, such as any plugins.
  • --config: path to a config file; used if the file is in a non-standard location such that it cannot be auto-discovered.

For example:

[docformatter]
args = ["--wrap-summaries=100", "--wrap-descriptions=100"]

[flake8]
# Load a config file in a non-standard location.
config = "build-support/.flake8"
version = "flake8==3.8.0"
# Add a Flake8 plugin:
extra_requirements.add = ["flake8-2020"]

Run ./pants help-advanced black, ./pants help-advanced flake8, and so on for more information.

πŸ“˜

Config files are normally auto-discovered

For tools that autodiscover config filesβ€”such as Black, isort, Flake8, and Pylintβ€”Pants will include any relevant config files in the process's sandbox when running the tool.

If your config file is in a non-standard location, you must instead set the --config option, e.g. [isort].config. This will ensure that the config file is included in the process's sandbox and Pants will instruct the tool to load the config.

Skipping a formatter or linter

To temporarily skip a tool, use the --skip option. For example, run:

$ ./pants --black-skip --flake8-skip lint ::

You can also skip on a per-target basis, which can be useful for incrementally adopting new tools. For example:

# Skip Black and Flake8 for all the non-test files in this folder.
python_library(skip_black=True, skip_flake8=True)

# Skip isort for all the test files in this folder.
python_tests(name="tests", skip_isort=True)

When you run ./pants fmt :: and ./pants lint ::, Pants will ignore any files belonging to skipped targets.

Tip: only run over changed files

With formatters and linters, there is usually no need to rerun on files that have not changed.

Use the option --changed-since to get much better performance, like this:

$ ./pants --changed-since=HEAD fmt

or

$ ./pants --changed-since=main lint

Pants will find which files have changed and only run over those files. See Advanced target selection for more information.

Tips for specific tools

Bandit and Flake8: report files

Flake8 and Bandit can both generate report files saved to disk.

For Pants to properly preserve the reports, instruct both tools to write to the reports/ folder by updating their config files or --flake8-args and --bandit-args. For example, in your pants.toml:

[bandit]
args = ["--output=reports/report.txt"]

[flake8]
args = ["----output-file=reports/report.txt"]

Pants will copy all reports into the folder dist/lint/<linter_name>.

Pylint: how to add first-party plugins

Run ./pants help-advanced pylint for instructions to add plugins written by you with the --pylint-source-plugins option.

If you want to write first-party plugins for other linters like Flake8, let us know on Slack.

Bandit: less verbose logging

Bandit output can be extremely verbose, including on successful runs. You may want to use its --quiet option, which will turn off output for successful runs but keep it for failures.

For example, you can set this in your pants.toml:

[bandit]
args = ["--quiet"]

isort: possible issues with its import classifier algorithm

Some Pants users had to explicitly set default_section = "THIRDPARTY" to get iSort 5 to correctly classify their first-party imports, even though this is the default value.

They report that this config works for them:

# pyproject.toml
[tool.isort]
known_first_party = ["my_org"]
default_section = "THIRDPARTY"

You may also want to try downgrading to iSort 4.x by setting version = "isort>=4.6,<5" in the [isort] options scope.


Did this page help you?