def test_multiple_raise_errors() -> None: with pytest.raises(BippyException) as exc: (Either.right(42).raise_errors().flat_map( lambda _: Either.left(BippyException("kaboom 1"))). flat_map(lambda _: Either.left(BippyException("kaboom 2"))).flat_map( lambda _: Either.left(BippyException("kaboom 3"))).raise_errors()) assert str(exc.value) == "kaboom 1"
def test_match_types_complex() -> None: case_1 = Environment[str]().map(lambda s: s + "1") case_2 = Environment[str]().map(lambda s: s + "2") case_3 = Environment[str]().map(lambda s: s + "3") with pytest.raises(TypeMatchException): unsafe_run(Environment[int]().match_types().provide(42)) # Obviously grotesque use of `match_types`...but we should understand # its semantics. assert unsafe_run(Environment[str]().match_types().at_type( str, case_1).at_type(str, case_2).map(lambda s: s + "foo").at_type( str, case_3).provide("")) == "1foo" with pytest.raises(TypeMatchException): unsafe_run(Environment[dict]().match_types().at_type( str, case_1).at_type(str, case_2).map(lambda s: s + "foo").at_type( str, case_3).provide({'ka': 'boom'})) with pytest.raises(TypeMatchException): unsafe_run(Environment[str]().match_types().provide("")) with pytest.raises(TypeMatchException): unsafe_run(Environment[str]().match_types().at_type( int, Environment[int]()).provide("")) assert unsafe_run(Environment[str]().require( lambda s: len(s) > 5, lambda s: f"String '{s}' is too short.").at_type( str, case_1).either().provide("foo")) == Either.left( "String 'foo' is too short.")
def test_tap() -> None: x = 0 def foo(arg: Either[int, str]) -> None: nonlocal x x = arg.map(len).fold(lambda x: x, lambda x: x) e: Either[int, str] = Either.left(42) assert e.tap(foo) == e assert x == 42 e = Either.right("hello") assert e.tap(foo) == e assert x == len("hello")
def test_display(capsys: Any) -> None: e: Either[int, str] = Either.left(42) assert e.display() == e captured = capsys.readouterr() assert captured.out == str(e) + "\n" assert e.display("Bippy") == e captured = capsys.readouterr() assert captured.out == "Bippy:\n" + str(e) + "\n" e = Either.right("hello") assert e.display() == e captured = capsys.readouterr() assert captured.out == str(e) + "\n" assert e.display("Bippy") == e captured = capsys.readouterr() assert captured.out == "Bippy:\n" + str(e) + "\n"
def test_to_union() -> None: # The point of this test is to ensure that mypy's type inference works # properly, ergo we prefer to not parametrize it. def foo(x: Union[int, str]) -> bool: return isinstance(x, int) or isinstance(x, str) e: Either[int, str] = Left(42) assert e.to_union() == 42 assert foo(e.to_union()) e = Right("hello") assert e.to_union() == "hello" assert foo(e.to_union()) # mypy should properly unify Union[NoReturn, X] for all types X. assert Either.left(42).to_union() + 1 == 43 assert len(Either.right("hello").to_union()) == len("hello")
def test_swap_left(input: A, left_type: Type[A], right_type: Type[B]) -> None: x: Either[A, B] = Either.left(input) assert x.swap().swap() == x == Either.right(input).swap()
def test_fold_left() -> None: x: Either[int, str] = Either.left(42) assert x.fold(lambda x: x + 1, lambda y: y + "!") == 43
def test_match_left() -> None: x: Either[int, str] = Either.left(42) assert x.match(lambda x: x.value + 1, lambda y: y.value + "!") == 43
def test_from_union_left(value: Union[A, B], left_type: Type[A], right_type: Type[B]) -> None: assert Either.from_union(value, left_type, right_type) == Either.left(value)
def test_raise_errors_right_flat_map() -> None: with pytest.raises(BippyException) as exc: (Either.right(42).flat_map( lambda _: Either.left(BippyException("Oh noes!"))).raise_errors()) assert str(exc.value) == "Oh noes!"
def test_raise_errors_exception_on_left() -> None: with pytest.raises(BippyException) as exc: Either.left(BippyException("Oh noes!")).raise_errors() assert str(exc.value) == "Oh noes!"
def test_raise_errors_regular_value() -> None: with pytest.raises(EitherException) as exc: Either.left(42).raise_errors() assert exc.value == EitherException(value=42)
def test_either_left(input: Either[A, NoReturn], expected: Left[A]) -> None: x = Either.left(input) assert x == x.to_left() == expected with pytest.raises(TypeError): x.to_right() # type: ignore
def test_flatten_left() -> None: x: Either[str, Either[str, int]] = Either.left("asdf") assert x.flatten() == Left("asdf")
eqr.EQ2, eqr.EQ3, eqr.EQ4, eqr.EQ5, eqr.EQ6, eqr.EQ7, ]) def test_do_notation(equivalence_relation: eqr.Equiv[R, E, A]) -> None: output_p = unsafe_run(equivalence_relation.p.either().provide( equivalence_relation.environment)) output_q = unsafe_run(equivalence_relation.q.either().provide( equivalence_relation.environment)) assert output_p == output_q == equivalence_relation.expected_output @pytest.mark.parametrize("input", [Either.left(42), Either.right("hello")]) def test_from_either(input: Either[int, str]) -> None: assert ZIO.from_either(input)._run(()) == input @pytest.mark.parametrize("input,expected", [(ZIO.fail("oh noes"), Right(Left("oh noes"))), (ZIO.succeed(42), Right(Right(42)))]) def test_either(input: ZIO[object, str, int], expected: Either[str, int]) -> None: assert input.either()._run(()) == expected @pytest.mark.parametrize("input,expected", [(ZIO.succeed(Left("oh noes")), Left("oh noes")), (ZIO.succeed(Right(42)), Right(42))])
def _f(r: R) -> Either[Union[E, X], AA]: try: return self._run(r) except exc as e: return Either.left(e)