Thank you for your interest in contributing to cachekit! This document provides guidelines and instructions for contributing to the project.
This project follows a standard code of conduct. Please be respectful and professional in all interactions.
- Python 3.9 or higher
- Rust 1.80 or higher
- Redis 5.0 or higher (for testing)
- uv (recommended for dependency management)
- pytest-redis 3.0+ (automatically installed with
uv sync)
-
Clone the repository
git clone https://github.com/cachekit-io/cachekit-py.git cd cachekit-py -
Install dependencies
uv sync && make install -
Run tests to verify setup
make test
- Check existing issues to avoid duplicate work
- For major changes, open an issue first to discuss the approach
- Fork the repository and create a feature branch
-
Create a feature branch
git checkout -b feature/your-feature-name
-
Make your changes
- Write clear, focused commits
- Follow the code style guidelines (see below)
- Add tests for new functionality
- Update documentation as needed
-
Run quality checks
make quick-check # format + lint + critical tests -
Run full test suite
make check # format + lint + type-check + all tests -
Commit your changes
git add <files> git commit -m "feat: add new feature"
- Line length: 129 characters maximum
- Formatter: Ruff (runs automatically with
make format) - Linter: Ruff (runs automatically with
make lint) - Type checker: basedpyright (standard mode, zero errors enforced)
- Type hints: Required for all public APIs
- Python 3.9+ compatibility: Use
from __future__ import annotationsfor modern union syntax - Docstrings: Google style for public functions/classes
- Imports: Absolute imports only
Example:
from __future__ import annotations
from cachekit import cache
@cache
def example_function(param: str) -> dict[str, str]:
"""Brief description of what this does.
Args:
param: Description of parameter
Returns:
Description of return value
"""
return {"result": param}- Formatter: rustfmt (standard settings)
- Linter: clippy
- Documentation: Required for public APIs
Follow KISS, DRY, YAGNI, SOLID:
- Keep It Simple - Prefer simple solutions over clever ones
- Don't Repeat Yourself - Extract common patterns (2+ uses minimum)
- You Aren't Gonna Need It - Build only what's needed now
- Single Responsibility - Each component has one clear purpose
Prefer:
- Guard clauses over deep nesting
- Early returns for readability
- Explicit over implicit
- Composition over inheritance
- Critical tests: Essential functionality in
tests/critical/(must always pass) - Unit tests: Fast, isolated tests in
tests/unit/ - Integration tests: Tests requiring Redis in
tests/integration/ - Performance tests: Benchmarks in
tests/performance/
Test markers:
import pytest
@pytest.mark.critical
def test_cache_basic_functionality():
"""Critical test - must pass for library to be usable"""
pass
@pytest.mark.unit
def test_cache_configuration():
"""Test cache configuration validation"""
pass
@pytest.mark.integration
def test_redis_connection():
"""Test actual Redis connection (requires Redis running)"""
passRedis Test Isolation: All tests requiring Redis must use pytest-redis for proper isolation:
from ..utils.redis_test_helpers import RedisIsolationMixin
class TestMyCacheFeature(RedisIsolationMixin):
def test_feature(self):
# Test automatically gets isolated Redis instance
pass# All tests
make test
# Critical tests only (fastest, must pass before commit)
make test-critical
# Specific test file
uv run pytest tests/unit/test_decorators.py -v
# Specific test function
uv run pytest tests/unit/test_decorators.py::test_cache_basic -v
# Run by marker
uv run pytest -m critical -v # Critical tests
uv run pytest -m unit -v # Unit tests
uv run pytest -m integration -v # Integration tests
# With coverage
make test-covImportant: pytest-redis is required for all Redis-dependent tests. If tests fail with "pytest-redis is required", run uv sync to install dependencies.
- Aim for >85% coverage for new code
- All public APIs must have tests
- Edge cases and error conditions must be tested
Rust Test Coverage:
- ByteStorage module: 82% coverage (measured via LLVM source-based coverage)
- Encryption modules: Integration tests only (PyO3 cdylib limitation prevents coverage measurement)
- All Rust functionality validated via Python integration tests in
tests/critical/ - PyO3's cdylib architecture prevents LLVM coverage tracking across module boundaries
- This is a known limitation, not a code quality issue
-
Ensure all checks pass
make check # Must pass before PR -
Update documentation
- Update README.md if adding user-facing features
- Add/update docstrings
- Update CHANGELOG.md (if exists)
-
Write clear PR description
- What problem does this solve?
- What changes were made?
- How was it tested?
- Any breaking changes?
-
PR Title Format
feat: add distributed cache warming fix: resolve connection pool leak docs: update getting started guide test: add integration tests for encryption refactor: simplify serializer interface -
Review process
- Address reviewer feedback
- Keep PR focused and reasonably sized
- Rebase on main if needed
make build # Standard build
make build-pgo # Profile-Guided Optimization (5-8% faster)make benchmark-quick # Quick performance checkmake format # Auto-format Python and Rustmake type-check # Run basedpyright type checker (zero errors)cachekit/
├── src/cachekit/ # Python package
│ ├── decorators/ # Cache decorators
│ ├── serializers/ # Serialization engines
│ ├── reliability/ # Circuit breaker, etc.
│ └── ...
├── rust/ # Rust extensions
│ └── src/
│ ├── serialization/
│ ├── compression.rs
│ └── ...
├── tests/ # Test suite
│ ├── critical/ # Critical path tests
│ ├── unit/
│ ├── integration/
│ └── performance/
├── docs/ # Documentation
└── pyproject.toml # Project metadata
- Questions: Open a GitHub Discussion
- Bug Reports: Open an issue
- Security Issues: See SECURITY.md
By contributing to cachekit, you agree that your contributions will be licensed under the MIT License.