Code Quality Standards
August 24, 2025 · View on GitHub
Overview
This document outlines the code quality standards and tools used in the ChukTerm project. We use automated tools to ensure consistent code style, catch potential bugs, and maintain high code quality across the codebase.
Tools and Configuration
1. Ruff - Fast Python Linter
Purpose: Catches code quality issues, potential bugs, and enforces Python best practices.
Configuration: pyproject.toml
[tool.ruff]
line-length = 120
target-version = "py310"
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"B", # flake8-bugbear
"I", # isort
"N", # pep8-naming
"UP", # pyupgrade
"ARG", # flake8-unused-arguments
"SIM", # flake8-simplify
]
ignore = []
2. Black - Code Formatter
Purpose: Automatically formats Python code to ensure consistent style.
Configuration: pyproject.toml
[tool.black]
line-length = 120
target-version = ['py310']
3. MyPy - Type Checker
Purpose: Static type checking for Python code to catch type-related bugs before runtime.
Configuration: pyproject.toml
[tool.mypy]
python_version = "3.10"
warn_return_any = true
warn_unused_configs = true
no_implicit_reexport = true
disallow_untyped_defs = false # Relaxed for gradual typing
disallow_any_unimported = false
check_untyped_defs = true
strict_equality = true
warn_redundant_casts = true
warn_unused_ignores = false
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "tests.*"
ignore_errors = true
[[tool.mypy.overrides]]
module = "chuk_term.ui.*"
disallow_untyped_defs = false # UI modules use gradual typing
Running Code Quality Checks
Quick Commands
# Check all code quality issues
uv run ruff check src/ tests/
# Auto-fix safe issues
uv run ruff check --fix src/ tests/
# Auto-fix including unsafe fixes (review changes!)
uv run ruff check --fix --unsafe-fixes src/ tests/
# Check formatting
uv run black --check src/ tests/
# Format code
uv run black src/ tests/
# Type checking
uv run mypy src/
# Run all quality checks at once
make check
# Or manually:
uv run ruff check src/ tests/ && \
uv run black --check src/ tests/ && \
uv run mypy src/ && \
uv run pytest
Pre-commit Hooks
For automatic checking before commits:
# Install pre-commit
uv add --dev pre-commit
# Install hooks
pre-commit install
# Run manually on all files
pre-commit run --all-files
Common Issues and Solutions
MyPy Errors
Missing Type Annotations
# Error: Function is missing a return type annotation
def process(data):
return data * 2
# Fix: Add type hints
def process(data: int) -> int:
return data * 2
# Or suppress if intentional
def process(data): # type: ignore[no-untyped-def]
return data * 2
Incompatible Types
# Error: Incompatible return value type
def get_value() -> str:
return None # Error!
# Fix: Use Optional
from typing import Optional
def get_value() -> Optional[str]:
return None
# Or use union syntax (Python 3.10+)
def get_value() -> str | None:
return None
Type Ignore Comments
# Ignore specific error
result = complex_function() # type: ignore[return-value]
# Ignore all errors on a line (use sparingly!)
result = complex_function() # type: ignore
Ruff Errors
F401 - Unused Import
# Bad
import os # F401: unused import
# Good - remove unused import or use it
import os
print(os.getcwd())
# Or if needed for re-export
import os # noqa: F401
ARG001/ARG002 - Unused Function/Method Arguments
# Bad
def process(data, unused_param): # ARG001
return data
# Good - use underscore prefix
def process(data, _unused_param):
return data
# Or add noqa for legitimate cases (e.g., test mocks)
def test_method(self, mock_input): # noqa: ARG002
assert something
SIM117 - Combine Multiple Context Managers
# Bad
with open('file1') as f1:
with open('file2') as f2:
process(f1, f2)
# Good
with (
open('file1') as f1,
open('file2') as f2
):
process(f1, f2)
B007 - Unused Loop Variable
# Bad
for i in range(10):
print("hello")
# Good
for _ in range(10):
print("hello")
W293 - Blank Line Contains Whitespace
# Bad
def function():
"""Docstring."""
···· # Whitespace on blank line
return None
# Good
def function():
"""Docstring."""
# No whitespace on blank line
return None
Black Formatting
Black is opinionated and will automatically format your code. Common changes include:
- String quotes: Prefers double quotes
- Line breaks: Adds/removes based on line length
- Trailing commas: Adds in multi-line structures
- Parentheses: Adds for clarity in complex expressions
# Before Black
x = {'a':1,'b':2,'c':3}
very_long_function_name(argument1,argument2,argument3,argument4)
# After Black
x = {"a": 1, "b": 2, "c": 3}
very_long_function_name(
argument1,
argument2,
argument3,
argument4,
)
Code Quality Standards
Line Length
- Maximum: 120 characters
- Exceptions: URLs, long strings with no natural break points
Imports
- Grouped and sorted automatically by Ruff
- Order: standard library, third-party, local
- One import per line for clarity
Type Hints
- Required for all public functions
- Encouraged for complex internal functions
- Use modern syntax (e.g.,
list[str]notList[str])
# Good
def process_items(items: list[str], count: int) -> dict[str, int]:
"""Process items and return counts."""
return {item: count for item in items}
Docstrings
- Required for all public modules, classes, and functions
- Use Google or NumPy style consistently
- Include Args, Returns, Raises sections when applicable
def calculate_average(numbers: list[float], weights: list[float] | None = None) -> float:
"""
Calculate weighted average of numbers.
Args:
numbers: List of numbers to average
weights: Optional weights for each number
Returns:
Weighted average of the numbers
Raises:
ValueError: If weights don't match numbers length
"""
CI/CD Integration
GitHub Actions Example
name: Code Quality
on: [push, pull_request]
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: |
pip install uv
uv sync --dev
- name: Run Ruff
run: uv run ruff check src/ tests/
- name: Check Black formatting
run: uv run black --check src/ tests/
- name: Run MyPy
run: uv run mypy src/
Suppressing Warnings
Sometimes you need to suppress warnings for legitimate reasons:
File-level Suppression
# ruff: noqa: ARG002 # Suppress unused argument warnings for entire file
Line-level Suppression
import unused_module # noqa: F401 # Needed for side effects
Block-level Suppression (Ruff)
# fmt: off
complex_matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]
# fmt: on
Best Practices
- Run checks before committing: Use pre-commit hooks or run manually
- Fix issues immediately: Don't let quality issues accumulate
- Review auto-fixes: Always review changes made by
--fixflags - Keep tools updated: Regular updates via
uv lock --upgrade - Document exceptions: If suppressing a warning, add a comment explaining why
- Consistent configuration: Share config via
pyproject.toml
Metrics and Goals
Current Status
- ✅ All files pass Ruff checks
- ✅ All files formatted with Black
- ✅ MyPy type checking passes (with gradual typing)
Quality Goals
- Zero Ruff errors in production code
- 100% Black formatted
- No suppressed warnings without documentation
- Type hints for all public APIs
- Docstrings for all public functions
Troubleshooting
Common Problems
Problem: Ruff and Black disagree on formatting Solution: Black takes precedence for formatting; Ruff for linting
Problem: Too many errors to fix manually
Solution: Use ruff check --fix --unsafe-fixes carefully, review all changes
Problem: CI fails but local passes
Solution: Ensure same tool versions; run uv lock --upgrade
Problem: Import order keeps changing Solution: Let Ruff handle imports with isort rules
Resources
- Ruff Documentation
- Black Documentation
- MyPy Documentation
- PEP 8 Style Guide
- Google Python Style Guide
Conclusion
Maintaining high code quality is essential for the ChukTerm project. These tools and standards help us:
- Write more maintainable code
- Catch bugs early
- Ensure consistency across contributors
- Reduce code review time
- Improve overall code reliability
Remember: Clean code is a team effort! 🚀