Development Guide¶
This guide covers development setup, coding standards, testing practices, and contribution guidelines for the Athena Client library.
Development Setup¶
Prerequisites¶
Python 3.10 or higher
uv package manager
Git
Pre-commit (installed via uv)
Initial Setup¶
Clone the repository:
git clone <repository-url> cd athena-client
Install dependencies:
uv sync --dev
Install pre-commit hooks:
pre-commit install
Compile protocol buffers:
bash scripts/compile_proto.sh
Verify setup:
pytest pyright ruff check
Project Structure¶
athena-client/
├── src/athena_client/ # Main source code
│ ├── client/ # Client implementation
│ │ ├── models/ # Data models
│ │ ├── streaming/ # Streaming components
│ │ └── transformers/ # Image processing pipeline
│ ├── generated/ # Generated protobuf code
│ └── grpc_wrappers/ # gRPC integration
├── tests/ # Test suite
├── examples/ # Usage examples
├── docs/ # Sphinx documentation
├── scripts/ # Build and utility scripts
└── athena-protobufs/ # Protocol buffer definitions (submodule)
Development Commands¶
Package Management¶
# Install all dependencies
uv sync --dev
# Add a new dependency
uv add package-name
# Add a development dependency
uv add --dev package-name
# Build the package
uv build
Testing¶
# Run all tests
pytest
# Run specific test file
pytest tests/test_client.py
# Run tests with pattern matching
pytest -k "test_oauth"
# Run tests with coverage
pytest --cov=src/athena_client
# Run tests with coverage report
pytest --cov=src/athena_client --cov-report=html
Code Quality¶
# Format code
ruff format
# Check code style and fix auto-fixable issues
ruff check --fix
# Type checking
pyright
# Run all quality checks
ruff format && ruff check && pyright
Documentation¶
# Build documentation
cd docs && make clean && make html
# View documentation
open docs/_build/html/index.html
# Watch for changes (if sphinx-autobuild is installed)
cd docs && sphinx-autobuild . _build/html
Protocol Buffers¶
# Compile protocol buffers
bash scripts/compile_proto.sh
# Update submodule (if protobuf definitions change)
git submodule update --remote
Coding Standards¶
Python Style¶
The project follows these coding standards:
Type hints: Required for all public APIs and recommended for internal code
Docstrings: Google-style docstrings for all public functions and classes
Formatting: Black-compatible formatting via ruff
Line length: 80 characters maximum
Import organization: isort-compatible import sorting
Example function with proper style:
async def classify_images(
self,
image_iterator: AsyncIterator[bytes],
*,
correlation_id: str | None = None,
) -> AsyncIterator[ClassificationResult]:
"""Classify a stream of images.
Args:
image_iterator: Async iterator yielding image data as bytes
correlation_id: Optional correlation ID for request tracing
Returns:
Async iterator of classification results
Raises:
AthenaClientError: If classification fails
ConnectionError: If connection to service fails
Example:
>>> async with AthenaClient(channel, options) as client:
... results = client.classify_images(image_iterator)
... async for result in results:
... print(result.outputs)
"""
Async Patterns¶
The codebase uses async/await throughout:
Async context managers: For resource management
Async iterators: For streaming data processing
Async generators: For pipeline transformations
Proper cleanup: Using try/finally or async context managers
# Good: Async context manager with proper cleanup
async with AthenaClient(channel, options) as client:
async for result in client.classify_images(images):
# Process results
pass
# Good: Async generator for transformations
async def transform_images(
image_iterator: AsyncIterator[bytes]
) -> AsyncIterator[ProcessedImage]:
async for image_data in image_iterator:
processed = await process_image(image_data)
yield processed
Error Handling¶
Use structured error handling with custom exception types:
from resolver_athena_client.client.exceptions import AthenaClientError
try:
result = await client.classify_image(image_data)
except AthenaClientError as e:
logger.error(f"Classification failed: {e}")
# Handle specific client errors
except Exception as e:
logger.exception("Unexpected error")
# Handle unexpected errors
Testing Guidelines¶
Test Structure¶
Location: All tests in
tests/
directoryNaming: Test files start with
test_
Organization: Mirror the source code structure
tests/
├── test_client.py # Client tests
├── test_authentication.py # Auth tests
├── test_transformers.py # Pipeline tests
└── integration/ # Integration tests
└── test_end_to_end.py
Test Categories¶
- Unit Tests:
Test individual functions and classes
Mock external dependencies
Fast execution (< 1 second each)
- Integration Tests:
Test component interactions
May use real services in test mode
Slower execution acceptable
- End-to-End Tests:
Test complete workflows
Use real services when possible
Mark with
@pytest.mark.e2e
Writing Tests¶
Use pytest with async support:
import pytest
from unittest.mock import AsyncMock, Mock
from resolver_athena_client.client.athena_client import AthenaClient
@pytest.fixture
async def mock_channel():
"""Create a mock gRPC channel."""
return Mock()
@pytest.fixture
def client_options():
"""Create test client options."""
return AthenaOptions(
host="test-host",
deployment_id="test-deployment",
resize_images=True,
)
@pytest.mark.asyncio
async def test_client_classification(mock_channel, client_options):
"""Test basic client classification."""
async with AthenaClient(mock_channel, client_options) as client:
# Test implementation
pass
@pytest.mark.asyncio
async def test_authentication_failure():
"""Test authentication error handling."""
with pytest.raises(AuthenticationError):
# Test that should raise AuthenticationError
pass
Mocking Guidelines¶
Mock external services: Don’t make real API calls in unit tests
Use AsyncMock: For async functions and context managers
Test both success and failure: Cover error paths
Verify interactions: Check that mocks were called correctly
@pytest.mark.asyncio
async def test_credential_helper_token_refresh():
"""Test automatic token refresh."""
mock_http_client = AsyncMock()
mock_http_client.post.return_value.json.return_value = {
"access_token": "new-token",
"expires_in": 3600
}
credential_helper = CredentialHelper(
client_id="test-id",
client_secret="test-secret",
http_client=mock_http_client
)
token = await credential_helper.get_token()
assert token == "new-token"
mock_http_client.post.assert_called_once()
Documentation Standards¶
Documentation Types¶
API Documentation: Auto-generated from docstrings
User Guides: High-level usage documentation
Examples: Working code examples
Development Docs: This guide and contribution instructions
Docstring Format¶
Use Google-style docstrings:
def process_image(image_data: bytes, *, resize: bool = True) -> ProcessedImage:
"""Process image data for classification.
This function resizes, compresses, and validates image data before
sending it for classification.
Args:
image_data: Raw image data as bytes
resize: Whether to resize image to optimal dimensions
Returns:
Processed image ready for classification
Raises:
ValueError: If image data is invalid
ProcessingError: If image processing fails
Example:
>>> image_data = load_image("photo.jpg")
>>> processed = process_image(image_data, resize=True)
>>> print(f"Processed size: {len(processed.data)}")
"""
Building Documentation¶
# Install documentation dependencies
uv sync --group docs
# Build HTML documentation
cd docs
make clean
make html
# View the documentation
open _build/html/index.html
Contribution Workflow¶
Making Changes¶
Create a feature branch:
git checkout -b feature/your-feature-name
Make your changes following the coding standards
Add tests for new functionality
Update documentation if needed
Run quality checks:
ruff format ruff check pyright pytest
Commit your changes:
git add . git commit -m "feat: add new feature"
Pull Request Guidelines¶
Title format:
[component] Description
(e.g.,[client] Add OAuth support
)Description: Clear description of changes and motivation
Tests: Include tests for new functionality
Documentation: Update docs for API changes
Quality checks: Ensure all checks pass
Single focus: Keep PRs focused on one change
Commit Message Format¶
Use conventional commit format:
type(scope): description
[optional body]
[optional footer]
Types:
* feat
: New feature
* fix
: Bug fix
* docs
: Documentation changes
* test
: Test changes
* refactor
: Code refactoring
* style
: Code style changes
* chore
: Maintenance tasks
Examples:
feat(client): add OAuth credential helper
fix(auth): handle token refresh failures
docs(api): update authentication examples
test(client): add integration tests for streaming
Pre-commit Hooks¶
The project uses pre-commit hooks to ensure code quality:
ruff: Code formatting and linting
pyright: Type checking
conventional-commits: Commit message validation
If hooks fail, fix the issues and commit again:
# Fix formatting issues
ruff format
# Fix linting issues
ruff check --fix
# Check types
pyright
# Commit again
git add .
git commit -m "fix: resolve linting issues"
Release Process¶
Version Management¶
The project uses semantic versioning (SemVer):
Major (1.0.0): Breaking changes
Minor (0.1.0): New features, backward compatible
Patch (0.0.1): Bug fixes, backward compatible
Creating Releases¶
Update version in
pyproject.toml
Update changelog with release notes
Create release tag:
git tag -a v0.1.0 -m "Release v0.1.0" git push origin v0.1.0
Build and publish:
uv build # Publish to PyPI (if configured)
Debugging and Troubleshooting¶
Common Development Issues¶
- Protocol buffer compilation fails:
Ensure all dependencies are installed:
uv sync --dev
Check that the submodule is up to date:
git submodule update --init
- Type checking errors:
Ensure pyright is configured correctly
Check that generated code is excluded: see
pyproject.toml
- Test failures:
Run tests individually to isolate issues
Check mock setup and assertions
Verify async test patterns
- Import errors in tests:
Ensure the package is installed in development mode
Check that
__init__.py
files are present
Debug Configuration¶
Enable debug logging for development:
import logging
# Enable debug logging
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s.%(msecs)03d %(levelname)s [%(name)s]: %(message)s",
datefmt="%H:%M:%S"
)
# Set specific logger levels
logging.getLogger("athena_client").setLevel(logging.DEBUG)
logging.getLogger("grpc").setLevel(logging.INFO)
Performance Profiling¶
For performance analysis:
import cProfile
import pstats
# Profile code execution
profiler = cProfile.Profile()
profiler.enable()
# Your code here
await your_function()
profiler.disable()
# Analyze results
stats = pstats.Stats(profiler)
stats.sort_stats('cumulative')
stats.print_stats(20)
Getting Help¶
If you encounter issues during development:
Check the documentation: Review relevant sections
Search existing issues: Look for similar problems on GitHub
Enable debug logging: Get more detailed error information
Ask for help: Create an issue with detailed information
For questions about:
Setup issues: See Installation Guide
Usage patterns: See Examples
API details: See API Reference
Authentication: See Authentication
Contributing Checklist¶
Before submitting a pull request:
[ ] Code follows project style guidelines
[ ] All tests pass (
pytest
)[ ] Type checking passes (
pyright
)[ ] Code is formatted (
ruff format
)[ ] Linting passes (
ruff check
)[ ] Documentation is updated for API changes
[ ] Tests are added for new functionality
[ ] Commit messages follow conventional format
[ ] Pre-commit hooks are installed and passing
This ensures high code quality and smooth collaboration.