Replace all ~1,086 occurrences of Wizamart/wizamart/WIZAMART/WizaMart with Orion/orion/ORION across 184 files. This includes database identifiers, email addresses, domain references, R2 bucket names, DNS prefixes, encryption salt, Celery app name, config defaults, Docker configs, CI configs, documentation, seed data, and templates. Renames homepage-wizamart.html template to homepage-orion.html. Fixes duplicate file_pattern key in api.yaml architecture rule. 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 Orion 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.