Skip to main content

Bloat free result type for Python. No dummy classes. Pure types magic.

Project description

Resultite

Minimalist Result Type Handling with Pure Python Typing

Tired of boilerplate classes just to represent success or failure? Looking for Rust-like Result expressiveness without adopting a whole new type system or custom classes?

Resultite offers a clean, lightweight approach using Python's native Union[T, Exception] type hint pattern. Capture function outcomes transparently, handle errors gracefully, and chain operations—all with standard Python functions and type safety. It's the Pythonic way to handle results.


✨ Features

  • Purely Type-Based: Uses typing.Union[T, Exception] (aliased as Result[T]) – no custom classes needed.
  • Type-Safe: Leverages Python's type hinting for robust error handling patterns.
  • Sync & Async: Seamlessly works with both synchronous and asynchronous functions.
  • Composable: Functions designed for easy chaining and transformation of results.
  • Minimal & Lightweight: No external dependencies beyond standard Python.
  • Easy Handling: Utility functions to extract values, provide defaults, map results, or raise errors.
  • Testable: Simple functional units make testing straightforward.
  • Pythonic Philosophy: Embraces Python's built-in features over introducing complex abstractions.

📦 Installation

pip install resultite

Or using uv:

uv pip install resultite

🔧 Usage

Basic Sync Example:

from resultite import run_catching, get_or_throw, get_or_default, Result

def might_fail(data: str) -> int:
    if not data:
        raise ValueError("Input cannot be empty")
    return int(data) * 2

# Capture the outcome
result: Result[int] = run_catching(might_fail, "10") # -> 20
error_result: Result[int] = run_catching(might_fail, "") # -> ValueError("Input cannot be empty")

# Handle the result
try:
    value = get_or_throw(error_result) # Raises ValueError
except ValueError as e:
    print(f"Caught expected error: {e}")

# Provide a default
safe_value = get_or_default(error_result, -1) # -> -1
print(f"Safe value: {safe_value}")

# Get None on error
maybe_value = get_or_none(result) # -> 20
maybe_error = get_or_none(error_result) # -> None

Async Example:

import asyncio
from resultite import async_run_catching, map_result_async, get_or_else_async

async def fetch_data(url: str) -> dict:
    # In a real scenario, this would use a library like aiohttp
    await asyncio.sleep(0.1)
    if "error" in url:
        raise ConnectionError("Failed to connect")
    return {"data": url}

async def process_data(data: dict) -> str:
    await asyncio.sleep(0.1)
    return data.get("data", "default").upper()

async def main():
    result: Result[dict] = await async_run_catching(fetch_data, "http://example.com")
    # -> {"data": "http://example.com"}

    error_result: Result[dict] = await async_run_catching(fetch_data, "http://error.com")
    # -> ConnectionError("Failed to connect")

    # Map the successful result asynchronously
    processed_result: Result[str] = await map_result_async(result, process_data)
    # -> "HTTP://EXAMPLE.COM"

    # Handle error result with an async fallback
    final_value = await get_or_else_async(
        processed_result,
        lambda e: f"Async fallback for error: {e}" # Fallback if process_data failed
    )
    print(f"Processed value: {final_value}") # -> Processed value: HTTP://EXAMPLE.COM

    final_error_value = await get_or_else_async(
        error_result, # Original fetch error
        lambda e: f"Async fallback for error: {e}"
    )
    # -> Async fallback for error: Failed to connect
    print(f"Processed error value: {final_error_value}")

asyncio.run(main())

🔁 API Overview

The core type is Result[T] = Union[T, Exception].

Function Description Sync/Async
run_catching(func, *args, **kwargs) Executes func(*args, **kwargs) and returns Result[T]. Sync
async_run_catching(func, *args, **kwargs) Awaits func(*args, **kwargs) and returns Result[T]. Async
get_or_throw(result) Returns T if result is T, otherwise raises the Exception. Sync
get_or_none(result) Returns T if result is T, otherwise returns None. Sync
get_or_default(result, default) Returns T if result is T, otherwise returns default. Sync
get_or_else(result, func) Returns T if result is T, otherwise calls func(exception) to get a fallback T. Sync
get_or_else_async(result, func) Returns T if result is T, otherwise awaits func(exception) to get a fallback T. Async
map_result(result, func) If result is T, returns run_catching(func, result). If result is Exception, returns the exception. Output is Result[U]. Sync
map_result_async(result, func) If result is T, returns await async_run_catching(func, result). If result is Exception, returns the exception. Output is Result[U]. Async

✅ Testing

Tests use Python's built-in unittest module.

First, install test dependencies (e.g., if you need specific libraries for test functions):

# Using pip
pip install .[test]

# Using uv
uv pip install .[test]

(Note: Add a [test] extra in your pyproject.toml if you have test-specific dependencies).

Then, run tests:

# Discover and run tests using unittest's discovery
python -m unittest discover tests

# Or use pytest (it can run unittest tests)
pytest

🧪 Design Philosophy

No DSLs. No magic classes. No frameworks. Just Python's strengths, applied cleanly.

Resultite embraces Python's existing type system to provide a robust yet minimal way to handle function outcomes. It avoids introducing new abstractions often seen elsewhere, focusing instead on simple, composable functions that operate on the standard Union[T, Exception] pattern. Inspired by the clarity of Result patterns, but executed with Pythonic simplicity and pragmatism.


❤️ Credits

Crafted with a love for minimalism and functional programming principles in Python.


📄 License

MIT License

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

resultite-0.1.0.tar.gz (6.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

resultite-0.1.0-py3-none-any.whl (5.2 kB view details)

Uploaded Python 3

File details

Details for the file resultite-0.1.0.tar.gz.

File metadata

  • Download URL: resultite-0.1.0.tar.gz
  • Upload date:
  • Size: 6.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for resultite-0.1.0.tar.gz
Algorithm Hash digest
SHA256 9f4cb4be2c7dc609be6261fb8679c2162d3a3228a9ee62b9320acb064fde5378
MD5 7e6a572dfee7484cc52cac22a42e7f86
BLAKE2b-256 e1b05b0008bdca33ec85d73c683165ff5b1485ab12a0c3d42ba4db374057fe73

See more details on using hashes here.

Provenance

The following attestation bundles were made for resultite-0.1.0.tar.gz:

Publisher: publish.yml on LiYulin-s/resultite

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file resultite-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: resultite-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 5.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for resultite-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c7dc3de4bfaabeac5f73e89a865f4b767caa1bde57d605ed862a8737ed2094b4
MD5 9df9b3225bf1372b32b80e883253c555
BLAKE2b-256 86879ca39b0468ff40d4dc62998b297b17bb493c303cdb37e77afac09ba53676

See more details on using hashes here.

Provenance

The following attestation bundles were made for resultite-0.1.0-py3-none-any.whl:

Publisher: publish.yml on LiYulin-s/resultite

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page