Skip to main content

Bring some useful tools from Rust to Python

Project description

rusty-utils

Bringing useful tools from Rust to Python.

🚪 Portals

📥 Installation

rusty-utils is available on PyPI.

Install using pip:

pip install rusty-utils

Or with poetry:

poetry add rusty-utils

📚 Features

rusty-utils brings Rust-inspired constructs like Result and Option to Python, allowing developers to write cleaner and more robust error-handling code.

For more details, check out the full documentation.

Result[T, E]

The Result type is inspired by Rust, enabling you to handle success (Ok) and failure (Err) in a clean, expressive way.

from rusty_utils import Result, Ok, Err

# Success case
success: Result[int, Exception] = Ok(42)

# Failure case
failure: Result[int, Exception] = Err(Exception("An error occurred"))

Custom Result Types

You can alias your own custom Result types to suit your domain-specific needs.

from typing import TypeVar
from rusty_utils import Result, Ok, Err


class MyError(Exception): pass


_T = TypeVar("_T")
MyResult = Result[_T, MyError]

# Custom success and failure cases
success: MyResult[int] = Ok(42)
failure: MyResult[int] = Err(MyError("Something went wrong"))

? Operator

In Rust, the ? operator is used to easily propagate errors up the call stack, allowing you to return early if a function fails, and to handle success (Ok) and failure (Err) values concisely.

fn side_effect() -> Resut<i32, Error> {
    // some code that may fail
    Ok(42)
}

fn process() -> Result<(), Error> {
  let result = side_effect()?; // Propagates the error if it occurs
  ... // Other operation
}

In Python, you can't overload the ? operator (we even didn't treat ? as a valid operator in Python)

result = side_effect()?  # What???

In Python, something has similar ability to throw the Err and remain the Ok value is:
Python built-in try-except.

from rusty_utils import Catch


def side_effect() -> float:
    # Simulate a potential failure (e.g., division by zero)
    return 42 / 0


@Catch(Exception, ZeroDivisionError)
def wrapped_side_effect() -> float:
    return side_effect()


@Catch(Exception)
def process() -> None:
    result1 = wrapped_side_effect().unwrap_or_raise()  # You achieve same functionality with `unwrap_or_raise()`!
    result2 = Catch(Exception, ZeroDivisionError, func=side_effect).unwrap_or_raise() 

In this example:

  • We use the @Catch(Exception) decorator to make sure we can capture the raised Err and return to the caller.
    • What the @Catch(E) do is transform the function into a capturer which returns Result[T, E]
  • We can use the Catch in this way (result2) to capture the result of a fallible function call into a Result.
  • The wrapped_side_effect() function returns a Result that might be an error (Err) or a valid value (Ok).
    • Since the function returns a float and we mark it might raise an Exception, so it actually returns a Result[float, Exception].
  • Then use unwrap_or_raise() to handle the result: if it's an error, it raises the exception, effectively mimicking Rust's ? operator.

This approach enables cleaner error propagation and handling in Python, much like in Rust, but using Python’s exception-handling style.

Although the @Catch decorator accpets multiple exception types, it's recommended to use it only for one type of exception at a time, or your linter might can't resolve the type hints correctly. (like it might think the wrapped_side_effect returns a Result[float, Any])

API Overview

  • Querying Result Type:

    • is_ok(): Returns True if the Result is Ok.
    • is_err(): Returns True if the Result is Err.
  • Unwrapping Values:

    • expect(message): Unwraps the value or raises UnwrapError with a custom message.
    • unwrap(): Unwraps the value or raises UnwrapError.
    • unwrap_or(default): Returns the provided default value if Err.
    • unwrap_or_else(func): Returns the result of the provided function if Err.
    • unwrap_or_raise(): Raises the exception contained in Err.
  • Transforming Results:

    • ok(): Transforms Ok to Option[T]
    • err(): Transforms Err to Option[E]
    • map(func): Applies func to the Ok value.
    • map_err(func): Applies func to the Err value.
    • map_or(default, func): Applies func to Ok or returns default if Err.
    • map_or_else(f_err, f_ok): Applies different functions depending on whether the Result is Ok or Err.
  • Logical Operations:

    • and_(other): Returns the second Result if the first is Ok; otherwise returns the original Err.
    • or_(other): Returns the first Ok, or the second Result if the first is Err.
    • and_then(func): Chains another operation based on the Ok value.
    • or_else(func): Chains another operation based on the Err value.

