πŸ§™ A simple, typed and monad-based Result type for Python.

Overview

meiga πŸ§™ version ci pypi

A simple, typed and monad-based Result type for Python.

Table of Contents

Installation πŸ’»

pip install meiga

Getting Started πŸ“ˆ

meiga πŸ§™ provides a simple and clear way of handling errors in Python without using Exceptions. This package can help you to dry your code helping on modeling the output of your classes and method.

This package provides a new type class, the Result[Type, Type] This Result type allows to simplify a wide range of problems, like handling potential undefined values, or reduce complexity handling exceptions. Additionally, code can be simplified following a semantic pipeline reducing the visual noise of checking data types, controlling runtime flow and side-effects.

This package is based in another solutions from another modern languages as this swift-based Result implementation.

Example

The best way to illustrate how meiga can help you is with an example.

Consider the following example of a function that tries to extract a String (str) for a given key from a Dict.

from meiga import Result, Error


class NoSuchKey(Error):
    pass


class TypeMismatch(Error):
    pass


def string_from_key(dictionary: dict, key: str) -> Result[str, Error]:
    if key not in dictionary.keys():
        return Result(failure=NoSuchKey())

    value = dictionary[key]
    if not isinstance(value, str):
        return Result(failure=TypeMismatch())

    return Result(success=value)

Returned value Result type provides a robust wrapper around the functions and methods. Rather than throw an exception, it returns a Result that either contains the str value for the given key, or an typed Error detailing what went wrong (Result[str, Error]).

Features

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
throw() Throws the encapsulated failure value if this instance derive from Error or BaseException.
unwrap() Returns the encapsulated value if this instance represents success or None if it is failure.
unwrap_or_throw() Returns the encapsulated value if this instance represents success or throws the encapsulated exception if it is failure.
unwrap_or_return() Returns the encapsulated value if this instance represents success or return Result as long as @meiga decorator wraps the function.
unwrap_or(failure_value) Returns the encapsulated value if this instance represents success or the selected failure_value if it is failure.
unwrap_or_else(on_failure) Returns the encapsulated value if this instance represents success or execute the on_failure function when it is failure.
unwrap_and(on_success) Returns the encapsulated value if this instance represents success and execute the on_success function when it is success.
handle(on_success,on_failure) Returns itself and execute the on_successfunction when the instance represemts success and the on_failure function when it is failure.
map(transform) Returns a transformed result applying transform function applied to encapsulated value if this instance represents success or failure

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

Let's image we have a dictionary that represent a user info data

>>> user_info = {"first_name": "Rosalia", "last_name": "De Castro", "age": 60}

And we try to obtain first_name

>>> result = string_from_key(dictionary=user_info, key="first_name")
Result[status: success | value: Rosalia]

You can check the status of the result

>>> result.is_success
True
>>> result.is_failure
False

If the result is a success you can get the expected value

>>> result.value
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[status: failure | value: NoSuchKey]
>>> result.is_failure
True
>>> result.value
NoSuchKey() // Error 

Or

>>> result = string_from_key(dictionary=user_info, key="age")
Result[status: failure | value: TypeMismatch]
>>> result.is_failure
True
>>> result.value
TypeMismatch() // Error 

Alias

Use meiga aliases to improve the semantics of your code.

For success result you can use:

result = Result(success="Rosalia")
result = Success("Rosalia") # it is equivalent

If return value is a bool you can use:

result = Success()
result = Success(True)
result = isSuccess

For failure results:

class NoSuchKey(Error):
    pass

result = Result(failure=NoSuchKey())
result = Failure(NoSuchKey())

If you don't want to specify the error, you can use default value with:

result = Failure()
result = Failure(Error())
result = isFailure # Only valid for a failure result with non-specific Error() value

Bringing previous example back. that is the way you can use the alias:

from meiga import Result, Error, Success, Failure,


class NoSuchKey(Error):
    pass


class TypeMismatch(Error):
    pass


def string_from_key(dictionary: dict, key: str) -> Result[str, Error]:
    if key not in dictionary.keys():
        return Failure(NoSuchKey())

    value = dictionary[key]
    if not isinstance(value, str):
        return Failure(TypeMismatch())

    return Success(value)

Furthermore, there is a available a useful alias: NotImplementedMethodError

Use it when define abstract method that returns Result type

from meiga import Result, Error, NotImplementedMethodError

from abc import ABCMeta, abstractmethod

class AuthService:

    __metaclass__ = ABCMeta

    @abstractmethod
    def __init__(self, base_url: str):
        self.base_url = base_url

    @abstractmethod
    def create_token(self, client: str, client_id: str) -> Result[str, Error]:
        return NotImplementedMethodError

