CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-returns

Functional programming library providing type-safe containers for error handling, side effects, and composable operations with monadic patterns.

Pending
Overview
Eval results
Files

development-tools.mddocs/

Development Tools

Integration tools for testing frameworks (pytest, hypothesis), static analysis (mypy), and development workflows with functional containers. These tools enhance the development experience when working with Returns.

Capabilities

Pytest Integration

Testing utilities and assertions specifically designed for functional containers.

# Pytest plugin automatically loaded
from returns.contrib.pytest import ReturnsAsserts

class ReturnsAsserts:
    """Custom assertions for Returns containers"""
    
    def assert_success(self, container: Result[T, E], expected: T) -> None:
        """Assert Result is Success with expected value"""
        
    def assert_failure(self, container: Result[T, E]) -> None:
        """Assert Result is Failure"""
        
    def assert_failure_with(self, container: Result[T, E], expected_error: E) -> None:
        """Assert Result is Failure with specific error"""
        
    def assert_some(self, container: Maybe[T], expected: T) -> None:
        """Assert Maybe is Some with expected value"""
        
    def assert_nothing(self, container: Maybe[T]) -> None:
        """Assert Maybe is Nothing"""

# Additional test utilities
def raises_failure(exception_type: type[Exception]) -> Callable:
    """Context manager for testing exception-based failures"""

def returns_success(expected_value: T) -> Callable:
    """Decorator to test function returns Success with value"""

def returns_failure() -> Callable:
    """Decorator to test function returns Failure"""

Usage examples:

import pytest
from returns.contrib.pytest import ReturnsAsserts
from returns.result import Success, Failure, safe
from returns.maybe import Some, Nothing

class TestMyFunctions(ReturnsAsserts):
    """Test class with Returns assertions"""
    
    def test_successful_operation(self):
        result = Success(42)
        self.assert_success(result, 42)
    
    def test_failed_operation(self):
        result = Failure("error occurred")
        self.assert_failure(result)
        self.assert_failure_with(result, "error occurred")
    
    def test_maybe_operations(self):
        some_value = Some(42)
        nothing_value = Nothing
        
        self.assert_some(some_value, 42)
        self.assert_nothing(nothing_value)

# Testing with decorators and context managers
@safe
def divide(a: int, b: int) -> float:
    return a / b

def test_safe_division():
    """Test safe division function"""
    # Test successful case
    result = divide(10, 2)
    assert isinstance(result, Success)
    assert result.unwrap() == 5.0
    
    # Test failure case
    result = divide(10, 0)
    assert isinstance(result, Failure)
    assert isinstance(result.failure(), ZeroDivisionError)

# Using pytest fixtures
@pytest.fixture
def sample_data():
    return [Success(1), Failure("error"), Success(3)]

def test_with_fixture(sample_data):
    successes = [r for r in sample_data if isinstance(r, Success)]
    assert len(successes) == 2

Hypothesis Integration

Property-based testing strategies for generating test data for functional containers.

from returns.contrib.hypothesis import get_strategies

# Strategy generators
def get_strategies(container_type: type) -> dict[str, SearchStrategy]:
    """Get Hypothesis strategies for container types"""

# Pre-defined strategies
success_strategy = strategies.success(strategies.integers())
failure_strategy = strategies.failure(strategies.text())
result_strategy = strategies.result(strategies.integers(), strategies.text())

some_strategy = strategies.some(strategies.integers())
nothing_strategy = strategies.nothing()
maybe_strategy = strategies.maybe(strategies.integers())

io_strategy = strategies.io(strategies.integers())
future_strategy = strategies.future(strategies.integers())

Usage examples:

import pytest
from hypothesis import given, strategies as st
from returns.contrib.hypothesis import get_strategies
from returns.result import Result, Success, Failure
from returns.maybe import Maybe, Some, Nothing

# Get strategies for specific types
result_strategies = get_strategies(Result)
maybe_strategies = get_strategies(Maybe)

