Contributing
We welcome contributions to crabML! This document provides guidelines for contributing.
Development Setup
Clone the repository:
git clone https://github.com/yourusername/crabml.git
cd crabml
Install with development dependencies:
uv sync --all-extras --reinstall-package crabml-rust --group dev --group docs
Verify installation:
uv run pytest
Code Style
Python Code
We use ruff for linting and formatting:
# Format code
uv run ruff format src/ tests/
# Check for issues
uv run ruff check src/ tests/
Guidelines:
Follow PEP 8 style guide
Use Google-style docstrings
Maximum line length: 100 characters
Type hints for function signatures
Rust Code
Rust code should follow standard Rust conventions:
cd rust/
cargo fmt
cargo clippy
Running Tests
Run all tests:
uv run pytest -n 8
Run specific test file:
uv run pytest tests/test_api.py -v -n 8
Run tests with coverage:
uv run pytest --cov=crabml --cov-report=html
PAML validation tests (slower):
uv run pytest tests/test_paml_examples/ -v -n 4
Building Documentation
Build Sphinx documentation:
cd docs/
make html
View documentation:
open _build/html/index.html # macOS
xdg-open _build/html/index.html # Linux
Contributing Guidelines
Create an issue first to discuss major changes
Fork the repository and create a feature branch:
git checkout -b feature/your-feature-name
Make your changes following code style guidelines
Add tests for new functionality
Update documentation if needed
Run tests to ensure nothing breaks:
uv run pytest -n 8
Commit your changes with clear messages:
git add .
git commit -m "Add feature: brief description
More detailed explanation of changes, why they were needed,
and any relevant context."
Push to your fork and create a pull request
Pull Request Process
Ensure all tests pass
Update CHANGELOG.md with your changes
Update documentation if you changed the API
Fill out the PR template with:
Description of changes
Issue number (if applicable)
Testing performed
Screenshots (if UI changes)
Wait for review - maintainers will review and provide feedback
Code Review Checklist
Reviewers will check for:
☐ All tests pass
☐ Code follows style guidelines
☐ Documentation updated
☐ CHANGELOG.md updated
☐ No unnecessary dependencies added
☐ PAML validation tests still pass (for model changes)
☐ Performance hasn’t regressed
Reporting Bugs
Use GitHub Issues to report bugs. Include:
Environment information:
Python version
Rust version
Operating system
crabML version
Steps to reproduce the bug
Expected behavior
Actual behavior
Minimal example that demonstrates the issue
Error messages and stack traces
Example bug report:
**Bug**: M2a optimizer fails on large alignments
**Environment:**
- Python 3.11.5
- Rust 1.73.0
- Ubuntu 22.04
- crabML 0.2.0
**Steps to reproduce:**
1. Load alignment with 200 sequences
2. Run `optimize_model("M2a", align, tree)`
**Expected:** Should complete successfully
**Actual:** Crashes with "out of memory" error
**Error message:**
```
MemoryError: Unable to allocate array with shape (200, 1000, 10)
```
Feature Requests
We welcome feature requests! Please:
Check existing issues to avoid duplicates
Describe the feature in detail
Explain the use case and why it’s needed
Provide examples of how it would be used
Adding New Models
To add a new codon model:
Implement optimizer class in
src/crabml/optimize/
class MyModelOptimizer(BaseOptimizer):
def __init__(self, alignment, tree, **kwargs):
super().__init__(alignment, tree, **kwargs)
def optimize(self, **kwargs):
# Implement optimization logic
pass
Add parser function to
src/crabml/api.py
def _parse_mymodel_result(result_tuple, ...):
# Parse optimizer output into ModelResult
pass
Add to optimize_model() function
OPTIMIZER_MAP = {
...
"mymodel": MyModelOptimizer,
}
PARSER_MAP = {
...
"mymodel": _parse_mymodel_result,
}
Add tests in
tests/test_api.pyAdd PAML validation in
tests/test_paml_examples/Update documentation in
docs/user_guide/models.rst
Testing Guidelines
All code should be tested. We use pytest.
Test structure:
class TestMyFeature:
def test_basic_functionality(self):
"""Test basic use case."""
result = my_function()
assert result == expected
def test_edge_case(self):
"""Test edge case behavior."""
# ...
def test_error_handling(self):
"""Test that errors are raised appropriately."""
with pytest.raises(ValueError):
my_function(invalid_input)
PAML validation tests:
def test_mymodel_vs_paml():
"""Test MyModel against PAML reference."""
result = optimize_model("MyModel", align, tree)
paml_lnL = -1234.567890 # From PAML output
assert abs(result.lnL - paml_lnL) < 0.01
Documentation Guidelines
Docstring format (Google style):
def my_function(param1: str, param2: int = 5) -> bool:
"""
Brief one-line description.
Longer description with more details about what the function
does, when to use it, and any important notes.
Args:
param1: Description of param1
param2: Description of param2. Defaults to 5.
Returns:
Description of return value
Raises:
ValueError: When param1 is empty
RuntimeError: When computation fails
Examples:
>>> my_function("test", 10)
True
>>> my_function("")
ValueError: param1 cannot be empty
"""
pass
Getting Help
GitHub Issues: For bugs and feature requests
GitHub Discussions: For questions and general discussion
Email: adkern@uoregon.edu for other inquiries
License
By contributing, you agree that your contributions will be licensed under the GPL-3.0-or-later license.