Advance Usage πŸš€

Decorator

Use @meiga as a decorator to protect your results and prevent from unexpected exceptions. It allways returns a Result object.

@meiga
def create_user(user_id: UserId) -> BoolResult:
     user = user_creator.execute(user_id).unwrap_or_return()
     return repository.save(user)

When decorate staticmethod and classmethod check the order, otherwise it will raise an error (UnexpectedDecorationOrderError) as these kind of methods are not callable

class UserCreatorFactory:

    @staticmethod
    @meiga
    def from_version(version: str) -> Result[UserCreator, Error]:
        if version == "migration_v1":
            creator = UserCreator.build()
        else:
            creator = LegacyUserCreator.build()
        return Success(creator)

Unwrap Result

If you wrap a Result object, its will return a valid value if it is success. Otherwise, it will return None.

result = Result(success="Hi!")
value = result.unwrap()
assert value == "Hi!"

result = Failure(Error())
value = result.unwrap()

assert value is None

You can use unwrap_or_returnin combination with @meiga decorator. If something wrong happens unwraping your Result, the unwrap_or_return function will raise an Exception (ReturnErrorOnFailure). @meiga decorator allows to handle the exception in case of error and unwrap the value in case of success. The following example illustrate this:

from meiga import Result, Error
from meiga.decorators import meiga

@meiga
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:

first_name = string_from_key(dictionary=user_info, key=key).unwrap_or_return(return_value_on_failure=isSuccess) 

Handle Result

This framework also allows a method for handling Result type. handle method returns itself and execute the on_success function when the instance represemts success and the on_failure function when it is failure.

When the operations is executed with its happy path, handle function returns the success value, as with result.value.

>>> result = string_from_key(dictionary=user_info, key="first_name")
Result[status: success | value: Rosalia]
>>> first_name = result.handle()
Rosalia

In addition, you can call another function after evaluate the result. Use optional parameters success_handler and failure_handler (Callable functions).

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=success_handler, on_failure=failure_handler)
Additional parameters

