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 raisedErr
and return to the caller.- What the
@Catch(E)
do is transform the function into a capturer which returnsResult[T, E]
- What the
- We can use the
Catch
in this way (result2) to capture the result of a fallible function call into aResult
. - The
wrapped_side_effect()
function returns aResult
that might be an error (Err
) or a valid value (Ok
).- Since the function returns a
float
and we mark it might raise anException
, so it actually returns aResult[float, Exception]
.
- Since the function returns a
- 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 thewrapped_side_effect
returns aResult[float, Any]
)
API Overview
-
Querying Result Type:
is_ok()
: ReturnsTrue
if theResult
isOk
.is_err()
: ReturnsTrue
if theResult
isErr
.
-
Unwrapping Values:
expect(message)
: Unwraps the value or raisesUnwrapError
with a custom message.unwrap()
: Unwraps the value or raisesUnwrapError
.unwrap_or(default)
: Returns the provided default value ifErr
.unwrap_or_else(func)
: Returns the result of the provided function ifErr
.unwrap_or_raise()
: Raises the exception contained inErr
.
-
Transforming Results:
ok()
: TransformsOk
toOption[T]
err()
: TransformsErr
toOption[E]
map(func)
: Appliesfunc
to theOk
value.map_err(func)
: Appliesfunc
to theErr
value.map_or(default, func)
: Appliesfunc
toOk
or returnsdefault
ifErr
.map_or_else(f_err, f_ok)
: Applies different functions depending on whether theResult
isOk
orErr
.
-
Logical Operations:
and_(other)
: Returns the secondResult
if the first isOk
; otherwise returns the originalErr
.or_(other)
: Returns the firstOk
, or the secondResult
if the first isErr
.and_then(func)
: Chains another operation based on theOk
value.or_else(func)
: Chains another operation based on theErr
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()
: ReturnsTrue
if theOption
contains a value.is_none()
: ReturnsTrue
if theOption
contains no value.
-
Unwrapping Values:
expect(message)
: Unwraps the value or raisesUnwrapError
with a custom message.unwrap()
: Unwraps the value or raisesUnwrapError
.unwrap_or(default)
: Returns the provided default value ifNone
.unwrap_or_else(func)
: Returns the result of a provided function ifNone
.
-
Transforming Options:
map(func)
: Transforms theSome
value.map_or(default, func)
: Transforms theSome
value or returns a default ifNone
.map_or_else(default_func, func)
: Transforms theSome
value or returns the result of a default function ifNone
.
-
Logical Operations:
and_(other)
: Returns the secondOption
if the first isSome
; otherwise returnsNone
.or_(other)
: Returns the firstSome
, or the secondOption
if the first isNone
.and_then(func)
: Chains another operation based on theSome
value.or_else(func)
: Chains another operation based on theNone
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
Built Distribution
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
Algorithm | Hash digest | |
---|---|---|
SHA256 | 1a470f74d0cafc9365e1d0b6b73bde11a247e020bae2950370375459dddd36c4 |
|
MD5 | 96d7c8901a7d26fe22d430f05f8b0f7b |
|
BLAKE2b-256 | 5740048b3dceb8def27e049630728db16db5ef8d1d779d99d5ff92428fae1079 |
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
Algorithm | Hash digest | |
---|---|---|
SHA256 | d6e892c3fce8c7d96121a99d4087235e634f156fab32b94e8a1b31b0edb5c46f |
|
MD5 | 8089a3e4d42c004140cb71c8cff01271 |
|
BLAKE2b-256 | 5e58a218e77aa4d5b6c3f7f767aca9b1fb600f0553cc3d653976f7170de901c7 |