mark's result class for safe value retrieval
Project description
mares
mark's result class for safe data retrieval
or, as i learnt from opus 4.5, a railway-oriented two-track result pattern for explicit success/failure handling
the class
@dataclass(frozen=True, slots=True)
class Result(Generic[T]):
"""
`dataclasses.dataclass` representing a result for safe value retrieval
attributes:
`value: T`
value to return or fallback value if erroneous
`error: BaseException | None = None`
exception if any
methods:
`def __bool__(self) -> bool: ...`
method for boolean comparison for exception safety
`def get(self) -> T: ...`
method that raises or returns an error if the Result is erroneous
`def map(self, func: Callable[[T], U]) -> Result[U]: ...`
method that maps the value when not erroneous
`def bind(self, func: Callable[[T], Result[U]]) -> Result[U]: ...`
method that binds to another Result-returning function
`def cry(self, string: bool = False) -> str: ...`
method that returns the result value or raises an error
"""
value: T
error: BaseException | None = None
def __bool__(self) -> bool:
"""
method for boolean comparison for easier exception handling
returns: `bool`
that returns True if `self.error` is not None
"""
return self.error is None
def cry(self, string: bool = False) -> str: # noqa: FBT001, FBT002
"""
method that raises or returns an error if the Result is erroneous
arguments:
`string: bool = False`
if `self.error` is an Exception, returns it as a string
error message
returns: `str`
returns `self.error` as a string if `string` is True,
or returns an empty string if `self.error` is None
"""
if isinstance(self.error, BaseException):
if string:
message = f"{self.error}"
name = self.error.__class__.__name__
return f"{message} ({name})" if (message != "") else name
raise self.error
return ""
def get(self) -> T:
"""
method that returns the result value or raises an error
returns: `T`
returns `self.value` if `self.error` is None
raises: `BaseException`
if `self.error` is not None
"""
if self.error is not None:
raise self.error
return self.value
def map(self, func: Callable[[T], U]) -> "Result[U]":
"""
method that maps the value when not erroneous
arguments:
`func: Callable[[T], U]`
function to transform the value
returns: `Result[U]`
returns a new Result with the transformed value, or the same error
"""
if self.error is not None:
return Result(cast(U, self.value), error=self.error)
return Result(func(self.value))
def bind(self, func: Callable[[T], "Result[U]"]) -> "Result[U]":
"""
method that binds to another Result-returning function
arguments:
`func: Callable[[T], Result[U]]`
function to transform the value into a Result
returns: `Result[U]`
returns the bound Result, or the same error
"""
if self.error is not None:
return Result(cast(U, self.value), error=self.error)
return func(self.value)
@staticmethod
def wrap(default: R) -> Callable[[Callable[P, R]], Callable[P, "Result[R]"]]:
"""decorator that wraps a non-Result-returning function to return a Result"""
def result_decorator(func: Callable[P, R]) -> Callable[P, Result[R]]:
@wraps(func)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> Result[R]:
try:
return Result(func(*args, **kwargs))
except Exception as exc:
return Result(default, error=exc)
return wrapper
return result_decorator
an example
def fetch_json(url: str) -> Result[dict[str, Any]]:
try:
with urllib.request.urlopen(url, timeout=10) as response:
data = response.read().decode("utf-8")
return Result(json.loads(data))
except (OSError, ValueError, json.JSONDecodeError) as exc:
return Result({}, error=exc)
def require_field(data: dict[str, Any], field: str) -> Result[dict[str, Any]]:
if field not in data:
return Result({}, error=KeyError(f"Missing field: {field}"))
return Result(data)
return (
fetch_json(f"https://jsonplaceholder.typicode.com/users/{randint(1, 10)}")
.bind(lambda payload: require_field(payload, "name"))
.map(lambda payload: str(payload["name"]))
)
bonus! this is available as a library, and as a quick insertion tool.
pip install mares
uv install mares
uvx mares inject path/to/file.py
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
mares-2026.3.2.tar.gz
(6.5 kB
view details)
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mares-2026.3.2.tar.gz.
File metadata
- Download URL: mares-2026.3.2.tar.gz
- Upload date:
- Size: 6.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.28 {"installer":{"name":"uv","version":"0.9.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f5c61bcb5a81b909e5286ed0f409abb8caed6cd9f1c6486217a58c02eb3758e8
|
|
| MD5 |
0e43bb0cf8e2345e689d1a53e11d9f98
|
|
| BLAKE2b-256 |
e2f8715ce9a7940e64162b2b6028ed28a1109300dd05127055be3a0e68a090bc
|
File details
Details for the file mares-2026.3.2-py3-none-any.whl.
File metadata
- Download URL: mares-2026.3.2-py3-none-any.whl
- Upload date:
- Size: 9.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.28 {"installer":{"name":"uv","version":"0.9.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a885d58fe25634d81ba439996b72f1025bf7860e343ecdfeca40c064afb5b8a9
|
|
| MD5 |
fdd5a1cc8f65e578206bd4709fd65cec
|
|
| BLAKE2b-256 |
5d813a093c8572650b45d2d9ba3203f13db65bb1780b62dd3cdce8d866cdc612
|