Check Python with Flake8 Bugbear#

Flake8 is a Python tool that checks Python code for style, programming errors, and help reduce code complexity. It is a wrapper around PyFlakes, pycodestyle, and Ned Batchelder’s McCabe script. Flake8 has a plugin structure and there are many plugins available. One of the plugins is Flake8 Bugbear. Flake8 Bugbear is a plugin for Flake8 that finds likely bugs and design problems in your program. It is a plugin for Flake8 that adds rules from the community that are not included in the core flake8 package.

Installing Flake8 Bugbear#

As Flake8 Bugbear is a plugin for Flake8, you need to install Flake8 first. You can install Flake8 Bugbear with pip for example and in the example below Flake8 Bugbear is installed into a virtual environment.

Install Flake8 plugin Bugbear into a virtual environment#
$ python3 -m venv .venv
$ source .venv/bin/activate
(.venv) $ python3 -m pip install flake8-bugbear
Requirement already satisfied: flake8-bugbear in ./.venv/lib/python3.10/site-packages (23.7.10)
Requirement already satisfied: flake8>=6.0.0 in ./.venv/lib/python3.10/site-packages (from flake8-bugbear) (6.0.0)
Requirement already satisfied: attrs>=19.2.0 in ./.venv/lib/python3.10/site-packages (from flake8-bugbear) (23.1.0)
Requirement already satisfied: pyflakes<3.1.0,>=3.0.0 in ./.venv/lib/python3.10/site-packages (from flake8>=6.0.0->flake8-bugbear) (3.0.1)
Requirement already satisfied: mccabe<0.8.0,>=0.7.0 in ./.venv/lib/python3.10/site-packages (from flake8>=6.0.0->flake8-bugbear) (0.7.0)
Requirement already satisfied: pycodestyle<2.11.0,>=2.10.0 in ./.venv/lib/python3.10/site-packages (from flake8>=6.0.0->flake8-bugbear) (2.10.0)

With command flake8 --version you can check the Flake8 and Flake8 Bugbear version, and you can see the plugins installed.

Show Flake8 and plugin Bugbear version#
(.venv) $ flake8
6.0.0 (flake8-bugbear: 23.7.10, mccabe: 0.7.0, pycodestyle: 2.10.0, pyflakes: 3.0.1) CPython 3.10.6 on Linux

The next step is to run Flake8 on some Python code. In the example below Flake8 is run on a Python file with the extension .py.

Check Python code with Flake8#
(.venv) $ flake8 *py

By default Flake8 will not show the source code of the Python file. To show the source code of the Python file you can use the option --show-source.

Check Python code with Flake8 and show source#
(.venv) $ flake8 --showsource *py

Flake8 Bugbear warnings#

The Bugbear plugin for Flake8 adds rules from the community that are not included in the core flake8 package. The Bugbear plugin for Flake8 adds the following warnings to Flake8 for example. Let’s have a look at some of the warnings and how to fix them.

B005: Using .strip() with multi-character strings is misleading the reader#

In post Use removeprefix instead of lstrip to remove a prefix from a string the warning B005 is explained. The warning B005 is about using str.strip() with multi-character strings. The warning B005 is about using str.strip() with multi-character strings. It is misleading the reader. It looks like stripping a substring. Move your character set to a constant if this is deliberate. Use str.replace(), bytearray.removeprefix(), bytearray.removesuffix(), or regular expressions to remove string fragments.

The example below shows the warning B005 and how to fix it.

Example with a B005 warning#
#!/usr/bin/env python3

def main():
  line = 'www.example.org'
  print(line.lstrip('www.'))
  print(line.removeprefix('www.'))


if __name__ == '__main__':
  main()

If we run Flake8 on the Python file above we get the warning B005 as shown below.

Flake8 Bugbear detects a B005 warning#
(.venv) $ flake8 B005.py
./B005.py:5:11: B005 Using .strip() with multi-character strings is misleading the reader. It looks like stripping a substring. Move your character set to a constant if this is deliberate. Use .replace(), .removeprefix(), .removesuffix(), or regular expressions to remove string fragments.

B012: Use control blocks will silence exceptions or override return values#

Another common one is B012. The warning B012 is about using control blocks will silence exceptions or override return values. The warning B012 is about using return, continue, or break statements inside a finally block. This causes exceptions to be silenced. Exceptions should be silenced in except blocks and the control statements can be moved outside the finally block.

Example with multiple B012 warnings#
#!/usr/bin/env python3

def a():
  try:
    pass
  finally:
    return  # warning


def main():
  while True:
    try:
      pass
    finally:
      continue  # warning

  while True:
    try:
      pass
    finally:
      break  # warning


if __name__ == '__main__':
  main()

Running Flake8 on the Python file above will give us three warnings B012 as shown below. The solution is to move the return, continue, or break statements outside the finally block but it depends on the use case on how to fix it.

Flake8 Bugbear detects multiple B012 warnings#
(.venv) $ flake8 B012.py
B012.py:7:9: B012 return/continue/break inside finally blocks cause exceptions to be silenced. Exceptions should be silenced in except blocks. Control statements can be moved outside the finally block.
B012.py:15:13: B012 return/continue/break inside finally blocks cause exceptions to be silenced. Exceptions should be silenced in except blocks. Control statements can be moved outside the finally block.
B012.py:21:13: B012 return/continue/break inside finally blocks cause exceptions to be silenced. Exceptions should be silenced in except blocks. Control statements can be moved outside the finally block.

B020: Loop control variable overrides iterable it iterates#

Another good example is the warning B020. The warning B020 is about a loop control variable that overrides an iterable it iterates. The example below shows the warning B020 and how to fix it. The warning B020 is about a loop control variable that overrides an iterable it iterates. The example below shows the warning B020 and how to fix it.

Example of the B020 warning#
#!/usr/bin/env python3

def main():
  values = {"secret": 123}

  for key, value in values.items():
    print(f"{key}, {value}")

  for key, values in values.items():
    print(f"{key}, {values}")


if __name__ == '__main__':
  main()

If we run Flake8 on the Python file above we get the warning B020 as shown below. The warning B020 is about a loop control variable that overrides an iterable it iterates with each iterable value in the second for loop as there is a variable called values in the for loop.

Flake8 Bugbear detects a B020 warning#
(.venv) $ flake8 B020.py
B020.py:9:14: B020 Found for loop that reassigns the iterable it is iterating with each iterable value.

Using .flake8 file#

Adding parameters to the Flake8 command line can be cumbersome. You can use a .flake8 file to store the parameters for Flake8. The example below shows a .flake8 file for Flake8 as we used in the previous examples. It also excludes some directories from the Flake8 check to avoid warnings about third-party code.

Example of a .flake8 file for Flake8#
[flake8]
max-line-length = 88
max-complexity = 18
select = B,C,E,F,W,T4,B9
ignore = E203, E501, W503, B950
exclude = .git,__pycache__,.venv,.mypy_cache,.pytest_cache
show-source = True
statistics = True
count = True
format = default

Final thoughts about Bugbear#

Flake8 already hints to a lot of improvements you can make in your Python code. The Bugbear plugin for Flake8 adds even more warnings to Flake8 that will lead to common mistakes like the ones shown above. It is a good idea to use the Bugbear plugin for Flake8 to check your Python code for common mistakes. Many more plugins are available for Flake8 but the Bugbear plugin is a good start to check code for possible issues.