Contributing Guide¶
Thank you for your interest in contributing to protoruf! This guide covers setup, development workflow, and best practices.
Development Setup¶
Prerequisites¶
- Python 3.12+
- Rust (1.70+) — Install via rustup
- uv — Fast Python package manager
1. Clone the Repository¶
2. Install Dependencies¶
This installs all development dependencies including maturin, pytest, and mypy.
3. Build the Extension¶
This compiles the Rust code and installs the Python package in development mode.
Project Structure¶
protoruf/
├── python/protoruf/ # Python package
│ ├── __init__.py # Public API wrappers
│ ├── _protoruf.pyi # Type stubs for Rust extension
│ ├── compiler.py # Proto compilation utilities
│ └── py.typed # PEP 561 type marker
├── src/ # Rust source code
│ ├── lib.rs # Python bindings (PyO3)
│ └── core.rs # Core logic (pure Rust)
├── tests/ # Python test suite
│ ├── proto/ # Test proto files
│ ├── test_models.py # Pydantic test models
│ └── test_rust_json_protobuf.py
├── examples/ # Usage examples
└── docs/ # Documentation
Development Workflow¶
Make Changes¶
- Edit Rust code in
src/ - Edit Python code in
python/protoruf/ - Rebuild the extension:
Run Tests¶
Python Tests¶
Rust Tests¶
All Tests¶
Type Checking¶
Pre-commit Checklist¶
Before committing, ensure all checks pass:
# 1. Python tests
uv run pytest tests/ -v
# 2. Rust tests
cargo test --lib
# 3. Type checking
uv run mypy python/protoruf/ --ignore-missing-imports
# 4. Build verification
uv run maturin develop
Adding Features¶
Rust Changes¶
- Add logic to
src/core.rs(pure Rust) - Expose via Python bindings in
src/lib.rs - Update type stubs in
python/protoruf/_protoruf.pyi - Write tests in both Rust and Python
Python Changes¶
- Update wrappers in
python/protoruf/__init__.py - Update type stubs if signatures change
- Add tests to
tests/
Documentation¶
Update relevant docs in docs/:
- API changes → docs/api/reference.md
- New features → docs/guide/
- Usage examples → docs/examples/
Testing Guidelines¶
Test Proto Files¶
Test proto files are in tests/proto/. When adding new proto features:
- Update or create a
.protofile - Add corresponding Python test
- Test both conversion directions (JSON ↔ Protobuf)
Example Test¶
def test_nested_message():
descriptor = compile_proto("tests/proto/message.proto")
json_data = '''
{
"id": "123",
"nested": {
"field1": "value1",
"field2": 42
}
}
'''
protobuf_bytes = json_to_protobuf(json_data, descriptor, "message.Message")
result = protobuf_to_json(protobuf_bytes, descriptor, "message.Message")
# Verify round-trip
assert json.loads(json_data) == json.loads(result)
Code Style¶
Rust¶
- Follow standard Rust conventions (rustfmt)
- Use descriptive variable names
- Add comments for complex logic
- Write unit tests for core functions
Python¶
- Follow PEP 8
- Use type hints everywhere
- Keep functions focused and small
- Docstrings for all public functions
Building for Distribution¶
Build Wheels¶
This creates distributable wheels in target/wheels/.
Local Install¶
Debugging¶
Enable Logging¶
Add print statements in Python:
Rust Debugging¶
Use eprintln! for debugging Rust code:
Test Individual Functions¶
# Test specific Rust module
cargo test core
# Test specific Python module
uv run pytest tests/test_rust_json_protobuf.py -v
Submitting Changes¶
- Fork the repository
- Create a branch for your feature:
git checkout -b feature/my-feature - Make changes and ensure all tests pass
- Update documentation if applicable
- Commit with clear messages:
git commit -m "feat: add support for maps" - Push to your fork:
git push origin feature/my-feature - Open a Pull Request with a clear description of changes
Commit Message Convention¶
Use conventional commits:
feat:— New featurefix:— Bug fixdocs:— Documentation changestest:— Test additions or changesrefactor:— Code refactoringchore:— Maintenance tasks
Reporting Issues¶
When reporting bugs:
- Include Python version
- Include protoruf version
- Provide a minimal reproducible example
- Include error messages and stack traces
- Describe expected vs actual behavior
Performance Considerations¶
When making changes:
- Avoid unnecessary allocations in Rust
- Use
&strinstead ofStringwhen possible - Profile before optimizing
- Benchmark significant changes
License¶
By contributing, you agree that your contributions will be licensed under the MIT License.