Skip to content

Instantly share code, notes, and snippets.

@jhidding
Last active January 13, 2020 13:41
Show Gist options
  • Save jhidding/523f917d00d5bb1c3a6bc00cdc34090d to your computer and use it in GitHub Desktop.
Save jhidding/523f917d00d5bb1c3a6bc00cdc34090d to your computer and use it in GitHub Desktop.
A function decorator for failing computations
from functools import (wraps)
from itertools import (chain)
class Failure:
"""Signifies a failure in a computation that was wrapped by a `@maybe`
decorator."""
def __init__(self, func, fails=None, exception=None):
self.name = func.__name__
self.fails = fails
self.exception = exception
self.trace = []
def __bool__(self):
"""Failures are falsy."""
return False
def add_trace(self, func):
self.trace.append(func.__name__)
def __str__(self):
msg = "Fail: " + " -> ".join(self.trace + [self.name])
if self.exception is not None:
msg += "\n* {}: ".format(type(self.exception).__name__)
msg += "\n ".join(l for l in str(self.exception).split('\n'))
elif self.fails:
msg += "\n* failed arguments:\n "
msg += "\n ".join(
"{} `{}` ".format(func, source) + "\n ".join(
l for l in str(fail).split('\n'))
for func, source, fail in self.fails)
return msg
def is_fail(obj):
"""Checks if an object is a Failure."""
return isinstance(obj, Failure)
def maybe(func):
"""Function decorator. Makes a function failt-tolerant by wrapping
the function call in a `try/except` block and checking arguments for
previous failures."""
@wraps(func)
def maybe_wrapped(*args, **kwargs):
# filter for all failures
fails = [(func.__name__, k, v) for k, v in
chain(enumerate(args), kwargs.items())
if is_fail(v)]
if fails:
return Failure(func, fails=fails)
try:
result = func(*args, **kwargs)
except Exception as exc:
return Failure(func, exception=exc)
else:
if is_fail(result):
result.trace.append(func)
return result
return maybe_wrapped
from maybe import maybe, is_fail
import math
@maybe
def reciprocal(x):
return 1 / x
@maybe
def square_root(x):
return math.sqrt(x)
print(square_root(reciprocal(0)))
print(square_root(reciprocal(-1)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment