Contributing Guide

Thank you for your interest in contributing to arrayops! This guide will help you get started.

Getting Started

Prerequisites

  • Python 3.8 or higher

  • Rust 1.75 or higher (required for SIMD optimizations)

  • maturin (for building the extension)

  • git for version control

Setting Up Development Environment

  1. Fork and clone the repository:

    git clone https://github.com/your-username/ao.git
    cd arrayops
    
  2. Install development dependencies:

    pip install -r requirements-dev.txt
    
  3. Build the package in development mode:

    maturin develop
    
  4. Verify the installation:

    python -c "import arrayops; print(ao.__version__)"
    

Development Workflow

1. Create a Feature Branch

git checkout -b feature/your-feature-name

Use descriptive branch names:

  • feature/add-map-operation

  • fix/sum-overflow-handling

  • docs/improve-api-docs

2. Make Your Changes

  • Write code following the style guidelines below

  • Add tests for all new functionality

  • Update documentation as needed

  • Ensure 100% test coverage is maintained

3. Test Your Changes

# Run Python tests
pytest tests/ -v

# Run with coverage
pytest tests/ --cov=arrayops --cov-report=html

# Run Rust tests
cargo test --lib

# Check code quality
ruff format .
ruff check .
mypy arrayops tests

4. Commit Your Changes

Follow conventional commit messages:

feat: add map operation for array transformation
fix: handle empty arrays in sum operation
docs: update API reference with new examples
test: add tests for edge cases in scale operation

5. Submit a Pull Request

  • Push your branch to your fork

  • Open a pull request on GitHub

  • Fill out the PR template

  • Link any related issues

Code Style Guidelines

Python Code

  • Follow PEP 8 style guide

  • Use ruff for formatting and linting

  • Maximum line length: 100 characters

  • Use type hints where applicable

  • Document all public functions with docstrings

Example:

def new_function(arr: array.array, param: float) -> None:
    """Brief description of the function.
    
    Args:
        arr: Description of arr parameter
        param: Description of param parameter
    
    Raises:
        TypeError: When arr is not an array.array
    """
    # Implementation

Rust Code

  • Follow Rust standard formatting (rustfmt)

  • Use cargo clippy for linting

  • Document all public functions

  • Use meaningful variable names

  • Handle errors explicitly (no panics in library code)

Example:

/// Brief description of the function.
///
/// # Arguments
/// * `py` - Python interpreter instance
/// * `array` - Input array to process
///
/// # Returns
/// Result containing the processed value or error
fn new_function(py: Python, array: &PyAny) -> PyResult<PyObject> {
    // Implementation
}

Testing Requirements

Test Coverage

  • 100% code coverage must be maintained

  • All code paths must be tested

  • Include edge cases (empty arrays, single elements, large arrays)

  • Test error conditions

Writing Tests

Python Tests:

import pytest
import array
import arrayops as ao

def test_new_function_basic():
    """Test basic functionality."""
    arr = array.array('i', [1, 2, 3])
    result = ao.new_function(arr, 2.0)
    assert result == expected_value

def test_new_function_edge_cases():
    """Test edge cases."""
    empty = array.array('i', [])
    result = ao.new_function(empty, 1.0)
    assert result == 0

Rust Tests:

#[test]
fn test_new_function_int32() {
    Python::with_gil(|py| {
        let array_module = PyModule::import(py, "array").unwrap();
        let array_type = array_module.getattr("array").unwrap();
        let arr = array_type
            .call1(("i", PyList::new(py, &[1, 2, 3])))
            .unwrap();
        let result = new_function(py, arr).unwrap();
        // Assertions
    });
}

Running Tests

# Python tests
pytest tests/ -v

# Rust tests
cargo test --lib

# Both with coverage
pytest tests/ --cov=arrayops --cov-report=term-missing
cargo tarpaulin --tests --lib

Adding New Operations

Step-by-Step Guide

  1. Design the API:

    • Determine function signature

    • Consider in-place vs. new array

    • Plan error handling

  2. Implement in Rust (src/lib.rs):

    • Add type dispatch for all supported types

    • Implement generic function

    • Add error handling

    • Write Rust unit tests

  3. Expose to Python:

    • Add #[pyfunction] attribute

    • Register in #[pymodule] function

    • Update __all__ in arrayops/__init__.py

  4. Add Type Stubs (arrayops/_ao.pyi):

    • Add function signature with type hints

    • Include docstring

  5. Write Python Tests (tests/):

    • Test all numeric types

    • Test edge cases

    • Test error conditions

  6. Update Documentation:

    • Add to docs/api.md

    • Add examples to docs/examples.md

    • Update README if needed

Example: Adding a New Operation

See the existing sum and scale implementations as reference:

  • src/lib.rs - Rust implementation

  • arrayops/__init__.py - Python wrapper

  • arrayops/_ao.pyi - Type stubs

  • tests/test_basic.py - Test examples

Code Review Process

What Reviewers Look For

  1. Correctness:

    • Code works as intended

    • All tests pass

    • Edge cases handled

  2. Code Quality:

    • Follows style guidelines

    • Well-documented

    • No unnecessary complexity

  3. Performance:

    • Efficient implementation

    • No performance regressions

    • Appropriate use of zero-copy

  4. Testing:

    • Comprehensive test coverage

    • Tests are clear and maintainable

  5. Security:

    • Input validation is performed

    • Error messages are safe (no sensitive info)

    • Unsafe code is minimized and documented

    • Security-sensitive paths are tested

    • See Security Review Checklist below

Responding to Feedback

  • Be open to suggestions

  • Ask questions if feedback is unclear

  • Make requested changes promptly

  • Update your PR when changes are made

Documentation

When to Update Documentation

  • Adding new functions → Update docs/api.md

  • New examples → Add to docs/examples.md

  • Architecture changes → Update docs/design.md

  • Breaking changes → Update docs/CHANGELOG.md

Documentation Style

  • Use clear, concise language

  • Include code examples

  • Cross-reference related docs

  • Keep examples up-to-date

Release Process

Contributors don’t need to handle releases, but understanding the process helps:

  1. Update version in pyproject.toml and arrayops/__init__.py

  2. Update docs/CHANGELOG.md

  3. Create git tag

  4. Build and publish to PyPI

Getting Help

  • Questions? Open a GitHub discussion

  • Found a bug? Open an issue

  • Need clarification? Ask in PR comments

  • Stuck on something? Check existing code for examples

Code of Conduct

  • Be respectful and inclusive

  • Welcome newcomers

  • Focus on constructive feedback

  • Help others learn and grow

Recognition

Contributors will be:

  • Listed in the project README

  • Credited in release notes

  • Acknowledged in the project

Thank you for contributing to arrayops!

Security Review Checklist

When reviewing code for security, ensure:

  • [ ] Input Validation: All inputs are validated (type, properties, constraints)

  • [ ] Error Messages: Error messages are helpful but don’t leak sensitive information

    • No memory addresses or pointers

    • No internal implementation details

    • No stack traces or debug information

  • [ ] Unsafe Code: Unsafe code is minimized and well-documented

    • Safety comments explain why code is safe

    • Unsafe blocks are justified (why safe APIs can’t be used)

  • [ ] Dependency Security: New dependencies are reviewed for security

    • Check for known vulnerabilities

    • Verify dependencies are actively maintained

  • [ ] Test Coverage: Security-sensitive code paths are tested

    • Input validation tests

    • Edge case tests

    • Error condition tests

    • Large input tests (DoS considerations)

For detailed security guidelines, see the Developer Security Guide.