Contributing
We welcome contributions to Planet Ruler! This guide will help you get started with developing, testing, and submitting improvements.
Getting Started
Development Setup
Fork and clone the repository:
git clone https://github.com/yourusername/planet-ruler.git cd planet-ruler
Create a virtual environment:
python -m venv planet-ruler-dev source planet-ruler-dev/bin/activate # On Windows: planet-ruler-dev\Scripts\activate
Install in development mode:
pip install -e ".[dev]"
Install pre-commit hooks:
pre-commit install
Development Dependencies
The development environment includes:
pip install -e ".[dev]"
This installs:
Testing: pytest, pytest-cov, pytest-benchmark, hypothesis
Code quality: black, flake8, mypy, pre-commit
Documentation: sphinx, sphinx-rtd-theme, myst-parser
Optional: segment-anything, torch (for advanced features)
Code Standards
Code Style
Planet Ruler follows PEP 8 with these specifications:
Line length: 127 characters (configured in pyproject.toml)
Code formatter: Black
Import sorting: isort (integrated with Black)
Type hints: Required for all public functions
Docstrings: Google/NumPy style for all public APIs
Format your code:
# Format all code
black planet_ruler/ tests/
# Check formatting
black --check planet_ruler/ tests/
Type Checking
Use type hints and run mypy for type checking:
# Run type checking
mypy planet_ruler/
# Type check specific file
mypy planet_ruler/geometry.py
Type hint examples:
from typing import Optional, Union, List, Dict
import numpy as np
def horizon_distance(r: float, h: float) -> float:
"""Calculate horizon distance with type hints."""
return math.sqrt(2 * r * h + h * h)
def load_config(config_path: str) -> Dict[str, Union[float, List[str]]]:
"""Load configuration with complex return type."""
# Implementation
Docstring Style
Use Google-style docstrings with mathematical notation support:
def limb_camera_angle(r: float, h: float) -> float:
"""
Calculate limb angle as seen from camera.
The limb angle is determined by the geometry of observation
from altitude h above a spherical body of radius r.
Args:
r (float): Planetary radius in meters.
h (float): Observation altitude above surface in meters.
Returns:
float: Limb angle in radians.
Raises:
ValueError: If r or h are negative.
Note:
The calculation assumes a spherical planet and uses the formula:
.. math::
\\theta = \\arcsin\\left(\\frac{r}{r + h}\\right)
Example:
Calculate Earth's limb angle from ISS altitude:
>>> angle = limb_camera_angle(6371000, 418000)
>>> print(f"Limb angle: {angle:.3f} rad")
Limb angle: 1.354 rad
"""
Testing Guidelines
Test Categories
Write tests in the appropriate category:
Unit tests: Test individual functions in isolation
Integration tests: Test complete workflows with real data
Property-based tests: Test mathematical properties with Hypothesis
Benchmarks: Performance tests for critical functions
Test Writing
File organization:
tests/
├── test_geometry.py # Unit tests for geometry module
├── test_integration_*.py # Integration tests
└── test_benchmarks.py # Performance benchmarks
Test function naming:
def test_function_name_condition_expected_result():
"""Test that function behaves correctly under specific conditions."""
Example unit test:
import pytest
import planet_ruler.geometry as geom
def test_horizon_distance_earth_iss_returns_expected_value():
"""Test horizon distance calculation for Earth from ISS altitude."""
result = geom.horizon_distance(r=6371000, h=418000)
expected = 2290704.6 # Pre-calculated value
assert abs(result - expected) < 1.0, f"Expected {expected}, got {result}"
def test_horizon_distance_raises_on_negative_radius():
"""Test horizon distance raises ValueError for negative radius."""
with pytest.raises(ValueError, match="radius must be positive"):
geom.horizon_distance(r=-1000, h=418000)
Integration test example:
def test_complete_earth_workflow_produces_reasonable_radius():
"""Test complete Earth analysis workflow."""
observation = obs.LimbObservation("demo/earth.jpg", "config/earth_iss_1.yaml")
observation.detect_limb(method="segmentation")
observation.fit_limb()
radius_result = calculate_parameter_uncertainty(observation, "r", scale_factor=1000)
assert 6300 < radius_result["value"] < 6400, "Earth radius should be ~6371 km"
Property-based test example:
from hypothesis import given, strategies as st
@given(
radius=st.floats(min_value=1e5, max_value=1e8),
altitude=st.floats(min_value=1e3, max_value=1e9)
)
def test_horizon_distance_monotonic_in_altitude(radius, altitude):
"""Test that horizon distance increases with altitude."""
h1 = geom.horizon_distance(radius, altitude)
h2 = geom.horizon_distance(radius, altitude * 1.1)
assert h2 > h1, "Horizon distance should increase with altitude"
Running Tests
# Run all tests
pytest
# Run with coverage
pytest --cov=planet_ruler --cov-report=html
# Run specific test categories
pytest tests/test_geometry.py -v
pytest -m integration tests/ -v
pytest --benchmark-only tests/
Performance Considerations
When adding features that may impact performance:
Add benchmarks: Include performance tests for new functions
Profile changes: Use cProfile to identify bottlenecks
Memory awareness: Consider memory usage for large images
Document performance: Include timing expectations
def test_new_function_performance(benchmark):
"""Benchmark new function performance."""
test_data = setup_test_data()
result = benchmark(new_function, test_data)
assert result is not None
Pull Request Process
Before Submitting
Run the full test suite:
pytest --cov=planet_ruler --cov-report=term-missing
Check code formatting:
black --check planet_ruler/ tests/ flake8 planet_ruler/ tests/
Run type checking:
mypy planet_ruler/Update documentation if needed:
cd docs/ make html
Pull Request Guidelines
PR Title Format:
feat: add uncertainty calculation functionsfix: resolve numpy compatibility in fit.pydocs: update installation instructions for segmentationtest: add integration tests for Saturn scenarioperf: optimize limb_arc calculation for large images
PR Description Template:
## Description
Brief description of changes and motivation.
## Type of Change
- [ ] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that changes existing API)
- [ ] Documentation update
- [ ] Performance improvement
## Testing
- [ ] New tests added for changed functionality
- [ ] All existing tests pass
- [ ] Integration tests pass with real data
- [ ] Performance benchmarks added if applicable
## Checklist
- [ ] Code follows project style guidelines
- [ ] Self-review completed
- [ ] Documentation updated
- [ ] Type hints added for public APIs
Code Review Process
All pull requests require:
Automated checks passing: CI/CD pipeline must be green
Code review: At least one maintainer approval
Documentation: Updates for user-facing changes
Tests: Adequate test coverage for new functionality
Performance: No significant performance regressions
Common Review Feedback
Add type hints: All public functions need type annotations
Improve docstrings: Include examples and parameter descriptions
Add error handling: Validate inputs and provide clear error messages
Consider edge cases: Test boundary conditions and error scenarios
Performance impact: Profile and benchmark performance-critical changes
Feature Development
Adding New Functions
Plan the API: Consider function signature, parameters, return types
Write tests first: TDD approach with failing tests
Implement functionality: Focus on correctness before optimization
Add documentation: Docstrings, examples, and user guide updates
Performance testing: Add benchmarks for critical functions
Example new function development:
# 1. Write failing test
def test_new_calculation_returns_expected_value():
result = geom.new_calculation(param1=100, param2=200)
assert abs(result - 141.42) < 0.01
# 2. Implement function
def new_calculation(param1: float, param2: float) -> float:
"""
Calculate new geometric property.
Args:
param1: First parameter in meters.
param2: Second parameter in meters.
Returns:
Calculated value in appropriate units.
"""
return math.sqrt(param1**2 + param2**2)
# 3. Add benchmark
def test_new_calculation_performance(benchmark):
result = benchmark(geom.new_calculation, 100.0, 200.0)
assert result > 0
Adding New Modules
For substantial new functionality:
Create module file:
planet_ruler/new_module.pyAdd to __init__.py: Import key functions/classes
Create test file:
tests/test_new_module.pyAdd documentation: Module-level docstring and API docs
Update dependencies: Add to pyproject.toml if needed
Documentation
Documentation Structure
Planet Ruler uses Sphinx with reStructuredText:
docs/
├── conf.py # Sphinx configuration
├── index.rst # Main documentation page
├── installation.rst # Installation guide
├── tutorials.rst # Step-by-step tutorials
├── examples.rst # Real-world examples
├── api.rst # API reference
├── modules.rst # Auto-generated module docs
├── testing.rst # Testing documentation
├── benchmarks.rst # Performance documentation
└── contributing.rst # This file
Building Documentation
# Install documentation dependencies
pip install -r docs/requirements.txt
# Build HTML documentation
cd docs/
make html
# Open in browser
open _build/html/index.html
Documentation Requirements
All public APIs: Must have comprehensive docstrings
Examples: Include usage examples in docstrings
Mathematical notation: Use LaTeX for mathematical expressions
Cross-references: Link between related functions and classes
def example_function(param: float) -> float:
"""
Example function demonstrating documentation standards.
This function calculates something important using the formula:
.. math::
result = \\sqrt{\\frac{param^2}{2}}
Args:
param: Input parameter in meters.
Returns:
float: Calculated result in meters.
See Also:
:func:`related_function`: For related calculations.
:class:`SomeClass`: For object-oriented approach.
Example:
Basic usage:
>>> result = example_function(10.0)
>>> print(f"Result: {result:.2f}")
Result: 7.07
"""
Release Process
Version Numbering
Planet Ruler uses semantic versioning (SemVer):
Major (1.0.0): Breaking API changes
Minor (1.1.0): New functionality, backward compatible
Patch (1.0.1): Bug fixes, backward compatible
Release Checklist
Before releasing a new version:
Update version number: In pyproject.toml and __init__.py
Update changelog: Document all changes since last release
Run full test suite: Ensure all tests pass across Python versions
Build documentation: Verify docs build without warnings
Performance check: Ensure no significant regressions
Tag release: Create Git tag with version number
# Create release tag
git tag -a v1.1.0 -m "Release version 1.1.0"
git push origin v1.1.0
Communication Guidelines
Issue Reporting
When reporting bugs or requesting features:
Search existing issues: Avoid duplicates
Use issue templates: Provide required information
Include minimal examples: Reproducible test cases
Provide context: Operating system, Python version, dependencies
Bug report template:
**Bug Description**
Clear description of the bug.
**To Reproduce**
Steps to reproduce the behavior:
1. Load image '...'
2. Call function '...'
3. See error
**Expected Behavior**
What you expected to happen.
**Environment**
- OS: [e.g. Ubuntu 20.04]
- Python: [e.g. 3.9.7]
- Planet Ruler: [e.g. 1.0.0]
Discussion Etiquette
Be respectful: Constructive feedback and professional communication
Stay on topic: Keep discussions focused on the issue at hand
Provide context: Include relevant background information
Be patient: Maintainers contribute in their spare time
Recognition
Contributors are recognized in:
CONTRIBUTORS.md: List of all contributors
Release notes: Acknowledgment of major contributions
Documentation: Author attribution for significant additions
Types of Contributions
All contributions are valued:
Code: Bug fixes, new features, optimizations
Documentation: Tutorials, examples, API improvements
Testing: Test cases, integration tests, benchmarks
Bug reports: Well-documented issues help improve quality
Feature requests: Ideas for improvements and new functionality
Getting Help
If you need help contributing:
GitHub Issues: Ask questions about development
Documentation: Review existing guides and examples
Code Review: Learn from feedback on pull requests
Community: Connect with other contributors
Development Tools
Recommended IDE Setup
VS Code configuration (.vscode/settings.json):
{
"python.linting.enabled": true,
"python.linting.flake8Enabled": true,
"python.formatting.provider": "black",
"python.testing.pytestEnabled": true,
"python.testing.pytestArgs": ["tests/"],
"python.linting.mypyEnabled": true
}
PyCharm configuration:
Enable Black formatter in File → Settings → Tools → External Tools
Configure pytest as test runner in Run/Debug Configurations
Enable type checking with mypy plugin
Debugging Tips
# Add breakpoints for debugging
import pdb; pdb.set_trace()
# Debug tests
pytest tests/test_geometry.py::test_specific_function --pdb
# Profile performance
python -m cProfile -o profile.stats script.py
Git Workflow
# Create feature branch
git checkout -b feature/new-uncertainty-function
# Make changes and commit
git add -A
git commit -m "feat: add uncertainty calculation with multiple methods"
# Push and create PR
git push origin feature/new-uncertainty-function
Thank you for contributing to Planet Ruler! Your efforts help make planetary radius measurements accessible to researchers, educators, and curious minds worldwide.