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):
# 3.14159 here is a magic number (a hardcoded value with no context)
def calculate_circle_area(radius):
return 3.14159 * radius * radiusRefactored Version:
import math
def calculate_circle_area(radius):
return math.pi * radius * radiusCommon 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
Popular Python Linters
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
Popular Python Formatters
- 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:
pip install pre-commit- Configure
pre-commitby creating.pre-commit-config.yamlfile:
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:
pre-commit install