Option[T]

The Option type expands Python's Optional, representing a value that may or may not be present (Some or None).

from rusty_utils import Option

some_value: Option[int] = Option(42)
none_value: Option[int] = Option()

API Overview

  • Querying Option Type:

    • is_some(): Returns True if the Option contains a value.
    • is_none(): Returns True if the Option contains no value.
  • Unwrapping Values:

    • expect(message): Unwraps the value or raises UnwrapError with a custom message.
    • unwrap(): Unwraps the value or raises UnwrapError.
    • unwrap_or(default): Returns the provided default value if None.
    • unwrap_or_else(func): Returns the result of a provided function if None.
  • Transforming Options:

    • map(func): Transforms the Some value.
    • map_or(default, func): Transforms the Some value or returns a default if None.
    • map_or_else(default_func, func): Transforms the Some value or returns the result of a default function if None.
  • Logical Operations:

    • and_(other): Returns the second Option if the first is Some; otherwise returns None.
    • or_(other): Returns the first Some, or the second Option if the first is None.
    • and_then(func): Chains another operation based on the Some value.
    • or_else(func): Chains another operation based on the None value.

⚙️ Usage Examples

Here are more practical examples of using Result and Option in real-world scenarios.

Example: Handling API Responses

from rusty_utils import Result, Ok, Err


def fetch_data() -> Result[dict, Exception]:
    try:
        # Simulating an API call
        data = {"id": 824, "name": "Kobe Bryant"}
        return Ok(data)
    except Exception as e:
        return Err(e)


result = fetch_data()

if result.is_ok():
    print("Success:", result.unwrap())
else:
    print("Error:", result.unwrap_err())

Example: Safely Unwrapping Values

from rusty_utils import Option


def get_value() -> Option[int]:
    return Option(42)


some_value = get_value()

print(some_value.unwrap_or(0))  # Outputs: 42

For more advanced use cases, consult the full documentation.

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

rusty_utils-0.1.5.tar.gz (9.2 kB view details)

Uploaded Source

Built Distribution

rusty_utils-0.1.5-py3-none-any.whl (8.1 kB view details)

Uploaded Python 3

File details

Details for the file rusty_utils-0.1.5.tar.gz.

File metadata

  • Download URL: rusty_utils-0.1.5.tar.gz
  • Upload date:
  • Size: 9.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.3 CPython/3.11.9 Linux/6.5.0-1025-azure

File hashes

Hashes for rusty_utils-0.1.5.tar.gz
Algorithm Hash digest
SHA256 1a470f74d0cafc9365e1d0b6b73bde11a247e020bae2950370375459dddd36c4
MD5 96d7c8901a7d26fe22d430f05f8b0f7b
BLAKE2b-256 5740048b3dceb8def27e049630728db16db5ef8d1d779d99d5ff92428fae1079

See more details on using hashes here.

Provenance

File details

Details for the file rusty_utils-0.1.5-py3-none-any.whl.

File metadata

  • Download URL: rusty_utils-0.1.5-py3-none-any.whl
  • Upload date:
  • Size: 8.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.3 CPython/3.11.9 Linux/6.5.0-1025-azure

File hashes

Hashes for rusty_utils-0.1.5-py3-none-any.whl
Algorithm Hash digest
SHA256 d6e892c3fce8c7d96121a99d4087235e634f156fab32b94e8a1b31b0edb5c46f
MD5 8089a3e4d42c004140cb71c8cff01271
BLAKE2b-256 5e58a218e77aa4d5b6c3f7f767aca9b1fb600f0553cc3d653976f7170de901c7

See more details on using hashes here.

Provenance

Supported by

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