Result
Result[T, Error]
👉 A discriminated union that encapsulates successful outcome with a value of type T or a failure with an arbitrary Error exception.
Functions¶
Functions | Definition |
---|---|
unwrap() |
Returns the encapsulated value if this instance is a success or None if it is failure. |
unwrap_or_raise() |
Returns the encapsulated value if this instance is a success or raise the encapsulated exception if it is failure. |
unwrap_or_return() |
Returns the encapsulated value if this instance is a success or return Result as long as @early_return decorator wraps the function. |
unwrap_or(failure_value) |
Returns the encapsulated value if this instance is a success or the selected failure_value if it is failure. |
reraise() |
Raises the encapsulated failure value if this instance derive from Error or BaseException. |
map() |
Modifies encapsulate value applying a mapper function. |
unwrap_or_else(on_failure_handler) |
Returns the encapsulated value if this instance is a success or execute the on_failure_handler when it is failure. |
unwrap_and(on_success_handler) |
Returns the encapsulated value if this instance is a success and execute the on_success_handler when it is success. |
handle(on_success_handler,on_failure_handler) |
Returns itself and execute the on_success_handler when the instance is a success and the on_failure_handler when it is failure. |
bind(func) |
Returns itself binding success value with input func |
transform() |
Transform the result with a transformer function. You can give the transformer callable or use the set_transformer function to pre-set the callable to be used. |
Properties¶
Properties | Definition |
---|---|
value |
Returns the encapsulated value whether it's success or failure |
is_success |
Returns true if this instance represents successful outcome. In this case is_failure returns false. |
is_failure |
Returns true if this instance represents failed outcome. In this case is_success returns false |
Introduction¶
Let's imagine we have a dictionary that represent a user info data and we use the string_from_key
(presented in the Getting Started section before) to retrieve the first name of the user.
user_info = {"first_name": "Rosalia", "last_name": "De Castro", "age": 60}
result = string_from_key(dictionary=user_info, key="first_name")
# ➡ Result will be Result[status: success | value: Rosalia]
You could also check the status of the result
is_success = result.is_success
# ➡️ It will return True
is_failure = result.is_failure
# ➡️ It will return False
If the result is a success you can get the expected value accessing the property value
or using the unwrap
function (recommended).
my_value = result.value
# ➡️ It will return Rosalia
my_value = result.unwrap()
# ➡️ It will return also Rosalia
Otherwise, if we try to access an invalid key or a non string value, returned result will be a failure.
result = string_from_key(dictionary=user_info, key="invalid_key")
# ➡ Result will be Result[status: failure | value: NoSuchKey]
is_success = result.is_success
# ➡️ It will return False
is_failure = result.is_failure
# ➡️ It will return True
my_value = result.value
# ➡️ It will return NoSuchKey (Error)
my_value = result.unwrap()
# ➡️ It will return also NoSuchKey (Error)
Or
result = string_from_key(dictionary=user_info, key="age")
# ➡ Result will be Result[status: failure | value: TypeMismatch]
is_success = result.is_success
# ➡️ It will return False
is_failure = result.is_failure
# ➡️ It will return True
my_value = result.value
# ➡️ It will return TypeMismatch (Error)
my_value = result.unwrap()
# ➡️ It will return also TypeMismatch (Error)
Detail¶
We will present some tips and examples to see in detail what meiga can offer us.
unwrap
¶
Returns the encapsulated value if this instance is a success or None if it is failure.
Example
If you unwrap
a Result object, it will return a valid value if it is success. Otherwise, it will return None.
result = Result(success="Hi!")
value = result.unwrap() # ➡️ It will return "Hi!"
result = Failure(Error())
value = result.unwrap() # ➡️ It will return None
See tests/unit/test_result_unwrap.py to see examples of usage.
unwrap_or_raise
¶
Returns the encapsulated value if this instance is a success or raises the encapsulated exception if it is failure.
Example
unwrap_or_return
¶
Returns the encapsulated value if this instance is a success or return Result as long as @early_return
decorator wraps the function.
Example
Use unwrap_or_return
in combination with @early_return
decorator.
If something wrong happens unwrapping your Result
, the unwrap_or_return
function will raise an controlled Exception (WaitingForEarlyReturn
).
@early_return
decorator will handle the exception and unwrap the value in case of success.
The following example illustrate this:
from meiga import Result, Error, early_return
@early_return
def handling_result(key: str) -> Result:
user_info = {"first_name": "Rosalia", "last_name": "De Castro", "age": 60}
first_name = string_from_key(dictionary=user_info, key=key).unwrap_or_return()
# Do whatever with the name
name = first_name.lower()
return Result(success=name)
If key is valid success value would be returned. Otherwise, an Error would be returned.
If you need to return a specific value if fails, you can do it with meiga:
unwrap_or¶
Returns the encapsulated value if this instance is a success or the selected failure_value if it is failure.
Example
reraise
¶
Raises the encapsulated failure value if this instance inherits from Error or BaseException.
Example
map¶
Modifies encapsulate value applying a mapper function.
Example
unwrap_or_else¶
Returns the encapsulated value if this instance is a success or execute the on_failure_handler when it is failure.
Example
unwrap_and¶
Returns the encapsulated value if this instance is a success and execute the on_success_handler when it is success.
Example
handle¶
Returns itself and execute the on_success_handler when the instance is a success and the on_failure_handler when it is failure.
Example
You can call another function after evaluate the result. Use optional parameters success_handler and failure_handler (Callable functions).
from meiga import OnSuccessHandler, OnFailureHandler
def success_handler():
print("Do my successful stuff here!")
def failure_handler():
print("Do my failure stuff here!")
result = string_from_key(dictionary=user_info, key="first_name")
result.handle(
on_success_handler=OnSuccessHandler(func=success_handler),
on_failure_handler=OnFailureHandler(func=failure_handler)
)
Tip: Additional parameters
If you need to add some arguments as a parameters, use success_args and failure_args:
from meiga import OnSuccessHandler, OnFailureHandler
def success_handler(param_1):
print(f"param_1: {param_1}")
def failure_handler(param_1, param_2):
print(f"param_1: {param_1}")
print(f"param_2: {param_2}")
result = string_from_key(dictionary=user_info, key="first_name")
result.handle(
on_success_handler=OnSuccessHandler(func=success_handler, args=(1,)),
on_failure_handler=OnFailureHandler(func=failure_handler, args=(1, 2))
)
Tip: Additional parameters in combination with the Result itself
Sometimes a handle function will need information about external parameters and also about the result itself. Now, is possible this combination thanks to Result.__id__
identifier.
from meiga import Result, Error, OnSuccessHandler, OnFailureHandler
args = (1, Result.__id__, 2)
def success_handler(param_1: int, result: Result, param_2: int):
assert param_1 == 1
assert isinstance(result, Result)
assert result.value is True
assert param_2 == 2
def failure_handler(param_1: int, result: Result, param_2: int):
assert param_1 == 1
assert isinstance(result, Result)
assert result.value == Error()
assert param_2 == 2
def run(result: Result):
result.handle(
on_success_handler=OnSuccessHandler(func=success_handler, args=args),
on_failure_handler=OnFailureHandler(func=failure_handler, args=args)
)
run(result)
bind¶
Returns itself binding success value with input func
Question
What's the difference with handle?
It's quite similar but simpler. Bind only be applied to success value and don't accept external arguments This function is very convenient for chaining actions on a result.
from typing import Any
from meiga import Success
user = {"name": "rosalia de castro", "age": 186}
result = Success(user)
def upper_name(value: Any) -> Any:
value.update({"name": value["name"].upper()})
return value
def update_age(value: Any) -> Any:
value.update({"age": value["age"] + 1})
return value
def add_location(value: Any) -> Any:
value.update({"location": "GALIZA"})
return value
result = (
result
.bind(upper_name)
.bind(update_age)
.bind(add_location)
)
transform
¶
Transform the result with a transformer function. You can give the transformer callable or use the set_transformer
function to pre-set the callable to be used.
You can define a transformer to capitalize the value in case of success and raise an
from meiga import Result, Success, Failure
def transformer(result: Result) -> tuple[int, str]:
match result:
case Success(value):
return 200, value.capitalize()
case Failure(error):
raise 500, "error"
result = Success("value")
status_code, message = result.transform(transformer)
In addition, you can set a transformer in an inner function that will be used in the future to transform the Result
.
from meiga import Result, Error, Success, Failure
def my_controller() -> Result[str, Error]
def transformer(result: Result) -> tuple[int, str]:
match result:
case Success(value):
return 200, value.capitalize()
case Failure(error):
raise 500, "error"
result = Success("value")
result.set_transformer(transformer)
result = my_controller()
status_code, message = result.transform() # (1)
- Use
trasformer
function set withset_transformer
.
match
¶
Python > 3.10
If you are using Python 3.10 or above, you can take advantage of new syntax proposed in PEP 636 – Structural Pattern Matching to handle the result.
from __future__ import annotations
from meiga import Error, Failure, Result, Success
class NoSuchKey(Error): ...
class TypeMismatch(Error): ...
def string_from_key(
dictionary: dict, key: str
) -> Result[str, NoSuchKey | TypeMismatch]:
if key not in dictionary.keys():
return Failure(NoSuchKey())
value = dictionary[key]
if not isinstance(value, str):
return Failure(TypeMismatch())
return Success(value)
dictionary = {"key1": "value", "key2": 2}
for key in ["key1", "key2", "key3"]:
result = string_from_key(dictionary=dictionary, key=key)
match result:
case Success(_):
print(f"Success")
case Failure(NoSuchKey()):
print("Failure with NoSuchKey")
case Failure(TypeMismatch()):
print("Failure with TypeMismatch")
case _:
print("default")
Warning
If are using Result(success="my_success")
and Result(failure=NoSuchKey())
syntax intead of recommended one
with Success
and Failure
aliases, you have to use a different match pattern.
You would have to use something like:
match result:
case Result(str(), _):
print(f"Success")
case Result(_, NoSuchKey()):
print("Failure with NoSuchKey")
case Result(_, TypeMismatch()):
print("Failure with TypeMismatch")
case _:
print("default")
Check this closed issue to learn more about the PEP 636
and this usage in the meiga library.
Deprecated ⚠️¶
throw
¶
Throws the encapsulated failure value if this instance inherits from Error or BaseException.
Example
unwrap_or_throw¶
Returns the encapsulated value if this instance is a success or throws the encapsulated exception if it is failure.