If you need to add some arguments as a parameters, use success_args and failure_args:

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=success_handler, 
              on_failure=failure_handler,
              success_args=1,
              failure_args=(1, 2))
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.

    parameters = (1, Result.__id__, 2)

    def on_success(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 on_failure(param_1: int, result: Result, param_2: int):
        assert param_1 == 1
        assert isinstance(result, Result)
        assert result.value == Error()
        assert param_2 == 2

    @meiga
    def run():
        result.handle(
            on_success=on_success,
            on_failure=on_failure,
            success_args=parameters,
            failure_args=parameters,
        )

    run()

Test Assertions

To help us on testing functions that returns Result, meiga provide us two functions: assert_success and access_failure.

Check the following pytest-based test for more information: tests/unit/test_result_assertions.py

Contact πŸ“¬

[email protected]

Comments
  • Enable match pattern [PEP 636 – Structural Pattern Matching]

    Enable match pattern [PEP 636 – Structural Pattern Matching]

    First Check

    • [X] I added a very descriptive title to this issue.
    • [X] I used the GitHub search to find a similar issue and didn't find it.
    • [X] I already read and followed all the documentation and didn't find an answer.

    Description & Wanted Solution

    Now, we have two ways to handle results:

    Checking the state of the result:

    from meiga import Result
    
    my_result = Result(success=True)
    
    if my_result.is_success:
           print("Success")
    else:
      if isinstance(my_result.value, NotUserFoundError)
           print("Failure: Not User Found")
      elif isinstance(my_result.value, AlreadyExistError)
           print("Failure: Already exist")
    

    Or using handlers:

    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)
    )
    

    I'd be nice to take advantage of new PEP 636 to handle and react to results

    Wanted Code

    from meiga import Result
    
    my_result = Result(success=True)
    
    match my_result:
       case Success(_):
           print("Success")
       case Failure(NotUserFoundError):
           print("Failure: Not User Found")
       case Failure(AlreadyExistError):
           print("Failure: Already exist")
    

    Alternatives

    No response

    Additional Context

    No response

    enhancement 
    opened by acostapazo 2
  • Import failing in 1.6.0 due to missing module

    Import failing in 1.6.0 due to missing module

    ❯ python3 -m venv venv
    ❯ . venv/bin/activate
    ❯ pip install meiga
    Collecting meiga
      Downloading meiga-1.6.0-py3-none-any.whl (13 kB)
    Installing collected packages: meiga
    Successfully installed meiga-1.6.0
    ❯ python3
    Python 3.10.5 (main, Aug  1 2022, 07:53:20) [GCC 12.1.0] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>> from meiga import Error, Success, Failure, Result
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/tmp/mmm/venv/lib/python3.10/site-packages/meiga/__init__.py", line 3, in <module>
        from meiga import public_api
      File "/tmp/mmm/venv/lib/python3.10/site-packages/meiga/public_api.py", line 6, in <module>
        from . import decorators
      File "/tmp/mmm/venv/lib/python3.10/site-packages/meiga/decorators/__init__.py", line 1, in <module>
        from .meiga_decorator import meiga
      File "/tmp/mmm/venv/lib/python3.10/site-packages/meiga/decorators/meiga_decorator.py", line 4, in <module>
        from typing_extensions import ParamSpec
    ModuleNotFoundError: No module named 'typing_extensions'
    

    1.5.1 works fine:

    ❯ pip install meiga==1.5.1
    Collecting meiga==1.5.1
      Downloading meiga-1.5.1-py3-none-any.whl (12 kB)
    Installing collected packages: meiga
      Attempting uninstall: meiga
        Found existing installation: meiga 1.6.0
        Uninstalling meiga-1.6.0:
          Successfully uninstalled meiga-1.6.0
    Successfully installed meiga-1.5.1
    ❯ python3
    Python 3.10.5 (main, Aug  1 2022, 07:53:20) [GCC 12.1.0] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>> from meiga import Error, Success, Failure, Result
    >>> 
    

    Broken on both python 3.10 and 3.9 (caught that one in CI), seemingly by #27 .

    I presume like there should be an optional dependency typing-extensions for py < 3.10, and for 3.10 and above ParamSpec should be imported from typing instead - from my understanding, said typehint got merged from extensions into stdlib in 3.10 release.


    FYI: if you look at the GHA run corresponding to #27 https://github.com/alice-biometrics/meiga/runs/7740328032?check_suite_focus=true , typing-extensions is actually installed there as a dependency of lume:

    Run pip install lume
    Collecting lume
      Using cached lume-0.9.2-py3-none-any.whl (33 kB)
    ...
    Collecting typing-extensions>=3.7.4.3
    

    This is how it leaked to production.

    bug 
    opened by Artalus 2
  • Better typing on Derived Callables [Handlers]

    Better typing on Derived Callables [Handlers]

    meiga has available some method to define derived actions depending on the result value

    Example:

        def handle(
            self,
            on_success: Optional[Callable[..., None]] = None,
            on_failure: Optional[Callable[..., None]] = None,
            success_args: Optional[Any] = None,
            failure_args: Optional[Any] = None,
        ) -> "Result":
            if on_failure:
                self.unwrap_or_else(on_failure, failure_args)
            if on_success:
                self.unwrap_and(on_success, success_args)
            return self
    

    It should be useful to have typed the Callable input with the same type as the _args variable.

    First Attempt

    We've tried some alternatives to link the Callable input parameters to those of the arguments that we pass in (e.g success_args)

    from typing import ParamSpec
    
    PS = ParamSpec("PS")
    
    def unwrap_and(self, on_success: Callable[PS, None], success_args: Optional[PS] = None)
    

    unfortunately, we have not yet found a solution in this regard.

    Breaking Change Proposal/Alternative

    Perhaps the derived actions should have their own class to be more cohesive semantically. For example:

    from typing import Callable, Iterable
    
    class DerivedAction:
    
        def __init__(self, func: Callable[..., None], args: Iterable = None):
            self.func = func
            self.args = args
    
    
    class OnSuccessAction(DerivedAction):
        ...
    
    
    class OnFailureAction(DerivedAction):
        ...
    

    And then, we could simplify handle, unwrap_or_else and unwrap_and.

    Example

         def handle(
            self,
            on_success: OnSuccessAction = None,
            on_failure: OnFailureAction = None,
        ) -> "Result":
            if on_failure:
                self.unwrap_or_else(on_failure)
            if on_success:
                self.unwrap_and(on_success)
            return self
    

    With this solution we're not solving the typing issue but I think the Dev Experience would be improved.

    enhancement 
    opened by acostapazo 2
  • Add AnyResult type

    Add AnyResult type

    First Check

    • [X] I added a very descriptive title to this issue.
    • [X] I used the GitHub search to find a similar issue and didn't find it.
    • [X] I already read and followed all the documentation and didn't find an answer.

    Description & Wanted Solution

    Create an alias for Result[Any, Error] when you don't want to type your success value (useful for declaring abstractmethods)

    Wanted Code

    from abc import ABC, abstractmethod
    from meiga import AnyResult
    
    class MyInterface(ABC):
    
        @abstractmethod
        def execute(self) -> AnyResult:
            pass
    

    Alternatives

    There is an ongoing PEP where they are working on default TypeVars. This could help meiga on generic definitions:

    TS = TypeVar("TS", default=Any)  # Success Type
    TF = TypeVar("TF", default=Error)  # Failure Type
    TEF = TypeVar("TEF")  # External Failure Type
    
    
    class Result(Generic[TS, TF]):
       ...
    

    Additional Context

    No response

    enhancement 
    opened by acostapazo 1
  • Could class typing of Success and Failure be improved?

    Could class typing of Success and Failure be improved?

    Success and Failure inherits from Result

    class Success(Result):
        def __init__(self, value=True) -> None:
            Result.__init__(self, success=value)
    
    
    class Failure(Result):
        def __init__(self, error=Error()) -> None:
            Result.__init__(self, failure=error)
    

    Thanks to the brand new type hints, the IDEs (Pycharm, VSCode,..) is able to assist us inspecting and checking the types of our objects.

    Example:

    my_result = Result[int, Error](2)
    value = my_result.unwrap()
     ^
      ---------- it is a int 
    

    However, when we use Success and Failure this does not work as we are not specifiying the type. I guess is not possible to update the type generic in runtime, but we should think about some alternative to improve the dev experience using these aliases.

    enhancement 
    opened by acostapazo 1
  • Result methods need more type hints

    Result methods need more type hints

    This library is awesome and I am so glad I stumbled upon it before diving into writing something similar on my own :)


    It feels to me that the core Result[...] class's methods kinda lack typization, making it harder to use with autocompletion and linters. For example:

    from typing import NamedTuple, cast
    
    from meiga import Result, Error
    
    class MyOutcome(NamedTuple):
        x: int
    
    def foo(fail: bool) -> Result[MyOutcome, Error]:
        if fail:
            return Result(failure=Error())
        return Result(success=MyOutcome(x=11))
    
    def test_type_check() -> None:
        a = foo(True)
        assert a.is_success
        av = a.unwrap_or_throw()
        assert av.x == 11
        assert cast(MyOutcome, av).x == 11
    

    Here av = a.unwrap_or_throw() should provide me with MyOutcome - since its very semantic is to either return a valid result, or raise exception - but VS Code (and probably other editors and IDEs that integrate with mypy) shows that the type is actually Any | None. Furthermore, accessing av.x causes x is not a known member of None warning. I then have to use cast(MyOutcome, av) to get typing back on tracks.

    If I understood it correctly, the problem arise because

    • Result.unwrap_or_throw is not annotated with type hints at all;
    • Result.throw (used inside the former) throws on condition, making the type checker think "it usually returns None".

    It seems that changing unwrap_or_throw to this:

        def unwrap_or_throw(self) -> TS:
            if self._is_success:
                return cast(TS, self.value)
            raise self.value
    

    fixes the problem, as the return type is specified and the raise is explicit.

    I just chose unwrap_or_throw() as the simplest example; all other methods are untyped too - either causing "use of untyped function in typed context" warnings from mypy, or confusing it into thinking that the returned values are Any instead of success- or error-types specified in Result[...] hint.

    enhancement 
    opened by Artalus 1
  • Review __eq__, __ne__ and __hash__

    Review __eq__, __ne__ and __hash__

    First Check

    • [X] I added a very descriptive title to this issue.
    • [X] I used the GitHub search to find a similar issue and didn't find it.
    • [X] I already read and followed all the documentation and didn't find an answer.

    Description

    I'd be nice to review __eq__, __ne__ and __hash__ of Result class to works as expected.

    Some tests are required here!

    Operating System

    macOS

    Operating System Details

    No response

    meiga Version

    v1.7.0

    Python Version

    all

    Additional Context

    No response

    bug 
    opened by acostapazo 0
  • feat(performance): add __slots__ to Result class

    feat(performance): add __slots__ to Result class

    Benchmark code:

    from __future__ import annotations
    
    from timeit import timeit
    
    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,
        "key3": "value",
        "key4": 2,
        "key5": "value",
        "key6": 2,
        "key7": "value",
        "key8": 2,
        "key9": "value",
        "key10": 2,
        "key11": "value",
        "key12": 2,
    }
    
    time_success = timeit(lambda: string_from_key(dictionary=dictionary, key="key1"))
    print(f"time when success: {time_success}")
    
    time_failure_no_such_key = timeit(
        lambda: string_from_key(dictionary=dictionary, key="invalid_key")
    )
    print(f"time when failure (no such key): {time_failure_no_such_key}")
    
    time_failure_type_missmatch = timeit(
        lambda: string_from_key(dictionary=dictionary, key="key2")
    )
    print(f"time when failure (type missmatch): {time_failure_type_missmatch}")
    
    

    Result without slots:

    $ python benchmark/time_result.py
    time when success: 0.5478533749992494
    time when failure (no such key): 0.5760475420102011
    time when failure (type missmatch): 0.6188615420251153
    

    Result with slots:

    $ python benchmark/time_result.py
    time when success: 0.5113824579748325
    time when failure (no such key): 0.550075833016308
    time when failure (type missmatch): 0.5880016250011977
    

    Improvements

    Improvement when success: ~7% faster (x1.071318279412337) Improvement when failure (no such key): ~5% faster (x1.047214779190496) Improvement failure (type missmatch): ~5% faster (x1.052482707039891)

    opened by acostapazo 0
  • Create decorator to convert a function without meiga to return a Result

    Create decorator to convert a function without meiga to return a Result

    First Check

    • [X] I added a very descriptive title to this issue.
    • [X] I used the GitHub search to find a similar issue and didn't find it.
    • [X] I already read and followed all the documentation and didn't find an answer.

    Description

    I'd be nice to have a decorator to convert a function return value to Result

    Wanted Solution

    A new decorator @to_result to convert exceptions to failures and returned value as success

    Wanted Code

    from meiga import to_result
    
    class NoSuchKey(Exception): ...
    class TypeMismatch(Exception): ...
    
    @to_result
    def string_from_key(dictionary: dict, key: str) -> str:
        if key not in dictionary.keys():
            raise NoSuchKey()
    
        value = dictionary[key]
        if not isinstance(value, str):
            raise TypeMismatch()
    
        return value
    
    
    dictionary = {"key1": "value", "key2": 2}
    key = "key1"
    result: Result[str, NoSuchKey | TypeMismatch] = string_from_key(dictionary, key)
    

    Alternatives

    Also to_result decorator should be called as a function converter

    from meiga import to_result
    
    class NoSuchKey(Exception): ...
    class TypeMismatch(Exception): ...
    
    def string_from_key(dictionary: dict, key: str) -> str:
        if key not in dictionary.keys():
            raise NoSuchKey()
    
        value = dictionary[key]
        if not isinstance(value, str):
            raise TypeMismatch()
    
        return value
    
    
    dictionary = {"key1": "value", "key2": 2}
    key = "key1"
    result: Result[str, NoSuchKey | TypeMismatch] = to_result(string_from_key(dictionary, key))
    

    Additional Context

    No response

    enhancement 
    opened by acostapazo 0
  • Rename @meiga decorator

    Rename @meiga decorator

    First Check

    • [X] I added a very descriptive title to this issue.
    • [X] I used the GitHub search to find a similar issue and didn't find it.
    • [X] I already read and followed all the documentation and didn't find an answer.

    Description

    As explained in https://alice-biometrics.github.io/meiga/usage/decorator/, we use @meiga decorator in combination with unwrap_or_return.

    Wanted Solution

    We should change @meiga decorator name to mitigate confusion with the name of the package:

    ❌

    This does not work

    import meiga
    
    @meiga
    def my_func():
      pass
    

    Name proposed: @early_return

    Wanted Code

    from meiga import early_return, BoolResult, isSuccess
    
    @early_return
    def update_user(user_id: UserId, new_name: str) -> BoolResult:
         user = repository.retrieve(user_id).unwrap_or_return()
         user.update_name(new_name)
         repository.save(user).unwrap_or_return()
         event_bus.publish(user.pull_domain_events()).unwrap_or_return()
         return isSuccess
    

    Alternatives

    No response

    Additional Context

    No response

    enhancement 
    opened by acostapazo 0
  • Use __slots__ for faster attribute access and space savings in memory

    Use __slots__ for faster attribute access and space savings in memory

    First Check

    • [X] I added a very descriptive title to this issue.
    • [X] I used the GitHub search to find a similar issue and didn't find it.
    • [X] I already read and followed all the documentation and didn't find an answer.

    Description

    Result class do not define slots for value.

    Wanted Solution

    meiga should define slots and then benchmark it to measure the improvements in terms of:

    • faster attribute access and
    • space savings in memory

    See this

    Wanted Code

    class Result(Generic[TS, TF]):
        __id__ = "__meiga_result_identifier__"
        __slots__ = "_value_success", "_value_failure" # This
    
        def __init__(
            self,
            success: Union[TS, Type[NoGivenValue]] = NoGivenValue,
            failure: Union[TF, Type[NoGivenValue]] = NoGivenValue,
        ) -> None:
            self._value_success = success
            self._value_failure = failure
            self._assert_values()
    
       ...
    

    Alternatives

    No response

    Additional Context

    No response

    enhancement 
    opened by acostapazo 0
