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.10 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.10+ 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.
Executable docs are tests. Every PR also runs the docstring examples in src/, the tests in tests/docs/, and the code blocks in docs/. A behaviour change must update the affected examples in the same PR. Check them locally with make test-doctest (with REDIS_URL unset, see #225), uv run pytest tests/docs/ and make test-docs-quick.
- Aim for >85% coverage for new code
- All public APIs must have tests
- Edge cases and error conditions must be tested
- CI enforces a total-coverage floor inside pytest on every PR
(
--cov-fail-under, value in.github/workflows/ci.yml), independent of the Codecov upload — a PR whose combined coverage falls below it fails theTestsjob.make test-covenforces the same floor locally. Codecov still reports the finer-grained new-code (patch) coverage on top of this.
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
This project uses documentation-only code ownership routing. GitHub's .github/CODEOWNERS file is not enforced in this repository (tracked internally as LAB-1151). The guidance below documents which paths benefit from security and maintainer review:
Security-sensitive paths — consider requesting review from maintainers:
/rust/— memory safety, FFI boundaries, cryptography/src/cachekit/serializers/and/src/cachekit/reliability/— serialization and fault tolerance/.github/workflows/,/pyproject.toml,/rust/Cargo.toml,/.pre-commit-config.yaml— supply chain configuration/tests/security/,/tests/fuzz/,/SECURITY.md— security documentation and testing
This is a single-maintainer org using documentation instead of enforcement. GitHub's review rulesets on this repo require zero code-owner approvals, so the path guidance above is a routing suggestion for pull requests, not a GitHub-enforced gate.
-
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 perf # Full battery: env fingerprint + timer calibration, serializer benchmarks, GIL scaling
make perf-compare # Regression gate: fail on >10% median serializer regression vs baseline
make benchmark # Serializer benchmarks only + save a local baseline
make benchmark-compare # Re-run and fail on >10% median regression vs that baseline
make benchmark-gil # Serializer thread-scaling under the current interpreter (GIL)All perf tests live in one folder, tests/performance/; the pytest-benchmark ones are
selected by --benchmark-only and skipped elsewhere via the --benchmark-skip default.
Baselines are written to .benchmarks/ (gitignored, per-machine), so regression
comparison is a local developer tool — wall-clock benchmarks deliberately do not gate
CI. make perf first prints a system fingerprint + environment verdict and
self-calibrates the timer, so numbers come with the context needed to trust them.
make 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.