πŸ§™ 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
Explorative Data Analysis Guidelines

Explorative Data Analysis Get data into a usable format! Find out if the following predictive modeling phase will be successful! Combine everything in

Florian Rohrer 18 Dec 26, 2022
This repo contains everything you'll ever need to learn/revise python basics

Python Notes/cheat sheet Simplified notes to get your Python basics right Just compare code and output side by side and feel the rush of enlightenment

Hem 5 Oct 06, 2022
Collections of Beautiful Latex Snippets

HandyLatex Collections of Beautiful Latex Snippets Table πŸ‘‰ Succinct table with bold separation line and gray text %################## Dependencies ##

Xintao 15 Apr 11, 2022
A Python package develop for transportation spatio-temporal big data processing, analysis and visualization.

English δΈ­ζ–‡η‰ˆ TransBigData Introduction TransBigData is a Python package developed for transportation spatio-temporal big data processing, analysis and

Qing Yu 251 Jan 03, 2023
Modified fork of CPython's ast module that parses `# type:` comments

Typed AST typed_ast is a Python 3 package that provides a Python 2.7 and Python 3 parser similar to the standard ast library. Unlike ast up to Python

Python 217 Dec 06, 2022
k3heap is a binary min heap implemented with reference

k3heap k3heap is a binary min heap implemented with reference k3heap is a component of pykit3 project: a python3 toolkit set. In this module RefHeap i

pykit3 1 Nov 13, 2021
Reproducible Data Science at Scale!

Pachyderm: The Data Foundation for Machine Learning Pachyderm provides the data layer that allows machine learning teams to productionize and scale th

Pachyderm 5.7k Dec 29, 2022
A repository of links with advice related to grad school applications, research, phd etc

A repository of links with advice related to grad school applications, research, phd etc

Shaily Bhatt 946 Dec 30, 2022
Fast, efficient Blowfish cipher implementation in pure Python (3.4+).

blowfish This module implements the Blowfish cipher using only Python (3.4+). Blowfish is a block cipher that can be used for symmetric-key encryption

Jashandeep Sohi 41 Dec 31, 2022
πŸ“˜ OpenAPI/Swagger-generated API Reference Documentation

Generate interactive API documentation from OpenAPI definitions This is the README for the 2.x version of Redoc (React-based). The README for the 1.x

Redocly 19.2k Jan 02, 2023
DataRisk Detection Learning Resources

DataRisk Detection Learning Resources Data security: Based on the "data-centric security system" position, it generally refers to the entire security

Liao Wenzhe 59 Dec 05, 2022
A simple flask application to collect annotations for the Turing Change Point Dataset, a benchmark dataset for change point detection algorithms

AnnotateChange Welcome to the repository of the "AnnotateChange" application. This application was created to collect annotations of time series data

The Alan Turing Institute 16 Jul 21, 2022
Testing-crud-login-drf - Creation of an application in django on music albums

testing-crud-login-drf Creation of an application in django on music albums Befo

Juan 1 Jan 11, 2022
Service for visualisation of high dimensional for hydrosphere

hydro-visualization Service for visualization of high dimensional for hydrosphere DEPENDENCIES DEBUG_ENV = bool(os.getenv("DEBUG_ENV", False)) APP_POR

hydrosphere.io 1 Nov 12, 2021
[Unofficial] Python PEP in EPUB format

PEPs in EPUB format This is a unofficial repository where I stock all valid PEPs in the EPUB format. Repository Cloning git clone --recursive MickaΓ«l Schoentgen 9 Oct 12, 2022

Sphinx Theme Builder

Sphinx Theme Builder Streamline the Sphinx theme development workflow, by building upon existing standardised tools. and provide a: simplified packagi

Pradyun Gedam 23 Dec 26, 2022
Python script to generate Vale linting rules from word usage guidance in the Red Hat Supplementary Style Guide

ssg-vale-rules-gen Python script to generate Vale linting rules from word usage guidance in the Red Hat Supplementary Style Guide. These rules are use

Vale at Red Hat 1 Jan 13, 2022
Pydantic model generator for easy conversion of JSON, OpenAPI, JSON Schema, and YAML data sources.

datamodel-code-generator This code generator creates pydantic model from an openapi file and others. Help See documentation for more details. Supporte

Koudai Aono 1.3k Dec 29, 2022
BakTst_Org is a backtesting system for quantitative transactions.

BakTst_Org δΈ­ζ–‡reademeοΌšδΌ ι€ι—¨ Introduction: BakTst_Org is a prototype of the backtesting system used for BTC quantitative trading. This readme is mainly di

18 May 08, 2021
Bring RGB to life in Neovim

Bring RGB to life in Neovim Change your RGB devices' color depending on Neovim's mode. Fast and asynchronous plugin to live your vim-life to the fulle

Antoine 40 Oct 27, 2022