Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
11 KiB
Code Quality
This guide covers the code quality tools and standards used in the Wizamart platform.
Overview
The project uses modern Python tooling to maintain high code quality:
- Ruff - All-in-one linter and formatter (replaces black, isort, flake8, and more)
- pre-commit - Git hooks that auto-fix lint issues before each commit
- mypy - Static type checker
- pytest - Testing framework with coverage reporting
All tools are configured in pyproject.toml for consistency and ease of use.
Setup (after cloning)
# Install dev dependencies (includes ruff, pre-commit, mypy)
pip install -r requirements-dev.txt
# Install pre-commit git hooks (required once per clone)
pre-commit install
The pre-commit hook runs automatically on every git commit and will:
- Validate architecture patterns
- Fix trailing whitespace and missing end-of-file newlines
- Auto-fix ruff lint issues (import sorting, unused imports, etc.)
If a hook modifies files (e.g. fixes imports), the commit is aborted. Simply git add the fixed files and commit again.
Quick Start
# Format code
make format
# Lint and auto-fix issues
make lint
# Run both formatting and linting
make check
# Strict linting (no auto-fix) - for CI/CD
make lint-strict
# Run pre-commit hooks manually on all files
pre-commit run --all-files
Ruff - Modern Linting and Formatting
Ruff is a blazingly fast Python linter and formatter written in Rust. It replaces multiple tools with a single, comprehensive solution.
What Ruff Does
Formatting (replaces black):
- Consistent code style
- Automatic quote normalization
- Line length enforcement (88 characters)
Linting (replaces flake8, isort, pyupgrade, and more):
- Import sorting and organization
- Code style checks (PEP 8)
- Bug detection (flake8-bugbear)
- Modern Python syntax suggestions (pyupgrade)
- Code simplification suggestions
Enabled Rule Sets
| Code | Description | Purpose |
|---|---|---|
| E, W | pycodestyle | PEP 8 style enforcement |
| F | pyflakes | Basic error detection |
| I | isort | Import sorting |
| N | pep8-naming | Naming conventions |
| UP | pyupgrade | Modern Python syntax |
| B | flake8-bugbear | Common bug patterns |
| C4 | flake8-comprehensions | Better comprehensions |
| SIM | flake8-simplify | Code simplification |
| PIE | flake8-pie | Misc. lints |
| RET | flake8-return | Return statement improvements |
| Q | flake8-quotes | Quote consistency |
Usage
# Format all code
make format
# or directly:
python -m ruff format .
# Lint and auto-fix issues
make lint
# or directly:
python -m ruff check . --fix
# Check without fixing (CI/CD)
make lint-strict
# or directly:
python -m ruff check .
Configuration
Ruff is configured in pyproject.toml:
[tool.ruff]
line-length = 88
target-version = "py311"
exclude = [".git", ".venv", "venv", "__pycache__", "alembic/versions"]
[tool.ruff.lint]
select = ["E", "W", "F", "I", "N", "UP", "B", "C4", "SIM", "PIE", "RET", "Q"]
ignore = ["E501", "B008", "RET504", "SIM102"]
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
mypy - Type Checking
mypy performs static type analysis to catch type-related errors before runtime.
Usage
# Run type checking
python -m mypy .
# Or as part of lint
make lint
Configuration
mypy is configured in pyproject.toml:
[tool.mypy]
python_version = "3.11"
warn_return_any = true
warn_unused_configs = true
ignore_missing_imports = true
exclude = ["^\\.venv/", "^venv/", "^alembic/versions/"]
Testing with pytest
Run tests with coverage reporting:
# Run all tests
make test
# Run with coverage
make test-coverage
# Run only unit tests
make test-unit
# Run only integration tests
make test-integration
# Run fast tests (skip slow ones)
make test-fast
Coverage Configuration
Coverage settings are in pyproject.toml:
[tool.coverage.run]
source = ["app", "models", "middleware", "tasks", "storage"]
omit = ["*/tests/*", "*/venv/*", "*/alembic/*"]
[tool.coverage.report]
precision = 2
show_missing = true
Makefile Commands
Code Quality Commands
| Command | Description | When to Use |
|---|---|---|
make format |
Format code with Ruff | Before committing |
make lint |
Lint and auto-fix with Ruff + mypy | Before committing |
make lint-strict |
Lint without auto-fix + mypy | In CI/CD pipelines |
make arch-check |
Validate architecture patterns | Before committing |
make arch-check-file file="path" |
Check a single file | Quick validation |
make arch-check-object name="merchant" |
Check all files for an entity | Entity-wide validation |
make check |
Run format + lint | Quick pre-commit check |
make ci |
Full CI pipeline (strict lint + coverage) | CI/CD workflows |
make qa |
Quality assurance (format + lint + arch-check + coverage + docs) | Before releases |
Testing Commands
| Command | Description |
|---|---|
make test |
Run all tests |
make test-unit |
Run unit tests only |
make test-integration |
Run integration tests only |
make test-coverage |
Run tests with coverage report |
make test-fast |
Run fast tests (skip slow ones) |
Architecture Validation
The architecture validator ensures code follows established patterns and best practices.
Quick Start
# Check all files
make arch-check
# Check a single file
make arch-check-file file="app/api/v1/admin/stores.py"
# Check all files related to an entity
make arch-check-object name="merchant"
make arch-check-object name="store"
What It Checks
| Category | Examples |
|---|---|
| API Endpoints | Pydantic models, no business logic, exception handling, auth |
| Service Layer | No HTTPException, proper exceptions, DB session as param |
| Models | SQLAlchemy/Pydantic separation |
| JavaScript | Centralized logger, apiClient usage |
| Templates | Base template inheritance |
Output
The validator provides a summary table showing pass/fail status per file:
📋 FILE SUMMARY:
--------------------------------------------------------------------------------
File Status Errors Warnings
----------------------------- -------- ------- --------
app/api/v1/admin/merchants.py ❌ FAILED 6 9
app/services/merchant_service.py ✅ PASSED 0 0
--------------------------------------------------------------------------------
Total: 2 files | ✅ 1 passed | ❌ 1 failed | 6 errors | 9 warnings
See Architecture Rules Reference for detailed rule documentation.
Pre-Commit Workflow
Before committing code:
# 1. Format, lint, and verify critical imports
make check
# 2. Validate architecture patterns
make arch-check
# 3. Run relevant tests
make test-fast
# 4. If all passes, commit
git add .
git commit -m "your message"
Critical Import Verification
The make check command includes a critical import verification step that ensures re-export imports haven't been removed by linters.
What it checks:
models/database/base.py- Re-exports Base from app.core.databasemodels/__init__.py- Exports Base for Alembicmodels/database/__init__.py- Exports Base from database package
Why it matters: Linters like Ruff may see these imports as "unused" (F401) because they're re-exported, not directly used. Removing them breaks the application.
Manual verification:
make verify-imports
If this fails, imports have been removed and must be restored.
CI/CD Integration
For continuous integration:
# Use strict mode (no auto-fixes)
make ci
This will:
- Run strict linting (fails on any issues)
- Run type checking with mypy
- Run full test suite with coverage
IDE Integration
VSCode
Install these extensions:
- Ruff (charliermarsh.ruff)
- Python (ms-python.python)
Add to .vscode/settings.json:
{
"[python]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.codeActionsOnSave": {
"source.fixAll": true,
"source.organizeImports": true
}
},
"ruff.enable": true,
"ruff.lint.enable": true
}
PyCharm
- Go to Settings → Tools → External Tools
- Add Ruff:
- Program:
python - Arguments:
-m ruff check $FilePath$ --fix - Working directory:
$ProjectFileDir$
- Program:
Common Issues
Import Order
Ruff automatically organizes imports into sections:
- Future imports
- Standard library
- Third-party packages
- First-party packages (app, models, middleware, tasks, storage, scripts)
- Local folder imports
Line Length
Default line length is 88 characters (black's default). Long lines are automatically wrapped.
Type Hints
While not strictly enforced, adding type hints improves code quality:
# Good
def get_user(user_id: int) -> User:
return db.query(User).get(user_id)
# Better with Optional
from typing import Optional
def get_user(user_id: int) -> Optional[User]:
return db.query(User).get(user_id)
Ignoring Rules
Per-File Ignores
Configure in pyproject.toml:
[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"] # Allow unused imports
"tests/**/*.py" = ["S101"] # Allow assert statements
Inline Ignores
Use # noqa comments sparingly:
# Ignore specific rule
example = lambda x: x + 1 # noqa: E731
# Ignore all rules for line
long_url = "https://..." # noqa
Best Practices
- Run
make checkbefore every commit - Let Ruff auto-fix issues - don't fight the formatter
- Add type hints for better code quality
- Keep test coverage above 80%
- Use meaningful variable names that pass naming checks
- Avoid broad exception catches - be specific
- Use pathlib instead of os.path when possible
- Keep functions focused - if it's complex, break it down
Migration from Old Tools
If you were using black, isort, or flake8 before:
| Old Tool | New Tool | Notes |
|---|---|---|
| black | ruff format | 100% compatible |
| isort | ruff check --select I | Built into Ruff linting |
| flake8 | ruff check | Faster, more comprehensive |
| pyupgrade | ruff check --select UP | Built into Ruff |
All configurations have been migrated to pyproject.toml.
Performance
Ruff is 10-100x faster than the tools it replaces:
- black: ~2s → ruff: ~0.1s
- isort: ~3s → ruff: ~0.1s
- flake8: ~10s → ruff: ~0.2s
This means faster CI/CD pipelines and better developer experience.