Contributing to PyAMA

This guide covers contributing to PyAMA, including coding guidelines, development workflow, and contribution process.

Development Environment

Prerequisites

  • Python 3.11+

  • UV package manager

  • Git

  • Code editor (VS Code recommended with Python extension)

Setup

  1. Clone the Repository

    git clone https://github.com/SoftmatterLMU-RaedlerGroup/pyama.git
    cd pyama
    
  2. Install Dependencies

    uv sync --all-extras
    
  3. Install in Development Mode

    uv pip install -e pyama/
    
  4. Verify Installation

    uv run pyama --version
    uv run pytest tests/
    

Coding Guidelines

Style Guide

PyAMA follows PEP 8 with additional conventions:

# Use snake_case for variables and functions
def process_cell_data(input_data: np.ndarray) -> Dict[str, float]:
    """Process cell data and return metrics.
    
    Args:
        input_data: Array of cell measurements
        
    Returns:
        Dictionary with calculated metrics
    """
    return {"mean": np.mean(input_data), "std": np.std(input_data)}


# Use PascalCase for classes
class CellProcessor:
    """Handles processing of cell data."""
    
    def __init__(self, config: ProcessingConfig):
        self.config = config
        
    def process(self, data: CellData) -> ProcessedData:
        """Process cell data according to configuration."""
        return ProcessedData(data, self.config)


# Use UPPER_CASE for constants
DEFAULT_MIN_CELL_SIZE = 50
MAX_WORKERS = 8

Type Hints

Use built-in generics and union types:

# Preferred
def load_config(path: Path) -> Dict[str, Any]:
    """Load configuration from file."""
    return {}
    
def process_data(
    items: List[str],
    options: Optional[Dict] = None
) -> Tuple[List[str],]:
    """Process items with optional options."""
    pass

# Avoid
from typing import Dict, List, Tuple, Optional
def load_config(path: 'Dict[str, str]'):
    pass

Import Organization

All imports at the top of the file:

# Standard library
import os
from pathlib import Path
from typing import Any, Dict, List

# Third-party
import numpy as np
from scipy import ndimage

# Local imports
from pyama.types.processing import Channels, ChannelSelection
from pyama.utils import validate_config

Docstring Format

Use Google-style docstrings:

def segment_cells(
    image: np.ndarray,
    method: str = "logstd",
    parameters: Optional[Dict[str, Any]] = None
) -> np.ndarray:
    """Segment cells in microscopy image.
    
    Args:
        image: Input microscopy image (2D array)
        method: Segmentation method to use ('logstd', 'threshold')
        parameters: Optional method-specific parameters
        
    Returns:
        Binary mask where True indicates cell pixels
        
    Raises:
        ValueError: If method is not supported
        ValueError: If image is not 2D
        
    Example:
        >>> image = np.random.random((100, 100))
        >>> mask = segment_cells(image, method="threshold")
        >>> print(mask.shape)
        (100, 100)
    """
    if image.ndim != 2:
        raise ValueError("Image must be 2D")
    
    if method not in ["logstd", "threshold"]:
        raise ValueError(f"Method {method} not supported")
    
    # Implementation
    return binary_mask

Frontend State/Events (React)

Use typed events and focused hooks instead of ad-hoc global state:

type TaskUpdate = {
  taskId: string;
  progressPercent: number;
  phase: string;
};

function useTaskStatus(taskId: string) {
  const [task, setTask] = useState<TaskUpdate | null>(null);

  useEffect(() => {
    if (!taskId) return;
    const timer = setInterval(async () => {
      const next = await api.getTask(taskId);
      setTask({
        taskId: next.id,
        progressPercent: next.progress_percent ?? 0,
        phase: next.phase ?? "pending",
      });
    }, 1000);
    return () => clearInterval(timer);
  }, [taskId]);

  return task;
}

Testing Guidelines

Test Structure

# pyama/tests/processing/test_seg.py
import pytest
import numpy as np
from pyama.processing.segmentation import segment_cells

class TestSegmentation:
    """Test cell segmentation functionality."""
    
    @pytest.fixture
    def sample_image(self):
        """Create test image with known cells."""
        image = np.zeros((100, 100), dtype=np.float32)
        # Add circular cells
        y, x = np.ogrid[-50:50, -50:50]
        mask = x*x + y*y <= 20*20
        image[mask] = 1.0
        return image
    
    def test_segment_cells_logstd(self, sample_image):
        """Test LOG-STD segmentation."""
        result = segment_cells(sample_image, method="logstd")
        
        # Verify basic properties
        assert result.dtype == bool
        assert result.shape == sample_image.shape
        assert np.any(result)  # Should detect cells
        
        # Verify specific properties
        n_cells = len(np.unique(result)) - 1  # Exclude background
        assert n_cells == 1
    
    def test_segment_cells_invalid_method(self, sample_image):
        """Test error handling for invalid method."""
        with pytest.raises(ValueError, match="not supported"):
            segment_cells(sample_image, method="invalid")

Visual Testing Rules

From AGENTS.md testing requirements:

  1. Essential tests only: Implement minimal tests that demonstrate correctness

  2. Save plots to {package}/tests/_plots/ directory (e.g., pyama/tests/_plots/ for core tests)

  3. Use deterministic RNG with np.random.seed()

  4. Keep assertions robust: Avoid tight numerical tolerances

pyama/tests/
├── _plots/              # Generated test plots
├── analysis/            # Analysis model tests
   ├── test_event.py
   └── test_kinetic.py
├── features/            # Feature extraction tests
   ├── test_area.py
   ├── test_intensity_total.py
   └── test_particle_num.py