Releases(v1.8.1)
  • v1.8.1(Nov 29, 2022)

    What's Changed

    • Feature/add pyupgrade by @acostapazo in https://github.com/alice-biometrics/meiga/pull/49
    • feat: add str implementation for Error class by @acostapazo in https://github.com/alice-biometrics/meiga/pull/50

    Full Changelog: https://github.com/alice-biometrics/meiga/compare/v1.8.0...v1.8.1

    Source code(tar.gz)
    Source code(zip)
  • v1.8.0(Oct 27, 2022)

    What's Changed

    • feat(performance): add slots to Result class (up to 7% faster) by @acostapazo in https://github.com/alice-biometrics/meiga/pull/41
    • feat(rename): use early_return instead of meiga decorator by @acostapazo in https://github.com/alice-biometrics/meiga/pull/42
    • chore: review and test object type attributes by @acostapazo in https://github.com/alice-biometrics/meiga/pull/46
    • feat(to_result): add new decorator to_result by @acostapazo in https://github.com/alice-biometrics/meiga/pull/44
    • chore(requirements): update dev requirements by @acostapazo in https://github.com/alice-biometrics/meiga/pull/47
    • feat(match): add match pattern feature (available from Python 3.10) by @acostapazo in https://github.com/alice-biometrics/meiga/pull/43

    Full Changelog: https://github.com/alice-biometrics/meiga/compare/v1.7.0...v1.8.0

    Source code(tar.gz)
    Source code(zip)
  • v1.7.0(Oct 10, 2022)

    What's Changed

    • Feature/improve doc by @acostapazo in https://github.com/alice-biometrics/meiga/pull/30
    • chore(docs): add first version of documentation (exploring mkdocs) by @acostapazo in https://github.com/alice-biometrics/meiga/pull/32
      • https://alice-biometrics.github.io/meiga/
    • 33 add assert success and assert failure to result class by @acostapazo in https://github.com/alice-biometrics/meiga/pull/34
    • feat(alias): make the aliases Success and Failure callables to return… by @acostapazo in https://github.com/alice-biometrics/meiga/pull/36

    Full Changelog: https://github.com/alice-biometrics/meiga/compare/v1.6.1...v1.7.0

    Source code(tar.gz)
    Source code(zip)
  • v1.6.1(Aug 25, 2022)

  • v1.6.0(Aug 9, 2022)

    What's Changed

    • feat: improve result aliases … by @acostapazo in https://github.com/alice-biometrics/meiga/pull/24
    • feat: add AnyResult new alias by @acostapazo in https://github.com/alice-biometrics/meiga/pull/26
    • chore(types): add type hints to meiga decorator by @acostapazo in https://github.com/alice-biometrics/meiga/pull/27

    Full Changelog: https://github.com/alice-biometrics/meiga/compare/v1.5.1...v1.6.0

    Source code(tar.gz)
    Source code(zip)
  • v1.5.1(Aug 5, 2022)

    What's Changed

    • fix: check message attribute in error representation by @franciscorode in https://github.com/alice-biometrics/meiga/pull/25

    New Contributors

    • @franciscorode made their first contribution in https://github.com/alice-biometrics/meiga/pull/25

    Full Changelog: https://github.com/alice-biometrics/meiga/compare/v1.5.0...v1.5.1

    Source code(tar.gz)
    Source code(zip)
  • v1.5.0(Aug 1, 2022)

    What's Changed

    • feat(mypy): simplify type hints by @acostapazo in https://github.com/alice-biometrics/meiga/pull/20
    • Adding new backward-compatible handlers by @acostapazo in https://github.com/alice-biometrics/meiga/pull/22

    Full Changelog: https://github.com/alice-biometrics/meiga/compare/v1.4.0...v1.5.0

    Source code(tar.gz)
    Source code(zip)
  • v1.4.0(Jul 26, 2022)

    What's Changed

    • fix: improve type hints by @fgsalomon in https://github.com/alice-biometrics/meiga/pull/17
    • chore(github-actions): update checkout and setup-python versions by @acostapazo in https://github.com/alice-biometrics/meiga/pull/18
    • feat(mypy): add py.typed to ship typing information by @acostapazo in https://github.com/alice-biometrics/meiga/pull/19

    New Contributors

    • @fgsalomon made their first contribution in https://github.com/alice-biometrics/meiga/pull/17

    Acknowledgments

    Thanks to @Artalus for giving us valuable feedback in https://github.com/alice-biometrics/meiga/issues/16 to improve meiga type hints.

    Full Changelog: https://github.com/alice-biometrics/meiga/compare/v1.3.2...v1.4.0

    Source code(tar.gz)
    Source code(zip)
  • v1.3.2(Jun 10, 2022)

    What's Changed

    • fix(warning): solve warning when VERSION file is not closed when read… by @acostapazo in https://github.com/alice-biometrics/meiga/pull/14
    • Bugfix/uploading to pypi missing requirements by @acostapazo in https://github.com/alice-biometrics/meiga/pull/15

    Full Changelog: https://github.com/alice-biometrics/meiga/compare/v1.3.1...v1.3.2

    Source code(tar.gz)
    Source code(zip)
  • v1.3.1(Apr 6, 2022)

    What's Changed

    • feat: update black version by @acostapazo in https://github.com/alice-biometrics/meiga/pull/12
    • feat: update pypi workflow. Use PYPI token instead former password by @acostapazo in https://github.com/alice-biometrics/meiga/pull/13

    Full Changelog: https://github.com/alice-biometrics/meiga/compare/v1.3.0...v1.3.1

    Source code(tar.gz)
    Source code(zip)
  • v1.3.0(Jan 10, 2022)

    What's Changed

    • Add static analysis and improve typing. by @acostapazo in https://github.com/alice-biometrics/meiga/pull/11
    • Add some missing tests. by @acostapazo in https://github.com/alice-biometrics/meiga/pull/11
    • Add coverage integration (Codecov) by @acostapazo in https://github.com/alice-biometrics/meiga/pull/11
    • Remove unused decorators (logs) by @acostapazo in https://github.com/alice-biometrics/meiga/pull/11

    Full Changelog: https://github.com/alice-biometrics/meiga/compare/v1.2.13...v1.3.0

    Source code(tar.gz)
    Source code(zip)
  • v1.2.13(Jan 3, 2022)

    What's Changed

    • Feature/update dev tools and modernize by @acostapazo in https://github.com/alice-biometrics/meiga/pull/10

    Full Changelog: https://github.com/alice-biometrics/meiga/compare/v1.2.12...v1.2.13

    Source code(tar.gz)
    Source code(zip)
  • v1.2.12(Dec 3, 2020)

  • v1.2.11(Nov 24, 2020)

    • Add new feature on unwrap_or_return Result method. Now, is possible to return a specific value on failure.
      • result.unwrap_or_return(return_value_on_failure=isSuccess)
    Source code(tar.gz)
    Source code(zip)
  • v1.2.10(Oct 7, 2020)

  • v1.2.9(Jun 9, 2020)

  • v1.2.8(May 21, 2020)

  • v1.2.7(Apr 22, 2020)

  • v1.2.6(Apr 17, 2020)

  • v1.2.5(Apr 17, 2020)

  • v1.2.4(Mar 27, 2020)

  • v1.2.3(Mar 11, 2020)

    • Add a new feature for handling results. Now, you can pass by reference both external parameter and the Result itself with handle. πŸ§™
    Source code(tar.gz)
    Source code(zip)
  • v1.2.2(Feb 5, 2020)

  • v1.2.1(Feb 5, 2020)

  • v1.2.0(Jan 23, 2020)

  • v1.1.2(Jan 22, 2020)

  • v1.1.0(Jan 21, 2020)

    • Added unwrap_and

    • Refactored handle. Now it returns itself.

      def on_success(value):
        print(f"on_success: {value}")
      
      def on_failure(value):
        print(f"on_failure: {value}")
      
      result = Success("Hi!")
      value = result.handle(on_success, on_failure).unwrap()
      
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0(Jan 20, 2020)

    • Functions refactor and stabilization

    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 |

    Functions

    | Functions | Definition | | --------------------------------|:-------------------------------------------------------------------------------------------- | | unwrap() | Returns the encapsulated value if this instance represents success or None if it is failure. | | unwrap_or(failure_value) | Returns the encapsulated value if this instance represents success or the selected failure_value if it is failure. |
    | unwrap_or_throw() | Returns the encapsulated value if this instance represents success or throws the encapsulated exception if it is failure. |
    | unwrap_or_else(on_failure) | Returns the encapsulated value if this instance represents success or execute the on_failure function when it is failure. |
    | handle(on_success,on_failure) | Returns itself and execute the on_successfunction when the instance represemts success and the on_failure function when it is failure. |
    | map(transform) | Returns a transformed result applying transform function applied to encapsulated value if this instance represents success or failure |

    Source code(tar.gz)
    Source code(zip)
