Skip to main content

Tangled Result

Project description

Build Status Stable Version Coverage Python License

thresult

Python Result library for handling returned values (Result, Ok, Err) from functions/methods and handling errors. It is error handling library which is alternative to try/except style of programming.

It is inspired by great Rust Result, Ok, Err types.

Installation

pip install thresult

Simple Usage

Traditional try-except example

def div(x: float, y: float) -> float:
    z: float = x / y
    return z


z0: float = div(1.0, 2.0) # 0.5
z1: float = div(1.0, 0.0) # raises "ZeroDivisionError: division by zero" exception

Unwrapping Result as Context Manager

from thresult import Result


@Result
def div(x: float, y: float) -> float:
    z: float = x / y
    return z


try:
    with div(1.0, 0.0) as z:
        # unreachable
        r: float = z
except ZeroDivisionError as e:
    # exception happened
    pass

Unwrapping Result in unwrap Context Manager

from thresult import Result, unwrap


@Result
def f(x: float, y: float) -> float:
    z: float = x / y
    return z


try:
    with unwrap():
        # unreachable
        r: float = f(1, 0)
except ZeroDivisionError as e:
    # exception happened
    raise e

Pattern-matching Result using match-case

from thresult import Result, Ok, Err


@Result
def div(x: float, y: float) -> float:
    # can raise "ZeroDivisionError: division by zero" exception
    z: float = x / y
    return z


r: Result = div(1.0, 2.0) # Ok[float](0.5)

match r:
    case Ok(v):
        assert v == 0.5
    case Err(e):
        # unreachable
        # "ZeroDivisionError: division by zero"
        assert isinstance(e, ZeroDivisionError)

In-place pattern-matching Result using walrus operator and match-case

@Result
def div(x: float, y: float) -> float:
    # can raise "ZeroDivisionError: division by zero" exception
    z: float = x / y
    return z


match r := div(1.0, 2.0): # Ok[float](0.5)
    case Ok(v):
        assert v == 0.5
    case Err(e):
        # unreachable
        # "ZeroDivisionError: division by zero"
        assert isinstance(e, ZeroDivisionError)

Advanced Usage

Unwrapping Result[float, ZeroDivisionError] as Context Manager

from thresult import Result, Ok, Err


@Result[float, ZeroDivisionError]
def div(x: float, y: float) -> float:
    z: float = x / y
    return z


try:
    with div(1.0, 0.0) as z:
        # unreachable 
        pass
except ZeroDivisionError as e:
    # exception happened
    pass

Manually create Result value, and Structural Pattern Matching

from thresult import Result, Ok, Err


def div(x: float, y: float) -> Result[float, ZeroDivisionError]:
    res: Result[float, ZeroDivisionError]

    try:
        # can raise "ZeroDivisionError: division by zero" exception
        z: float = x / y
        res = Ok[float](z)
    except ZeroDivisionError as e:
        res = Err[ZeroDivisionError](e)

    return res


r0: Result = div(1.0, 2.0) # Ok
r1: Result = div(1.0, 0.0) # Err

match r0:
    case Ok(v):
        print('Ok, value:', v)
    case Err(e):
        print('Err, error:', e) # "ZeroDivisionError: division by zero"

match r1:
    case Ok(v):
        print('Ok, value:', v)
    case Err(e):
        print('Err, error:', e) # "ZeroDivisionError: division by zero"

z0: float = r0.unwrap() # 0.5
z1: float = r1.unwrap_or(float('inf')) # inf
z1: float = r1.unwrap() # raises "ZeroDivisionError: division by zero" exception

Decorate function with Result, and Structural Pattern Matching

from thresult import Result, Ok, Err


@Result[float, ZeroDivisionError]
def div(x: float, y: float) -> float:
    # can raise "ZeroDivisionError: division by zero" exception
    z: float = x / y
    return z


r0: Result = div(1.0, 2.0) # Ok
r1: Result = div(1.0, 0.0) # Err

match r0:
    case Ok(v):
        print('Ok, value:', v)
    case Err(e):
        print('Err, error:', e) # "ZeroDivisionError: division by zero"

match r1:
    case Ok(v):
        print('Ok, value:', v)
    case Err(e):
        print('Err, error:', e) # "ZeroDivisionError: division by zero"

z0: float = r0.unwrap() # 0.5
z1: float = r1.unwrap_or(float('inf')) # inf
z1: float = r1.unwrap() # raises "ZeroDivisionError: division by zero" exception

Run / Develop Cycle

docker run --rm -it -v $PWD/thresult:/code -w /code python:3.11-alpine python -B result.py

Testing

docker-compose build thresult-test ; docker-compose run --rm -v $PWD:/test thresult-test

Coverage

docker-compose build thresult-coverage ; docker-compose run --rm -v $PWD:/test thresult-coverage

Building

docker-compose build thresult-build ; docker-compose run --rm thresult-build

Licensing

thresult is licensed under the BSD 3 license.

Check the LICENSE for details.

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

thresult-0.9.25.tar.gz (7.1 kB view hashes)

Uploaded Source

Built Distribution

thresult-0.9.25-py3-none-any.whl (6.7 kB view hashes)

Uploaded Python 3

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