├── processing/          # Processing workflow tests
   ├── test_merge.py
   ├── test_normalization.py
   ├── test_seg.py
   └── test_track.py
└── utils/               # Utility function tests
    └── progress.py

Running Tests

# Run all tests
uv run pytest

# Run specific test file
uv run pytest pyama/tests/processing/test_merge.py

# Run with coverage
uv run pytest --cov=pyama

# Run visual tests
uv run pytest pyama/tests/processing/test_seg.py -v

Documentation Guidelines

Code Documentation

  • Every public function/class has a docstring

  • Document all parameters and return types

  • Include examples for complex functions

  • Use cross-references with :class:, :func:, etc.

README Files

Each package has minimal README with:

  • Brief description

  • Installation instructions

  • Usage example

  • Link to full documentation

Changes Documentation

Update:

  • CHANGELOG.md for user-facing changes

  • Inline code comments for technical notes

  • Documentation for new features

Git Workflow

Branch Structure

main                # Stable release branch
├── develop         # Integration branch
├── feature/xxx     # Feature branches
├── bugfix/xxx      # Bug fix branches
└── release/vX.Y.Z  # Release branches

Commit Messages

Follow conventional commits:

type(scope): description

feat(core): add support for new file format
fix(pro): fix crash when loading invalid config
docs(readme): update installation instructions
refactor(backend): improve error handling
test(visualization): add tests for trace plotting

Development Workflow

  1. Create Branch

    git checkout -b feature/my-feature
    
  2. Make Changes

    • Write code

    • Add tests

    • Update documentation

  3. Test Changes

    uv run pytest
    uv run ruff check
    uv run ruff format
    
  4. Commit Changes

    git add .
    git commit -m "feat: implement awesome feature"
    
  5. Push and Create PR

    git push origin feature/my-feature
    # Create PR on GitHub
    

Pull Request Process

  1. PR Requirements

    • All tests pass

    • Code formatted with ruff

    • Documentation updated

    • At least one review

  2. PR Template

    ## Description
    Brief description of changes
    
    ## Changes
    - What was changed
    - Why it was changed
    
    ## Testing
    - How tested
    - Test coverage
    
    ## Checklist
    - [ ] Tests pass
    - [ ] Code formatted
    - [ ] Documentation updated
    

Code Quality

Linting and Formatting

# Check code style
uv run ruff check

# Format code
uv run ruff format

# Type checking
uv run ty check

Pre-commit Hooks (Optional)

# Install pre-commit
pip install pre-commit

# Setup hooks
pre-commit install

# Hooks will run automatically on commit

Code Review Focus Areas

  1. Correctness: Does the code work as intended?

  2. Performance: Is it efficient for large datasets?

  3. Readability: Is the code clear and maintainable?

  4. Testing: Are edge cases covered?

  5. Documentation: Is it well-documented?

Package-specific Guidelines

pyama

  • Focus on pure Python algorithms

  • Minimal dependencies

  • Extensive type hints

  • Performance-critical sections use numpy

pyama-react

  • Keep components typed and focused

  • Move side effects to hooks in src/hooks/

  • Provide clear status/error feedback for all backend operations

  • Preserve API compatibility with pyama server routes

Performance Considerations

Memory Management

# Good: Use memory mapping for large arrays
image_stack = np.load("large_file.npy", mmap_mode="r")

# Good: Process frames one at a time
for frame in frames:
    process_frame(frame)
    del frame  # Explicit cleanup

# Avoid: Loading entire dataset into memory
all_data = [load_file(f) for f in file_list]  # Bad for large datasets

Threading

# Correct: Use ThreadPoolExecutor for CPU work
with ThreadPoolExecutor(max_workers=4) as executor:
    futures = [executor.submit(process_fov, fov) for fov in fovs]
    
# Avoid: Creating too many threads
threads = [Thread(target=worker) for _ in range(100)]  # Bad

Vectorization

# Good: Use numpy operations
result = np.sum(image > threshold)  # Fast

# Avoid: Python loops
count = 0
for i in range(image.shape[0]):
    for j in range(image.shape[1]):
        if image[i, j] > threshold:
            count += 1  # Slow

Release Process

Version Management

Use semantic versioning (MAJOR.MINOR.PATCH):

  • MAJOR: Breaking changes

  • MINOR: New features

  • PATCH: Bug fixes

Release Checklist

  1. Update Version

    • Update version in pyproject.toml

    • Update version in each package’s __init__.py

  2. Changelog

    • Update CHANGELOG.md

    • Document all changes

  3. Testing

    • Run full test suite

    • Test installation from source

  4. Documentation

    • Update user documentation

    • Check for broken links

  5. Tag and Push

    git tag -a v1.2.0 -m "Release v1.2.0"
    git push origin v1.2.0
    

Publishing

Each package can be published independently:

cd pyama
uv publish

cd pyama-acdc
uv publish
# etc.

Getting Help

Resources

  • Documentation: Full reference at https://pyama.readthedocs.io

  • Issues: Report bugs at https://github.com/SoftmatterLMU-RaedlerGroup/pyama/issues

  • Discussions: Feature requests and questions

  • Discord: Real-time chat (if available)

Asking Good Questions

  1. Check existing issues and documentation

  2. Provide minimal reproducible example

  3. Include system information (OS, Python version)

  4. Share error messages and logs

Recognition

Contributors are recognized in:

  • CONTRIBUTORS.md file

  • Release notes

  • git commit history (co-authorship)

Thank you for contributing to PyAMA! Your improvements help the microscopy community analyze data more effectively.