Module funsies.errors
Error for artefact results.
Expand source code
"""Error for artefact results."""
from __future__ import annotations
# std
from dataclasses import dataclass
from enum import Enum
from typing import Callable, Optional, Type, TypeVar, Union
# external
from redis import Redis
# module
from ._constants import ARTEFACTS, hash_t, join
class UnwrapError(Exception):
"""Exception thrown when unwrapping an error."""
pass
class ErrorKind(str, Enum):
"""Kinds of errors."""
# db errors
NotFound = "NotFound"
Mismatch = "Mismatch"
UnresolvedLink = "UnresolvedLink"
# Type errors
WrongType = "WrongType"
JSONEncodingError = "JSONEncodingError"
JSONDecodingError = "JSONDecodingError"
UnknownEncodingError = "UnknownEncodingError"
# Job error conditions
MissingOutput = "MissingOutput"
MissingInput = "MissingInput"
ExceptionRaised = "ExceptionRaised"
JobTimedOut = "JobTimedOut"
NoErrorData = "NoErrorData"
KilledBySignal = "KilledBySignal"
@dataclass
class Error:
"""An Error value for artefacts."""
kind: ErrorKind
source: Optional[hash_t] = None
details: Optional[str] = None
def put(self: "Error", db: Redis[bytes], hash: hash_t) -> None:
"""Save an Error to Redis."""
data = dict(kind=self.kind.name)
if self.source:
data["source"] = str(self.source)
if self.details:
data["details"] = str(self.details)
db.hset( # type:ignore
join(ARTEFACTS, hash, "error"),
mapping=data, # type:ignore
)
@classmethod
def grab(cls: Type["Error"], db: Redis[bytes], hash: hash_t) -> "Error":
"""Grab an Error from the Redis store."""
if not join(ARTEFACTS, hash, "error"):
raise RuntimeError(f"No error for artefact at {hash}")
data = db.hgetall(join(ARTEFACTS, hash, "error"))
kind = ErrorKind(data[b"kind"].decode())
# Sometimes the python boilerplate is really freaking annoying...
tmp = data.get(b"source", None)
if tmp is not None:
source: Optional[hash_t] = hash_t(tmp.decode())
else:
source = None
tmp = data.get(b"details", None)
if tmp is not None:
details: Optional[str] = tmp.decode()
else:
details = None
return Error(kind, source=source, details=details)
# A simple mypy result type
T = TypeVar("T")
Result = Union[Error, T]
"""Result contains either a value or an `Error` instance.
`Result[T]` is a type hint that corresponds to `Union[T, Error]`. This is only
a an abstraction: at runtime, a `Result[T]` is just `T` or `Error`. That is,
`Result` has no runtime representation.
"""
def unwrap(it: Result[T]) -> T:
"""Unwrap a `errors.Result` type.
Unwrap `errors.Result[T]` and return `T`. If `errors.Result[T]` is of type
`Error`, this function raises `errors.UnwrapError`.
Args:
it: An object of type `errors.Result[T]`.
Returns:
The value of it with type `T`.
Raises:
UnwrapError: `errors.Result[T]` is an `errors.Error` instance.
"""
if isinstance(it, Error):
raise UnwrapError(
f"data is errored: kind={it.kind}"
+ f"\nsource={it.source}"
+ f"\ndetails={it.details}"
)
else:
return it
T1 = TypeVar("T1")
T2 = TypeVar("T2")
def match(
result: Result[T], some: Callable[[T], T1], none: Callable[[Error], T2]
) -> Union[T1, T2]:
"""Pattern matching on Results."""
if isinstance(result, Error):
return none(result)
else:
return some(result)
Global variables
var Result
Functions
def unwrap(it: Result[T]) ‑> ~T-
Unwrap a
errors.Resulttype.Unwrap
errors.Result[T]and returnT. Iferrors.Result[T]is of typeError, this function raiseserrors.UnwrapError.Args
it- An object of type
errors.Result[T].
Returns
The value of it with type
T.Raises
UnwrapErrorerrors.Result[T]is anerrors.Errorinstance.
Expand source code
def unwrap(it: Result[T]) -> T: """Unwrap a `errors.Result` type. Unwrap `errors.Result[T]` and return `T`. If `errors.Result[T]` is of type `Error`, this function raises `errors.UnwrapError`. Args: it: An object of type `errors.Result[T]`. Returns: The value of it with type `T`. Raises: UnwrapError: `errors.Result[T]` is an `errors.Error` instance. """ if isinstance(it, Error): raise UnwrapError( f"data is errored: kind={it.kind}" + f"\nsource={it.source}" + f"\ndetails={it.details}" ) else: return it def match(result: Result[T], some: Callable[[T], T1], none: Callable[[Error], T2]) ‑> Union[~T1, ~T2]-
Pattern matching on Results.
Expand source code
def match( result: Result[T], some: Callable[[T], T1], none: Callable[[Error], T2] ) -> Union[T1, T2]: """Pattern matching on Results.""" if isinstance(result, Error): return none(result) else: return some(result)
Classes
class UnwrapError (*args, **kwargs)-
Exception thrown when unwrapping an error.
Expand source code
class UnwrapError(Exception): """Exception thrown when unwrapping an error.""" passAncestors
- builtins.Exception
- builtins.BaseException
class ErrorKind (value, names=None, *, module=None, qualname=None, type=None, start=1)-
Kinds of errors.
Expand source code
class ErrorKind(str, Enum): """Kinds of errors.""" # db errors NotFound = "NotFound" Mismatch = "Mismatch" UnresolvedLink = "UnresolvedLink" # Type errors WrongType = "WrongType" JSONEncodingError = "JSONEncodingError" JSONDecodingError = "JSONDecodingError" UnknownEncodingError = "UnknownEncodingError" # Job error conditions MissingOutput = "MissingOutput" MissingInput = "MissingInput" ExceptionRaised = "ExceptionRaised" JobTimedOut = "JobTimedOut" NoErrorData = "NoErrorData" KilledBySignal = "KilledBySignal"Ancestors
- builtins.str
- enum.Enum
Class variables
var NotFoundvar Mismatchvar UnresolvedLinkvar WrongTypevar JSONEncodingErrorvar JSONDecodingErrorvar UnknownEncodingErrorvar MissingOutputvar MissingInputvar ExceptionRaisedvar JobTimedOutvar NoErrorDatavar KilledBySignal
class Error (kind: ErrorKind, source: Optional[hash_t] = None, details: Optional[str] = None)-
An Error value for artefacts.
Expand source code
@dataclass class Error: """An Error value for artefacts.""" kind: ErrorKind source: Optional[hash_t] = None details: Optional[str] = None def put(self: "Error", db: Redis[bytes], hash: hash_t) -> None: """Save an Error to Redis.""" data = dict(kind=self.kind.name) if self.source: data["source"] = str(self.source) if self.details: data["details"] = str(self.details) db.hset( # type:ignore join(ARTEFACTS, hash, "error"), mapping=data, # type:ignore ) @classmethod def grab(cls: Type["Error"], db: Redis[bytes], hash: hash_t) -> "Error": """Grab an Error from the Redis store.""" if not join(ARTEFACTS, hash, "error"): raise RuntimeError(f"No error for artefact at {hash}") data = db.hgetall(join(ARTEFACTS, hash, "error")) kind = ErrorKind(data[b"kind"].decode()) # Sometimes the python boilerplate is really freaking annoying... tmp = data.get(b"source", None) if tmp is not None: source: Optional[hash_t] = hash_t(tmp.decode()) else: source = None tmp = data.get(b"details", None) if tmp is not None: details: Optional[str] = tmp.decode() else: details = None return Error(kind, source=source, details=details)Class variables
var kind : ErrorKindvar source : Optional[hash_t]var details : Optional[str]
Static methods
def grab(db: Redis[bytes], hash: hash_t) ‑> 'Error'-
Grab an Error from the Redis store.
Expand source code
@classmethod def grab(cls: Type["Error"], db: Redis[bytes], hash: hash_t) -> "Error": """Grab an Error from the Redis store.""" if not join(ARTEFACTS, hash, "error"): raise RuntimeError(f"No error for artefact at {hash}") data = db.hgetall(join(ARTEFACTS, hash, "error")) kind = ErrorKind(data[b"kind"].decode()) # Sometimes the python boilerplate is really freaking annoying... tmp = data.get(b"source", None) if tmp is not None: source: Optional[hash_t] = hash_t(tmp.decode()) else: source = None tmp = data.get(b"details", None) if tmp is not None: details: Optional[str] = tmp.decode() else: details = None return Error(kind, source=source, details=details)
Methods
def put(self: "'Error'", db: Redis[bytes], hash: hash_t) ‑> None-
Save an Error to Redis.
Expand source code
def put(self: "Error", db: Redis[bytes], hash: hash_t) -> None: """Save an Error to Redis.""" data = dict(kind=self.kind.name) if self.source: data["source"] = str(self.source) if self.details: data["details"] = str(self.details) db.hset( # type:ignore join(ARTEFACTS, hash, "error"), mapping=data, # type:ignore )