Skip to content

Linting and Formatting in Python

Linting and formatting are essential practices in Python development that enhance code quality, readability, and maintainability.

Linting

NOTE

Static code analysis means examining your source code without actually running it.

Linting is the process of statically analyzing source code for potential errors, stylistic issues, and code smells. Linters check code against a set of rules and guidelines, such as PEP 8, the style guide for Python code. This process helps to prevent bugs, enforce coding standards, and improve overall code quality.

What is a Code Smell?

A code smell is a general term for any symptom in code that may indicate a deeper problem. It’s not necessarily a bug, but it suggests a need for refactoring.

Examples of code smells include

NOTE

Cyclomatic Complexity is a software metric that measures the complexity of a program’s control flow. In simpler terms, how many different execution paths exist through a function or method.

DRY stands for Don’t Repeat Yourself.

  • High cyclomatic complexity: Function has too many branches or paths, making it hard to understand or test.
  • Duplicate code: Same logic appears in multiple places; violates the DRY principle.
  • Long methods: Functions that do too much; hard to read, test, and reuse.
  • Large classes: Classes that take on too many responsibilities; difficult to maintain.
  • Too many parameters: Functions with many arguments are hard to use and understand; may signal poor object modeling.
  • Feature envy: A method that accesses the data or behavior of another object more than its own.
  • Shotgun surgery: A small change requires edits in many places; indicates low cohesion.
  • Magic Numbers: Hardcoded numeric values used without explanation; should be named constants for clarity and flexibility.
  • and many more.

Example of Code Smell (Magic Numbers):

python
# 3.14159 here is a magic number (a hardcoded value with no context)
def calculate_circle_area(radius):
    return 3.14159 * radius * radius

Refactored Version:

python
import math

def calculate_circle_area(radius):
    return math.pi * radius * radius

Common issues caught by linters

  • Unused imports or variables
  • Bad naming conventions
  • Missing docstrings
  • Code smells
    • Examples: duplicate code, long functions, too many parameters
  • Code that may crash or behave unexpectedly
    • Examples: unreachable code, division by zero, use of undefined variables

pyflakes

A simple linter that focuses on detecting errors.

  • pyflakes: A simple linter that focuses on detecting errors.
  • pycodestyle: TBD
  • pylint: A highly configurable linter that checks for errors, enforces coding standards, and looks for bad code smells.
  • flake8: A wrapper around PyFlakes, pycodestyle, and mccabe, providing a comprehensive linting solution.
  • autoflake: TBD
  • ruff: A fast linter and formatter written in Rust, designed as a drop-in replacement for multiple tools.

Formatting

Formatting is the process of automatically adjusting code layout to ensure consistency and improve readability. Formatters restructure code, adjusting spacing, line length, and argument positioning to adhere to a defined style. While they modify how the code looks, they do not alter its underlying logic or behavior.

Common issues handled by formatters

  • Indentation and Spacing
  • Line length
  • Use of single vs double quotes
  • Import ordering
  • black: An opinionated code formatter that enforces a consistent style.
  • yapf: Google's formatter that aims for consistent code style.
  • autopep8: fixes code to conform to PEP8.
  • isort: sorts imports automatically.
  • ruff: Ruff is also a formatter, offering both linting and formatting capabilities.

Pre-commit Hooks

Linters and formatters can be integrated into development environments or run from the command line. You can also run your linters and formatters automatically before each commit, using tools like pre-commit hook.

  • Install the package:
shell
pip install pre-commit
  • Configure pre-commit by creating .pre-commit-config.yaml file:
yaml
repos:
  - repo: https://github.com/psf/black
    rev: stable
    hooks:
      - id: black
  - repo: https://github.com/pre-commit/mirrors-flake8
    rev: v6.1.0
    hooks:
      - id: flake8
  • Install pre-commit hook:
shell
pre-commit install