# 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) - **mypy** - Static type checker - **pytest** - Testing framework with coverage reporting All tools are configured in `pyproject.toml` for consistency and ease of use. ## Quick Start ```bash # 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 ``` ## 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 ```bash # 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`: ```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 ```bash # Run type checking python -m mypy . # Or as part of lint make lint ``` ### Configuration mypy is configured in `pyproject.toml`: ```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: ```bash # 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`: ```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 ```bash # 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](architecture-rules.md) for detailed rule documentation. ## Pre-Commit Workflow Before committing code: ```bash # 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.database - `models/__init__.py` - Exports Base for Alembic - `models/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:** ```bash make verify-imports ``` If this fails, imports have been removed and must be restored. ## CI/CD Integration For continuous integration: ```bash # Use strict mode (no auto-fixes) make ci ``` This will: 1. Run strict linting (fails on any issues) 2. Run type checking with mypy 3. Run full test suite with coverage ## IDE Integration ### VSCode Install these extensions: - Ruff (charliermarsh.ruff) - Python (ms-python.python) Add to `.vscode/settings.json`: ```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 1. Go to Settings → Tools → External Tools 2. Add Ruff: - Program: `python` - Arguments: `-m ruff check $FilePath$ --fix` - Working directory: `$ProjectFileDir$` ## Common Issues ### Import Order Ruff automatically organizes imports into sections: 1. Future imports 2. Standard library 3. Third-party packages 4. First-party packages (app, models, middleware, tasks, storage, scripts) 5. 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: ```python # 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`: ```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: ```python # Ignore specific rule example = lambda x: x + 1 # noqa: E731 # Ignore all rules for line long_url = "https://..." # noqa ``` ## Best Practices 1. **Run `make check` before every commit** 2. **Let Ruff auto-fix issues** - don't fight the formatter 3. **Add type hints** for better code quality 4. **Keep test coverage above 80%** 5. **Use meaningful variable names** that pass naming checks 6. **Avoid broad exception catches** - be specific 7. **Use pathlib** instead of os.path when possible 8. **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. ## Resources - [Ruff Documentation](https://docs.astral.sh/ruff/) - [Ruff Rules](https://docs.astral.sh/ruff/rules/) - [mypy Documentation](https://mypy.readthedocs.io/) - [pytest Documentation](https://docs.pytest.org/)