Stop the 3 AM outage. This repo teaches every pattern in the Error-Handling Standards through runnable code, deliberate failure examples, and a full test suite you can study and extend.
| # | Module | Concept |
|---|---|---|
| 1 | Exception Policy | Never bare except — canonical exceptions by operation |
| 2 | Retry & Backoff | Exponential backoff with jitter, retryable vs. non-retryable |
| 3 | Logging Standards | Level discipline, structured logs, what never goes in logs |
| 4 | Secrets Management | .env, environment variables, AWS Secrets Manager pattern |
| 5 | Graceful Degradation | Partial success, fallback values, circuit breaker |
| 6 | Failure Injection Testing | Mock side-effects, assert retry counts, log assertions |
# 1. Clone
git clone https://github.com/iamwaqarjaved/python-error-handling-tutorial.git
cd python-error-handling-tutorial
# 2. Create virtual environment
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
# 3. Install dependencies
pip install -r requirements.txt
# 4. Copy env template
cp .env.example .env
# 5. Run all tests
pytest --tb=short -v
# 6. Try a live example
python examples/run_all_demos.pypython-error-handling-tutorial/
│
├── README.md ← You are here
├── docs/
│ ├── STANDARDS.md ← The full team standards document
│ ├── 01_exception_policy.md
│ ├── 02_retry_backoff.md
│ ├── 03_logging.md
│ ├── 04_secrets.md
│ ├── 05_graceful_degradation.md
│ └── 06_testing.md
│
├── src/
│ ├── exceptions/
│ │ ├── __init__.py
│ │ ├── domain.py ← Custom domain exceptions
│ │ ├── io_handlers.py ← File I/O exception patterns
│ │ ├── network_handlers.py ← HTTP exception patterns
│ │ └── parse_handlers.py ← JSON/XML/CSV exception patterns
│ │
│ ├── retry/
│ │ ├── __init__.py
│ │ ├── backoff.py ← Hand-rolled decorator + tenacity usage
│ │ └── policies.py ← Retryable exception registry
│ │
│ ├── logging/
│ │ ├── __init__.py
│ │ ├── config.py ← Logging configuration
│ │ └── safe_logger.py ← Logger wrapper that scrubs secrets
│ │
│ ├── secrets/
│ │ ├── __init__.py
│ │ └── settings.py ← pydantic-settings config loader
│ │
│ ├── graceful_degradation/
│ │ ├── __init__.py
│ │ ├── partial_result.py ← Partial-success response pattern
│ │ └── fallback.py ← Cache-then-default pattern
│ │
│ └── circuit_breaker/
│ ├── __init__.py
│ └── breaker.py ← Thread-safe circuit breaker
│
├── tests/
│ ├── conftest.py
│ ├── test_exceptions/
│ │ ├── test_io_handlers.py
│ │ ├── test_network_handlers.py
│ │ └── test_parse_handlers.py
│ ├── test_retry/
│ │ └── test_backoff.py
│ ├── test_logging/
│ │ └── test_safe_logger.py
│ ├── test_circuit_breaker/
│ │ └── test_breaker.py
│ └── test_graceful_degradation/
│ └── test_patterns.py
│
├── examples/
│ ├── run_all_demos.py ← One script to demo everything
│ ├── demo_retry.py
│ ├── demo_circuit_breaker.py
│ └── demo_partial_success.py
│
├── .env.example
├── .gitignore
├── pyproject.toml
├── requirements.txt
└── requirements-dev.txt
# All tests
pytest
# One module at a time
pytest tests/test_retry/ -v
pytest tests/test_circuit_breaker/ -v
# With coverage report
pytest --cov=src --cov-report=term-missing
# Watch mode (requires pytest-watch)
ptw tests/If you are new to the topic: Read each docs/0N_*.md file in order, then open the matching src/ module and read it top-to-bottom. Then run the corresponding test file with pytest tests/test_<module>/ -v -s so you see what each test is asserting.
If you are reviewing a PR: Open docs/STANDARDS.md → scroll to the 10-question checklist at the end → work through it against the diff.
If you are onboarding a new engineer: Have them work through examples/run_all_demos.py, break one of the patterns intentionally (e.g., change except requests.exceptions.Timeout to except Exception), re-run the tests, and observe what fails.
A quick link for reviewers — the full version with rationale is in docs/STANDARDS.md.
- No bare
exceptorexcept Exceptionwithout traceback + re-raise - Caught exceptions are specific to the operation type
- Exception chaining used (
raise ... from exc) on type conversion - Every external call has an explicit timeout
- Retry policy separates transient from permanent errors
- Exponential backoff with jitter and a max retry count
- Log levels match the severity table (Section 4)
- No secrets, credentials, or PII in any log statement
- Zero hardcoded secrets in source (including tests and fixtures)
- Every new
exceptblock has at least one test that triggers it
- Fork the repo and create a feature branch.
- Add your pattern in
src/, a test intests/, and a doc note in the relevantdocs/0N_*.md. - Run
pytestand confirm all tests pass. - Open a PR — the checklist above is your self-review guide.
MIT — use freely, attribute kindly.