Skip to main content

No project description provided

Project description

DrResult

More radical approach to Rust's std::result in Python.

Motivation

I do not want exceptions in my code. Rust has this figured out quite neatly by essentially revolving around two pathways for errors: A possible error condition is either one that has no prospect of being handled -- then the program should terminate -- or it is one that could be handled -- then it has to be handled or explicitly ignored.

This concept is replicated here by using AssertionError to emulate Rust's panic!, mapping all unhandled exceptions to AssertionError and providing a Rust-like result type to signal error conditions that do not need to terminate the program.

Documentation

Basic Usage

from rust_result import Ok, Err, returns_result

@returns_result()
def read_file():
    with open('/this/path/is/invalid') as f:
        return Ok(f.read())

result = func()

This will raise an AssertionError due to an unhandled exception.

But if you specify the exceptions you expect, you can handle the error:

@returns_result(FileNotFoundError)
def read_file():
    with open('/this/path/is/invalid') as f:
        return Ok(f.read())

result = func()
if result.is_ok():
    print(f'File content: {result.unwrap()}')
else:
    print(f'Error: {result.unwrap_err()}')

Or -- if you are feeling fancy -- you can do pattern matching:

@returns_result(FileNotFoundError)
def read_file():
    with open('/this/path/is/invalid') as f:
        return f.read()

result = read_file()
match result:
    case Ok(v):
        print(f'File content: {v}')
    case Err(e):
        print(f'Error: {e}')

And even fancier:

data = [
    { 'foo': 'value-1' },
    { 'bar': 'value-2' }
]

@returns_result(IndexError, KeyError)
def retrieve_record_entry_backend(index, key):
    return Ok(data[index][key])

def retrieve_record_entry(index, key):
    match retrieve_record_entry_backend(index, key):
        case Ok(v):
            print(f'Retrieved: {v}')
        case Err(IndexError()):
            print(f'No such record: {index}')
        case Err(KeyError()):
            print(f'No entry `{key}` in record {index}')

retrieve_record_entry(2, 'foo')    # No such record: 2
retrieve_record_entry(1, 'foo')    # No entry `foo` in record 1
retrieve_record_entry(1, 'bar')    # Retrieved: value-2

Typing

from typing import NoReturn
from rust_result import Result, Ok, Err, returns_result

data = [{'foo': 'value-1'}, {'bar': 'value-2'}]


@returns_result(IndexError, KeyError)
def retrieve_record_entry_backend(index: int, key: str) -> Result[str, int]:
    if key == 'baz':
        return Err(1)
    return Ok(data[index][key])


def retrieve_record_entry(index: int, key: str) -> str:
    match retrieve_record_entry_backend(index, key):
        case Ok(v):
            return f'Retrieved: {v}'
        case Err(IndexError()):
            return f'No such record: {index}'
        case Err(KeyError()):
            return f'No entry `{key}` in record {index}'
        case Err(e):
            return f'Error: {e}'
        case _:
            assert False


print(retrieve_record_entry(2, 'foo'))
print(retrieve_record_entry(1, 'foo'))
print(retrieve_record_entry(1, 'bar'))
print(retrieve_record_entry(1, 'baz'))

As you can see from this example, the @returns_result decorator extends the return type of the function from Result[T, E] to Result[T, E | Exception].

Similar Projects

For a less extreme approach on Rust's result type, see:

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

drresult-0.2.1.tar.gz (3.7 kB view hashes)

Uploaded Source

Built Distribution

drresult-0.2.1-py3-none-any.whl (4.3 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