Owner
Alice Biometrics
Make You Unique
Alice Biometrics
A collection of simple python mini projects to enhance your python skills

A collection of simple python mini projects to enhance your python skills

PYTHON WORLD 12.1k Jan 5, 2023
A simple USI Shogi Engine written in python using python-shogi.

Revengeshogi My attempt at creating a USI Shogi Engine in python using python-shogi. Current State of Engine Currently only generating random moves us

null 1 Jan 6, 2022
A simple tutorial to get you started with Discord and it's Python API

Hello there Feel free to fork and star, open issues if there are typos or you have a doubt. I decided to make this post because as a newbie I never fo

Sachit 1 Nov 1, 2021
A simple malware that tries to explain the logic of computer viruses with Python.

Simple-Virus-With-Python A simple malware that tries to explain the logic of computer viruses with Python. What Is The Virus ? Computer viruses are ma

Xrypt0 6 Nov 18, 2022
SCTYMN is a GitHub repository that includes some simple scripts(currently only python scripts) that can be useful.

Simple Codes That You Might Need SCTYMN is a GitHub repository that includes some simple scripts(currently only python scripts) that can be useful. In

CodeWriter21 2 Jan 21, 2022
Simple yet powerful CAD (Computer Aided Design) library, written with Python.

Py-MADCAD >>> it's time to throw parametric softwares out ! Simple yet powerful CAD (Computer Aided Design) library, written with Python. Installation