@given(result_strategies['result'](st.integers(), st.text()))
def test_result_properties(result):
    """Property-based test for Result containers"""
    # Test that mapping preserves container type
    mapped = result.map(lambda x: x * 2)
    
    if isinstance(result, Success):
        assert isinstance(mapped, Success)
        assert mapped.unwrap() == result.unwrap() * 2
    else:
        assert isinstance(mapped, Failure)
        assert mapped.failure() == result.failure()

@given(maybe_strategies['maybe'](st.integers()))
def test_maybe_properties(maybe):
    """Property-based test for Maybe containers"""
    # Test that binding with identity preserves value
    identity_bound = maybe.bind(lambda x: Some(x))
    
    if isinstance(maybe, Some):
        assert isinstance(identity_bound, Some)
        assert identity_bound.unwrap() == maybe.unwrap()
    else:
        assert isinstance(identity_bound, Nothing)

# Custom property tests
@given(st.lists(st.integers(), min_size=1))
def test_collection_processing(numbers):
    """Test collection processing properties"""
    from returns.iterables import sequence
    from returns.result import safe
    
    @safe
    def process_number(n: int) -> int:
        return n * 2
    
    results = [process_number(n) for n in numbers]
    sequenced = sequence(results)
    
    # Property: if all operations succeed, sequence succeeds
    assert isinstance(sequenced, Success)
    assert sequenced.unwrap() == [n * 2 for n in numbers]

MyPy Integration

Static analysis plugin and type checking enhancements for Returns containers.

# MyPy plugin configuration (in mypy.ini or setup.cfg)
"""
[mypy]
plugins = returns.contrib.mypy.returns_plugin

[tool:mypy-returns.*]  
ignore_errors = True
"""

# Type transformation utilities
from returns.contrib.mypy import plugin_helpers

def transform_container_type(container_type: type) -> type:
    """Transform container types for better inference"""

def infer_generic_args(container: Container) -> tuple[type, ...]:
    """Infer generic type arguments from container instance"""

# Enhanced type stubs
def bind(self: Container[T], func: Callable[[T], Container[U]]) -> Container[U]: ...
def map(self: Container[T], func: Callable[[T], U]) -> Container[U]: ...

Configuration example:

# mypy.ini
[mypy]
plugins = returns.contrib.mypy.returns_plugin
strict_optional = True
warn_redundant_casts = True
warn_unused_ignores = True
disallow_any_generics = True

# Enable Returns-specific type checking
[mypy-returns.*]
check_untyped_defs = True
disallow_incomplete_defs = True

Usage examples:

# Type-safe container usage with mypy checking
from returns.result import Result, Success, Failure
from returns.maybe import Maybe, Some, Nothing

def process_data(data: str) -> Result[int, ValueError]:
    """MyPy will enforce return type matches Success/Failure types"""
    try:
        value = int(data)
        if value < 0:
            return Failure(ValueError("Negative values not allowed"))
        return Success(value)
    except ValueError as e:
        return Failure(e)

# MyPy will catch type mismatches
def chain_operations(x: int) -> Result[str, ValueError]:
    return (
        process_data(str(x))
        .bind(lambda n: Success(n * 2))  # Result[int, ValueError]
        .map(str)                        # Result[str, ValueError]
    )

# Generic type inference
def generic_processor[T, E](
    container: Result[T, E], 
    transform: Callable[[T], str]
) -> Result[str, E]:
    """MyPy infers correct generic types"""
    return container.map(transform)

Testing Utilities

Additional utilities for testing functional code patterns.

def assert_container_equals(actual: Container[T], expected: Container[T]) -> None:
    """Deep equality assertion for containers"""

def mock_container(container_type: type, value: T) -> Container[T]:
    """Create mock container for testing"""

def container_test_case(container: Container[T]) -> TestCase:
    """Generate test cases for container operations"""

class ContainerTestMixin:
    """Mixin class providing container testing utilities"""
    
    def run_container_laws(self, container: Container[T]) -> None:
        """Verify container follows mathematical laws"""
        
    def test_functor_laws(self, container: Functor[T]) -> None:
        """Test functor laws (identity, composition)"""
        
    def test_monad_laws(self, container: Monad[T]) -> None:
        """Test monad laws (left identity, right identity, associativity)"""

Usage examples:

