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.4.tar.gz (9.8 kB view details)

Uploaded Source

Built Distribution

rusty_utils-0.1.4-py3-none-any.whl (8.7 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: rusty_utils-0.1.4.tar.gz
  • Upload date:
  • Size: 9.8 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.4.tar.gz
Algorithm Hash digest
SHA256 f056fc1f33988cbbdf1fca59062d91bf948c30a3d0769abe2d1cb842ee26db59
MD5 7070bc283818285a69abc73e673bfe47
BLAKE2b-256 6298851feb33d6234c11fcaac5a50b5524257605192c6f70bf434de5dc8e4988

See more details on using hashes here.

Provenance

File details

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

File metadata

  • Download URL: rusty_utils-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 8.7 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.4-py3-none-any.whl
Algorithm Hash digest
SHA256 a7f88779eead1556dc573c320eeadf663bb7680b5f98b72c37f5a9bce0c7f44b
MD5 7dcb467b19338186b8622bbf0f6032b5
BLAKE2b-256 790079016da9ca3386735a9038736ec987f49262d8d314f9d91add6ad4604381

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