Everyone's raving about the new hip Python code formatter: black. It's highly opinionated and uncompromising, and in exchange for you giving in to its styling, it offers you the opportunity to program faster on a consistent codebase using less of your time and energy.

That's all fine and dandy, but how does it compare to the other Python formatters already out there? As I started researching the answer to this question, I quickly came across articles comparing black to other Python linters, but linters and formatters aren't quite the same, which meant that the comparisons didn't always make sense. So what's the difference?

Linters

A linter is a tool that does static code analysis to flag syntax bugs, style errors, and missed best practices. Examples of linters include clang-tidy, eslint, golint, and pylint.

When used on a program, the output is usually a list of all the lines of code that have violated some linter rule paired with the identifier for the rule that was violated. Rules vary from linter to linter though they may be based on a common standard, e.g. PEP8 for Python. For a more specific example, take a look at eslint, which defines its set of rules here and produces output that looks like this for Javascript programs:


/var/lib/jenkins/workspace/Releases/eslint Release/eslint/fullOfProblems.js
  1:10  error    'addOne' is defined but never used             no-unused-vars
  2:9   error    Use the isNaN function to compare with NaN    use-isnan
  3:16  error    Unexpected space before unary operator '++'   space-unary-ops
  3:20  warning  Missing semicolon                             semi
  4:12  warning  Unnecessary 'else' after 'return'             no-else-return
  5:1   warning  Expected indentation of 8 spaces but found 6  indent
  5:7   error    Function 'addOne' expected a return value     consistent-return
  5:13  warning  Missing semicolon                             semi
  7:2   error    Unnecessary semicolon                         no-extra-semi

✖ 9 problems (5 errors, 4 warnings)
  2 errors and 4 warnings potentially fixable with the `--fix` option.

Formatters

A formatter is a tool that converts code from one format to another, with the goal of standardizing style across the codebase. Examples are black, clang-format, gofmt, and prettier.

Unlike linters, formatters don't usually produce separate output; they simply change the code directly. And while it might seem like linters already catch style errors and should make formatters unnecessary, formatters tend to be better at styling code consistently regardless of how the input code is currently styled. This is because formatters work by regenerating code directly from the AST rather than only using the AST to inform them of how to modify the existing code, as linters do.

Most importantly, the use of linters and formatters to improve code shouldn't be mutually exclusive. In fact, some languages already promote their use together, e.g. the prettier-standard library combines eslint with prettier into one tool. Together, linters and formatters make fantastic tools that reduce potential bugs in code and increase team efficiency.

And as for how well black compares to other formatters, that'll be the topic of a future blog post!