jimy byerley 124 Jan 6, 2023
Code for our SIGIR 2022 accepted paper : P3 Ranker: Mitigating the Gaps between Pre-training and Ranking Fine-tuning with Prompt-based Learning and Pre-finetuning

P3 Ranker Implementation for our SIGIR2022 accepted paper: P3 Ranker: Mitigating the Gaps between Pre-training and Ranking Fine-tuning with Prompt-bas

null 14 Jan 4, 2023
This repository outlines deploying a local Kubeflow v1.3 instance on microk8s and deploying a simple MNIST classifier using KFServing.

Zero to Inference with Kubeflow Getting Started This repository houses all of the tools, utilities, and example pipeline implementations for exploring

Ed Henry 3 May 18, 2022
Documentation generator for C++ based on Doxygen and mosra/m.css.

mosra/m.css is a Doxygen-based documentation generator that significantly improves on Doxygen's default output by controlling some of Doxygen's more unruly options, supplying it's own slick HTML+CSS generation and adding a fantastic live search feature.

Mark Gillard 109 Dec 7, 2022
Explicit, strict and automatic project version management based on semantic versioning.

Explicit, strict and automatic project version management based on semantic versioning. Getting started End users Semantic versioning Project version

Dmytro Striletskyi 6 Jan 25, 2022
Gaphor is the simple modeling tool

