Code Quality¶
This guide covers the code quality assurance tools and practices used in this Python package template, including linting, security scanning, and spell checking.
Overview¶
Code quality is maintained through a combination of automated tools and manual reviews. The template includes:
- Linting: Ruff for code style and errors
- Security scanning: Bandit for vulnerability detection
- Spell checking: CSpell for documentation and comments
- Type checking: Pyright for static analysis
- Automated enforcement: Pre-commit hooks and CI checks
Linting Tools¶
Ruff¶
Ruff is a fast Python linter written in Rust that combines multiple tools.
Features:
- Extremely fast (10-100x faster than other linters)
- Comprehensive rule set covering style, errors, and complexity
- Auto-fixing capabilities
- Built-in formatter
Configuration (pyproject.toml):
[tool.ruff]
line-length = 120 # Matches Black's default
target-version = "py310" # Align with CI (3.10–3.12) and requires-python (>=3.10)
lint.extend-select = [
"E",
"F",
"W", # pycodestyle errors and warnings
"I", # isort rules
"UP", # pyupgrade rules
"C90", # McCabe complexity
"N", # PEP8 naming
"B", # Bugbear (opinionated checks)
"A", # Flake8-builtins
"C4", # Flake8-comprehensions
"PT", # Flake8-pytest
"RUF", # Ruff-specific rules
"PL", # Add pylint rules for fuller coverage
"SIM", # Similarities
"T10", # Debugger statements
"DOC", # Docstring
"D", # Doc style
]
lint.ignore = [
"E501", # Line length (handled by formatter)
"C901", # Disable overly strict complexity checks (optional, adjust as needed)
]
fix = true # Enable autofix
lint.unfixable = [] # List rules that should not be autofixed, if any
[tool.ruff.format]
quote-style = "double" # Use double quotes for strings
indent-style = "space" # Use spaces for indentation
docstring-code-format = true # Format code in docstrings
[tool.ruff.lint.pydocstyle]
Usage:
# Lint code
ruff check src/
# Auto-fix issues
ruff check --fix src/
# Format code
ruff format src/
Security Scanning¶
Bandit¶
Bandit finds common security issues in Python code.
Configuration (pyproject.toml):
[tool.bandit]
exclude_dirs = ["build", "dist", "tests", "scripts"]
number = 4
recursive = true
Common security issues detected:
- Use of
assertstatements - Shell injection vulnerabilities
- Weak cryptographic practices
- Hardcoded passwords
- Unsafe deserialization
Usage:
bandit -r src/
Spell Checking¶
CSpell¶
CSpell checks spelling in code comments, documentation, and strings.
Configuration (cspell.json):
{
"$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json",
"version": "0.2",
"language": "en",
"dictionaries": ["en", "ignore-words", "python", "softwareTerms", "node"],
"dictionaryDefinitions": [
{
"name": "ignore-words",
"path": ".ignore_words.txt",
"addWords": true
}
],
"languageSettings": [
{
"languageId": "*",
"locale": "en",
"dictionaries": ["en", "ignore-words"]
},
{
"languageId": "python",
"locale": "en",
"dictionaries": [
"en",
"ignore-words",
"python",
"softwareTerms",
"node"
]
}
],
"ignorePaths": ["node_modules/**", ".ignore_words.txt", "**/dist/**"],
"ignoreRegExpList": [
"np(?:\\.[a-zA-Z_][a-zA-Z0-9_]*)+",
"scipy(?:\\.[a-zA-Z_][a-zA-Z0-9_]*)+"
]
}
Usage:
cspell "**/*.{py,md,yml,yaml}"
Pre-commit Integration¶
All quality tools run automatically via pre-commit hooks.
Configuration (.pre-commit-config.yaml):
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: check-added-large-files
stages: [pre-commit]
- id: check-case-conflict
stages: [pre-commit]
- id: check-merge-conflict
stages: [pre-commit]
- id: check-symlinks
stages: [pre-commit]
- id: check-yaml
stages: [pre-commit]
- id: check-toml
stages: [pre-commit]
- id: debug-statements
stages: [pre-commit]
- id: end-of-file-fixer
stages: [pre-commit]
- id: mixed-line-ending
stages: [pre-commit]
- id: requirements-txt-fixer
stages: [pre-commit]
- id: trailing-whitespace
stages: [pre-commit]
- id: check-docstring-first
stages: [pre-commit]
- id: check-json
stages: [pre-commit]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.9
hooks:
- id: ruff
args: [--fix, --config=pyproject.toml]
stages: [pre-commit]
- id: ruff-format
stages: [pre-commit]
- repo: https://github.com/streetsidesoftware/cspell-cli
rev: v9.7.0 # Keep this version or update to the latest stable
hooks:
- id: cspell
name: Check spelling in Python, reSt, and Markdown files.
args: ['--config', 'cspell.json']
files: ^.*\.(py|rst|md)$
stages: [pre-commit]
- id: cspell
name: Check commit message spelling.
args:
- --config
- cspell.json
- --no-must-find-files
- --no-progress
- --no-summary
- --files
- .git/COMMIT_EDITMSG
stages: [commit-msg]
always_run: true
- repo: https://github.com/ComPWA/taplo-pre-commit
rev: v0.9.3
hooks:
- id: taplo-format
- repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.10.0
hooks:
- id: python-check-blanket-noqa
stages: [pre-commit]
- id: python-check-blanket-type-ignore
stages: [pre-commit]
- id: python-no-log-warn
stages: [pre-commit]
- id: python-no-eval
stages: [pre-commit]
- id: python-use-type-annotations
stages: [pre-commit]
- id: rst-backticks
stages: [pre-commit]
- id: rst-directive-colons
stages: [pre-commit]
- id: rst-inline-touching-normal
stages: [pre-commit]
- repo: https://github.com/PyCQA/bandit
rev: '1.9.4'
hooks:
- id: bandit
args: ['-c', 'pyproject.toml']
stages: [pre-commit]
additional_dependencies: ['.[toml]']
- repo: https://github.com/rbubley/mirrors-prettier
rev: v3.8.1
hooks:
- id: prettier
stages: [pre-commit]
- repo: https://github.com/DavidAnson/markdownlint-cli2
rev: v0.22.0
hooks:
- id: markdownlint-cli2
stages: [pre-commit]
args:
- --config
- .markdownlint.yaml
- repo: https://github.com/astral-sh/uv-pre-commit
rev: 0.11.3
hooks:
# Update the uv lockfile
- id: uv-lock
- repo: https://github.com/Yelp/detect-secrets
rev: v1.5.0 # Use the latest stable version
hooks:
- id: detect-secrets
name: Detect secrets within the codebase
description:
Detect and block secrets from committing to the codebase
CI/CD Quality Checks¶
Quality checks run automatically in GitHub Actions and pre-commit.ci:
- Pull requests: Pre-commit.ci runs all hooks and auto-fixes issues
- Main branch: Full quality suite via GitHub Actions
- Releases: Comprehensive checks
Pre-commit.ci Setup¶
Pre-commit.ci provides automated pre-commit hook execution:
- Install the GitHub App: Go to pre-commit.ci and install the app on your repository
-
Configuration (
.pre-commit-config.yaml):repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 hooks: - id: check-added-large-files stages: [pre-commit] - id: check-case-conflict stages: [pre-commit] - id: check-merge-conflict stages: [pre-commit] - id: check-symlinks stages: [pre-commit] -
Automatic execution: Pre-commit.ci runs on every PR and push
Code Quality Metrics¶
Complexity Analysis¶
Use tools to measure code complexity:
# McCabe complexity
python -m mccabe src/your_package/
# Radon metrics
radon cc src/ -a
radon mi src/ -s
Coverage Requirements¶
Test coverage is enforced:
envlist = py, integration, spark, all
Quality Gates¶
- Linting: Zero errors allowed
- Security: Critical vulnerabilities must be fixed
- Coverage: X% coverage required (You may gradually increase this)
- Spelling: No spelling errors in documentation
Best Practices¶
Code Style¶
- Follow PEP 8: Use consistent formatting
- Descriptive names: Use clear, meaningful identifiers
- DRY principle: Avoid code duplication
- SOLID principles: Write maintainable code
Documentation¶
- Docstrings: Document all public functions/classes
- Comments: Explain complex logic
- README: Clear project description
- Changelog: Track changes systematically
Security¶
- Input validation: Validate all inputs
- Secure defaults: Use secure defaults
- Dependency updates: Keep dependencies current
- Secrets management: Never commit secrets
Performance¶
- Efficient algorithms: Choose appropriate data structures
- Memory management: Avoid memory leaks
- Profiling: Use profiling tools for optimization
- Caching: Implement caching where beneficial
Tool Selection Rationale¶
Why Multiple Linters¶
- Ruff: Fast, comprehensive, auto-fixing
- Pylint: Detailed analysis, custom rules
- Flake8: Legacy compatibility, extensive plugins
Why Bandit¶
- Security focus: Catches common vulnerabilities
- Python specific: Understands Python security issues
- Configurable: Can be tuned for project needs
Why CSpell¶
- Multi-language: Works with Python, Markdown, YAML
- Custom dictionaries: Project-specific terminology
- IDE integration: Works with editors
Customizing Quality Checks¶
Adding New Rules¶
[tool.ruff]
select = ["E", "F", "B", "I", "CUSTOM"]
Ignoring False Positives¶
[tool.ruff.per-file-ignores]
"specific_file.py" = ["RULE_CODE"]
Custom Security Rules¶
Extend Bandit with custom plugins:
# custom_bandit_plugin.py
import bandit
from bandit.core import test_properties as test
@test.checks('Call')
@test.test_id('B999')
def custom_check(context):
# Custom security check logic
pass
Troubleshooting¶
Common Issues¶
- False positives: Use ignore rules judiciously
- Performance: Ruff is fastest, use for large codebases
- Configuration conflicts: Ensure consistent settings
- CI failures: Check tool versions and configurations
Debugging Quality Issues¶
- Verbose output: Use
--verboseflags - Specific files: Run tools on individual files
- Configuration validation: Test configurations separately
Performance Optimization¶
- Parallel execution: Use multiple cores where possible
- Incremental checks: Only check changed files
- Caching: Leverage tool caching features
Integration with IDEs¶
VS Code¶
{
"cSpell.words": ["pypi", "mkdocs"]
}
PyCharm¶
- Configure external tools for Ruff, Bandit
- Enable spell checking
- Set up pre-commit integration
Continuous Improvement¶
Metrics Tracking¶
Track quality metrics over time:
- Code coverage trends
- Complexity measurements
- Security vulnerability counts
- Performance benchmarks
Regular Audits¶
- Dependency audits: Check for vulnerabilities
- Code reviews: Manual quality checks
- Performance reviews: Optimization opportunities
- Security assessments: Penetration testing
Tool Updates¶
Keep tools current:
# Update pre-commit hooks
pre-commit autoupdate
# Update Python packages
pip install --upgrade ruff bandit
# Update Node.js packages
npm update cspell
For more information, see the documentation for individual tools: Ruff, Bandit, CSpell, Pyright.