To prevent CI/CD failures, ALWAYS run these checks locally before committing:
# Quick check using script (RECOMMENDED)
.github/scripts/pre-commit-checks.sh
# OR manually run each check:
# 1. Linting
ruff check src/ tests/ --fix
# 2. Type checking (CRITICAL for Python 3.9 compatibility)
mypy src/
# 3. Fast tests (excludes slow integration tests)
pytest --cov --cov-report=xml
# 4. Optional: Run slow tests locally
pytest -m slow -vWhy this is critical:
- CI tests on Python 3.9, 3.10, 3.11, and 3.12
- Type hints that work on 3.10+ may fail on 3.9
- Running checks locally catches these issues before CI
Install pre-commit hooks to automatically run checks before each commit:
pip install pre-commit
pre-commit installThis will automatically run:
- Ruff linting and formatting
- Mypy type checking
- Standard code quality checks
- Located in
tests/test_*.py - Run automatically in CI/CD
- Should complete in < 10 seconds total
- Use mocks/fixtures for external dependencies
- Marked with
@pytest.mark.slow - Excluded from CI/CD by default
- Run locally with:
pytest -m slow - Include: full-year simulations, API calls, heavy computations
- Located in
tests/integration/test_pr*.py - These are scripts, not pytest tests
- Run manually:
python tests/integration/test_pr5.py - NOT included in pytest discovery (excluded via
--ignore=tests/integration)
Symptom: Mypy passes on Python 3.10+ but fails on Python 3.9
Cause: Using lowercase type hints (list[], dict[]) not supported in Python 3.9
Solution: Always use capitalized types from typing module
# ❌ BAD - Fails on Python 3.9
from typing import Optional
def func(data: list[str]) -> dict[str, int]:
pass
# ✅ GOOD - Works on Python 3.9+
from typing import Dict, List, Optional
def func(data: List[str]) -> Dict[str, int]:
passAlways use:
List[T]notlist[T]Dict[K, V]notdict[K, V]Tuple[T, ...]nottuple[T, ...]Set[T]notset[T]
Symptom: Pytest hangs for 13+ minutes
Cause: Slow tests (full-year simulations) running in CI
Solution: Mark with @pytest.mark.slow
@pytest.mark.slow
class TestAnnualSimulation:
def test_full_year(self):
result = simulate_annual(...) # Takes 30+ secondsSymptom: error: Cannot find implementation or library stub
Cause: Missing type stubs for third-party libraries
Solution: Configure mypy.ini to ignore these
Already configured in mypy.ini:
[mypy]
ignore_missing_imports = True
disable_error_code = import-untyped,override,assignment,attr-definedSymptom: Pytest tries to import tests/integration/test_pr*.py and hangs
Cause: These are scripts, not pytest tests
Solution: Exclude via pyproject.toml
Already configured:
[tool.pytest.ini_options]
addopts = ["--ignore=tests/integration"]Symptom: F841, B904, C901, etc.
Cause: Code quality issues
Solution: Run ruff check --fix to auto-fix most issues
Common fixes:
- F841 (unused variable): Remove or use underscore prefix
- B904 (exception without
from): Useraise ... from err - C901 (complexity): Split function into smaller helpers
cd /path/to/pvsolarsim
source .venv/bin/activate # If using virtual environment- Write new feature
- Add/update tests
- Update documentation
# Auto-fix linting
ruff check src/ tests/ --fix
# Type check
mypy src/
# Run fast tests
pytest
# Optional: Run specific test
pytest tests/test_mymodule.py -vgit add .
git commit -m "Your message"
# Pre-commit hooks run automaticallygit push origin your-branch
# CI/CD runs automaticallyThe GitHub Actions workflow (.github/workflows/test.yml) runs:
- Install dependencies -
pip install -e ".[dev]" - Lint with ruff -
ruff check src/ tests/ - Type check with mypy -
mypy src/ - Test with pytest -
pytest --cov --cov-report=xml - Upload coverage - To Codecov
Important: Slow tests are excluded by default via -m "not slow" in pyproject.toml.
Available pytest markers:
@pytest.mark.slow- Slow tests (excluded from CI)
Run specific markers:
pytest -m slow # Run only slow tests
pytest -m "not slow" # Run all except slow tests (default)- Target: >75% overall coverage
- Weather modules: >85% coverage
- Integration tests: Not counted in coverage (excluded)
Check coverage:
pytest --cov --cov-report=html
open htmlcov/index.html- Check the logs - GitHub Actions → Failed job → View logs
- Reproduce locally:
pytest -v # Verbose output pytest tests/test_failing.py::test_name -vv # Very verbose
- Fix the issue
- Verify fix:
ruff check src/ tests/ mypy src/ pytest
- Commit and push
pyproject.toml- pytest, black, coverage configmypy.ini- mypy type checking config.pre-commit-config.yaml- pre-commit hooks.github/workflows/test.yml- CI/CD pipeline
# Full pre-push checklist
ruff check src/ tests/ --fix && \
mypy src/ && \
pytest && \
echo "✅ Ready to push!"
# Run slow tests locally (optional)
pytest -m slow -v
# Run integration scripts
python tests/integration/test_pr5.py
# Run with coverage
pytest --cov --cov-report=html
# Run specific test file
pytest tests/test_weather_base.py -vQ: Tests hang locally
A: You're probably running slow tests. Use pytest -m "not slow" or check pyproject.toml config.
Q: CI fails but tests pass locally
A: Check Python version differences. CI tests on 3.9, 3.10, 3.11, 3.12.
Q: Mypy errors in CI but not locally
A: Check mypy.ini is committed and mypy version matches.
Q: How do I add a new slow test?
A: Add @pytest.mark.slow decorator:
@pytest.mark.slow
def test_expensive_operation():
result = simulate_annual(...) # Takes long timeQ: How do I test on Python 3.9 specifically?
A: If you have Python 3.9 installed:
python3.9 -m venv .venv39
source .venv39/bin/activate
pip install -e ".[dev]"
mypy src/
pytestLast Updated: 2025-12-28
Maintainer: Development Team