Gaphor Gaphor is a UML and SysML modeling application written in Python. It is designed to be easy to use, while still being powerful. Gaphor implemen

Gaphor 1.3k Jan 3, 2023
Ε»mija is a simple universal code generation tool.

Ε»mija Ε»mija is a simple universal code generation tool. It is intended to be used as a means to generate code that is both efficient and easily mainta

Adrian Samoticha 2 Nov 23, 2021
A simple XLSX/CSV reader - to dictionary converter

sheet2dict A simple XLSX/CSV reader - to dictionary converter Installing To install the package from pip, first run: python3 -m pip install --no-cache

Tomas Pytel 216 Nov 25, 2022
layout-parser 3.4k Dec 30, 2022
Dev Centric Tools for Mkdocs Based Documentation

docutools MkDocs Documentation Tools For Developers This repo is providing a set of plugins for mkdocs material compatible documentation. It is meant

Axiros GmbH 14 Sep 10, 2022
Fast syllable estimation library based on pattern matching.

Syllables: A fast syllable estimator for Python Syllables is a fast, simple syllable estimator for Python. It's intended for use in places where speed

ProseGrinder 26 Dec 14, 2022
A document format conversion service based on Pandoc.

reformed Document format conversion service based on Pandoc. Usage The API specification for the Reformed server is as follows: GET /api/v1/formats: L

David Lougheed 3 Jul 18, 2022
This repo provides a package to automatically select a random seed based on ancient Chinese Xuanxue

?? Random Luck Deep learning is acturally the alchemy. This repo provides a package to automatically select a random seed based on ancient Chinese Xua

Tong ZhuοΌˆζœ±ζ‘οΌ‰ 33 Jan 3, 2023
script to calculate total GPA out of 4, based on input gpa.csv

gpa_calculator script to calculate total GPA out of 4 based on input gpa.csv to use, create a total.csv file containing only one integer showing the t

Mohamad Bastin 1 Feb 7, 2022