import unittest
from returns.contrib.pytest import ContainerTestMixin
from returns.result import Success, Failure
from returns.maybe import Some, Nothing

class TestContainerLaws(unittest.TestCase, ContainerTestMixin):
    """Test that containers follow mathematical laws"""
    
    def test_result_monad_laws(self):
        """Test Result follows monad laws"""
        success = Success(42)
        failure = Failure("error")
        
        self.test_monad_laws(success)
        self.test_monad_laws(failure)
    
    def test_maybe_functor_laws(self):
        """Test Maybe follows functor laws"""
        some = Some(42)
        nothing = Nothing
        
        self.test_functor_laws(some)
        self.test_functor_laws(nothing)

# Mock containers for testing
def test_with_mock_containers():
    """Test using mock containers"""
    from unittest.mock import Mock
    
    # Mock a Result container
    mock_result = Mock(spec=Success)
    mock_result.bind.return_value = Success(84)
    mock_result.map.return_value = Success("42")
    
    # Test interaction
    def double(x: int) -> Success[int]:
        return Success(x * 2)
    
    result = mock_result.bind(double)
    assert result.unwrap() == 84

Debugging and Inspection

Tools for debugging and inspecting functional container operations.

def trace_container(container: Container[T], label: str = "") -> Container[T]:
    """Log container operations for debugging"""

def inspect_container(container: Container[T]) -> dict[str, Any]:
    """Get detailed inspection info about container"""

def debug_pipeline(*operations: Callable) -> Callable:
    """Debug a pipeline of operations"""

class ContainerDebugger:
    """Debugger for container operations"""
    
    def __init__(self, enabled: bool = True): ...
    def trace(self, container: Container[T], operation: str) -> Container[T]: ...
    def dump_trace(self) -> list[dict]: ...

Usage examples:

from returns.contrib.debug import trace_container, debug_pipeline
from returns.result import Success
from returns.pipeline import flow

# Trace individual operations
result = (
    Success(42)
    .bind(lambda x: trace_container(Success(x * 2), "double"))
    .map(lambda x: trace_container(x + 1, "add_one"))
)

# Debug entire pipeline
@debug_pipeline
def process_data(x: int) -> int:
    return flow(
        x,
        lambda n: n * 2,
        lambda n: n + 1,
        lambda n: n / 2
    )

result = process_data(10)  # Logs each step

Development Workflow Patterns

Test-Driven Development

import pytest
from returns.contrib.pytest import ReturnsAsserts
from returns.result import Result, Success, Failure

class TestUserService(ReturnsAsserts):
    """TDD example with Returns containers"""
    
    def test_create_user_success(self):
        # Test first, implement later
        user_data = {"name": "John", "email": "john@example.com"}
        result = create_user(user_data)
        
        self.assert_success(result, User(name="John", email="john@example.com"))
    
    def test_create_user_invalid_email(self):
        user_data = {"name": "John", "email": "invalid-email"}
        result = create_user(user_data)
        
        self.assert_failure_with(result, ValidationError("Invalid email"))

# Implementation follows tests
def create_user(data: dict) -> Result[User, ValidationError]:
    # Implementation guided by tests
    pass

Property-Based Testing

from hypothesis import given, strategies as st
from returns.contrib.hypothesis import get_strategies

@given(st.lists(st.integers()))
def test_fold_properties(numbers):
    """Property-based testing of fold operations"""
    from returns.iterables import Fold
    from returns.result import Success
    
    def add(a: int, b: int) -> Success[int]:
        return Success(a + b)
    
    results = [Success(n) for n in numbers]
    folded = Fold.loop(results, Success(0), add)
    
    # Property: folding should equal sum
    assert folded.unwrap() == sum(numbers)

Development tools in Returns provide comprehensive support for testing, type checking, and debugging functional code, enabling robust development workflows while maintaining the benefits of functional programming patterns.

Install with Tessl CLI

npx tessl i tessl/pypi-returns

docs

async-operations.md

container-methods.md

context-operations.md

conversions.md

core-containers.md

development-tools.md

functional-utilities.md

index.md

iteration-utilities.md

pointfree.md

trampolines.md

unsafe-operations.md

tile.json