Пример #1
0
class TestOptionConstructors:
    """Test option constructors."""
    @pytest.mark.parametrize(
        "val, exp",
        (
            (5, Some(5)),
            (None, Nothing()),
            ("", Some("")),
            (False, Some(False)),
            ({}, Some({})),
            ([], Some([])),
        ),
    )
    def test_of(self, val: t.Any, exp: Option) -> None:
        """Option.of() returns an Option from an Optional."""
        assert Option.of(val) == exp

    @pytest.mark.parametrize(
        "predicate, val, exp",
        (
            (lambda x: x is True, True, Some(True)),
            (lambda x: x is True, False, Nothing()),
            (lambda x: x > 0, 1, Some(1)),
            (lambda x: x > 0, -2, Nothing()),
        ),
    )
    def test_some_if(self, predicate: t.Callable, val: t.Any,
                     exp: Option) -> None:
        """Test constructing based on some predicate."""
        assert Option.some_if(predicate, val) == exp

    @pytest.mark.parametrize(
        "predicate, val, exp",
        (
            (lambda x: x is True, True, Nothing()),
            (lambda x: x is True, False, Some(False)),
            (lambda x: x > 0, 1, Nothing()),
            (lambda x: x > 0, -2, Some(-2)),
        ),
    )
    def test_nothing_if(self, predicate: t.Callable, val: t.Any,
                        exp: Option) -> None:
        """Test constructing based on some predicate."""
        assert Option.nothing_if(predicate, val) == exp

    @pytest.mark.parametrize(
        "options, exp",
        (
            ([Some(1), Some(2), Some(3)], Some((1, 2, 3))),
            ([Some(1), Nothing(), Some(3)], Nothing()),
            ([Nothing()], Nothing()),
            ([Some(1)], Some((1, ))),
            ((Some(1), Some(2), Some(3)), Some((1, 2, 3))),
        ),
    )
    def test_collect(self, options: t.Iterable[Option], exp: Option) -> None:
        """Test constructing from an iterable of options."""
        assert Option.collect(options) == exp
Пример #2
0
 def test_raise_if_err(self) -> None:
     """This method is deprecated."""
     with pytest.deprecated_call():
         assert Some("hello").raise_if_err("error") == "hello"
     with pytest.deprecated_call():
         with pytest.raises(RuntimeError):
             Nothing().raise_if_err("error")
Пример #3
0
 def __init__(self, sql: Sql, dbname: str):
     self.sql = sql
     self.conn = psycopg3.connect(dbname=dbname)
     self.cursor = self.conn.cursor()
     if sql.select_sql != '':
         self.cursor.execute(sql.select_sql)
         self.select_result = Some(self.cursor.fetchall())
     else:
         self.select_result = Nothing()
Пример #4
0
class TestOptionConstructors:
    """Test option constructors."""
    @pytest.mark.parametrize(
        "val, exp",
        (
            (5, Some(5)),
            (None, Nothing()),
            ("", Some("")),
            (False, Some(False)),
            ({}, Some({})),
            ([], Some([])),
        ),
    )
    def test_of(self, val: t.Any, exp: Option) -> None:
        """Option.of() returns an Option from an Optional."""
        assert Option.of(val) == exp

    @pytest.mark.parametrize(
        "predicate, val, exp",
        (
            (lambda x: x is True, True, Some(True)),
            (lambda x: x is True, False, Nothing()),
            (lambda x: x > 0, 1, Some(1)),
            (lambda x: x > 0, -2, Nothing()),
        ),
    )
    def test_ok_if(self, predicate: t.Callable, val: t.Any,
                   exp: Option) -> None:
        """Test constructing based on some predicate."""
        assert Option.some_if(predicate, val) == exp

    @pytest.mark.parametrize(
        "predicate, val, exp",
        (
            (lambda x: x is True, True, Nothing()),
            (lambda x: x is True, False, Some(False)),
            (lambda x: x > 0, 1, Nothing()),
            (lambda x: x > 0, -2, Some(-2)),
        ),
    )
    def test_err_if(self, predicate: t.Callable, val: t.Any,
                    exp: Option) -> None:
        """Test constructing based on some predicate."""
        assert Option.nothing_if(predicate, val) == exp
Пример #5
0
class TestImplementationDetails:
    """Some implementation details need to be tested."""
    def test_nothing_singleton(self) -> None:
        """Ensure Nothing() is a singleton."""
        assert Nothing() is Nothing() is Nothing()

    @pytest.mark.parametrize("obj", (Some(1), Nothing(), Ok(1), Err(1)))
    def test_all_slotted(self, obj: t.Any) -> None:
        """All implementations use __slots__."""
        assert not hasattr(obj, "__dict__")
Пример #6
0
    def test_expect_raising(self, exc_cls: t.Type[Exception]) -> None:
        """Can specify exception msg/cls if value is not Some()."""
        exp_exc = exc_cls if exc_cls else RuntimeError
        kwargs = {"exc_cls": exc_cls} if exc_cls else {}
        msg = "not what I expected"

        with pytest.raises(exp_exc) as exc_info:
            Nothing().expect(msg, **kwargs)

        assert msg in str(exc_info.value)
Пример #7
0
def run(folder: Path, options: RunOptions, limit: Option[int], other_args):
    sql = Sql.load_files(folder)

    sys.path.append(str(folder))
    import script  #type: ignore

    log_path = folder.joinpath('errors.log')

    if options != RunOptions.NODB:
        if script.DBNAME != '':
            transform(script, Some(script.DBNAME), sql,
                      options == RunOptions.FULL, limit, log_path, other_args)
        else:
            print(
                'DBNAME not specified. Run with --nodb or fill DBNAME in script/__init__.py'
            )
    else:
        transform(script, Nothing(), sql, False, limit, log_path, other_args)
Пример #8
0
async def async_transform(script, dbname: Option[str], sql: Sql, export: bool,
                          limit: Option[int], log_path: Path, other_args):
    with log_path.open('w') as log_file:
        with MaybeDb(dbname.map(lambda name: Db(sql, name))) as maybe_db:
            input_rows = maybe_db.select_result().unwrap_or([])
            input_rows = clamp_to_limit(input_rows, limit)

            with MaybeExport(
                (maybe_db.export() if export else Nothing())) as maybe_export:
                sender, receiver = trio.open_memory_channel(0)
                async with trio.open_nursery() as nurs:
                    nurs.start_soon(run_script, script, input_rows, sender,
                                    other_args)
                    async with receiver:
                        async for output_result in receiver:
                            handle_result(output_result, maybe_export,
                                          log_file)

            if export:
                maybe_db.commit()
Пример #9
0
class TestOption:
    """Test the option type."""
    @pytest.mark.parametrize(
        "left, right, exp",
        (
            (Some(2), Nothing(), Nothing()),
            (Nothing(), Some(2), Nothing()),
            (Some(1), Some(2), Some(2)),
            (Nothing(), Nothing(), Nothing()),
        ),
    )
    def test_and(self, left: Option[int], right: Option[int],
                 exp: Option[int]) -> None:
        """Returns Some() if both options are Some()."""
        assert left.and_(right) == exp

    @pytest.mark.parametrize(
        "left, right, exp",
        (
            (Some(2), Nothing(), Some(2)),
            (Nothing(), Some(2), Some(2)),
            (Some(1), Some(2), Some(1)),
            (Nothing(), Nothing(), Nothing()),
        ),
    )
    def test_or(self, left: Option[int], right: Option[int],
                exp: Option[int]) -> None:
        """Returns Some() if either or both is Some()."""
        assert left.or_(right) == exp

    @pytest.mark.parametrize(
        "left, right, exp",
        (
            (Some(2), Nothing(), Some(2)),
            (Nothing(), Some(2), Some(2)),
            (Some(1), Some(2), Nothing()),
            (Nothing(), Nothing(), Nothing()),
        ),
    )
    def test_xor(self, left: Option[int], right: Option[int],
                 exp: Option[int]) -> None:
        """Returns Some() IFF only one option is Some()."""
        assert left.xor(right) == exp

    @pytest.mark.parametrize(
        "start, first, second, exp",
        (
            (Some(2), _sq, _sq, Some(16)),
            (Some(2), _sq, _nothing, Nothing()),
            (Some(2), _nothing, _sq, Nothing()),
            (Nothing(), _sq, _sq, Nothing()),
        ),
    )
    def test_and_then(
        self,
        start: Option[int],
        first: t.Callable[[int], Option[int]],
        second: t.Callable[[int], Option[int]],
        exp: Option[int],
    ) -> None:
        """Chains option-generating functions if results are `Some`."""
        assert start.and_then(first).and_then(second) == exp

    @pytest.mark.parametrize(
        "start, fn, exp",
        (
            (Some("one"), lambda: Some("one else"), Some("one")),
            (Nothing(), lambda: Some("one else"), Some("one else")),
            (Nothing(), lambda: Nothing(), Nothing()),
        ),
    )
    def test_or_else(
        self,
        start: Option[str],
        fn: t.Callable[[], Option[str]],
        exp: Option[str],
    ) -> None:
        """Chains option-generating functions if results are `None`."""
        assert start.or_else(fn) == exp

    @pytest.mark.parametrize("exc_cls", (None, IOError))
    def test_expect_raising(self, exc_cls: t.Type[Exception]) -> None:
        """Can specify exception msg/cls if value is not Some()."""
        exp_exc = exc_cls if exc_cls else RuntimeError
        kwargs = {"exc_cls": exc_cls} if exc_cls else {}
        msg = "not what I expected"

        with pytest.raises(exp_exc) as exc_info:
            Nothing().expect(msg, **kwargs)

        assert msg in str(exc_info.value)

    def test_expect_not_raising(self) -> None:
        """Expecting on a Some() returns the value."""
        assert Some("hello").expect("not what I expected") == "hello"

    @pytest.mark.parametrize(
        "start, exp",
        ((Nothing(), Nothing()), (Some(3), Nothing()), (Some(4), Some(4))),
    )
    def test_filter(self, start: Option[int], exp: Option[int]) -> None:
        """A satisfied predicate returns `Some()`, otherwise `None()`."""
        def is_even(val: int) -> bool:
            return val % 2 == 0

        assert start.filter(is_even) == exp

    @pytest.mark.parametrize("opt, exp", ((Nothing(), True), (Some(1), False)))
    def test_is_nothing(self, opt: Option[int], exp: bool) -> None:
        """"Nothings() are nothing, Some()s are not."""
        assert opt.is_nothing() is exp

    @pytest.mark.parametrize("opt, exp", ((Nothing(), False), (Some(1), True)))
    def test_is_some(self, opt: Option[int], exp: bool) -> None:
        """"Nothings() are nothing, Some()s are not."""
        assert opt.is_some() is exp

    @pytest.mark.parametrize("opt, exp", ((Nothing(), ()), (Some(5), (5, ))))
    def test_iter(self, opt: Option[int], exp: t.Tuple[int, ...]) -> None:
        """Iterating on a Some() yields the Some(); on a None() nothing."""
        assert tuple(opt.iter()) == tuple(iter(opt)) == exp

    @pytest.mark.parametrize("opt, exp", ((Some("hello"), Some(5)),
                                          (Nothing(), Nothing())))
    def test_map(self, opt: Option[str], exp: Option[int]) -> None:
        """Maps fn() onto `Some()` to make a new option, or ignores None()."""
        assert opt.map(len) == exp

    @pytest.mark.parametrize("opt, exp", ((Some("hello"), 5), (Nothing(), -1)))
    def test_map_or(self, opt: Option[str], exp: Option[int]) -> None:
        """Maps fn() onto `Some()` & return the value, or return a default."""
        assert opt.map_or(-1, lambda s: len(s)) == exp

    @pytest.mark.parametrize("opt, exp", ((Some("hello"), 5), (Nothing(), -1)))
    def test_map_or_else(self, opt: Option[str], exp: Option[int]) -> None:
        """Maps fn() onto `Some()` & return the value, or return a default."""
        assert opt.map_or_else(lambda: -1, lambda s: len(s)) == exp

    @pytest.mark.parametrize("opt, exp",
                             ((Some(2), Ok(2)), (Nothing(), Err("oh no"))))
    def test_ok_or(self, opt: Option[str], exp: Result[str, int]) -> None:
        """Map `Some(t)` to `Ok(t)`, or `Nothing()` to an `Err()`."""
        assert opt.ok_or("oh no") == exp

    @pytest.mark.parametrize("opt, exp",
                             ((Some(2), Ok(2)), (Nothing(), Err("oh no"))))
    def test_ok_or_else(self, opt: Option[str], exp: Result[str, int]) -> None:
        """Map `Some(t)` to `Ok(t)`, or `Nothing()` to an `Err()`."""
        assert opt.ok_or_else(lambda: "oh no") == exp

    def test_unwrap_raising(self) -> None:
        """Unwraping a Nothing() raises an error."""
        with pytest.raises(RuntimeError):
            Nothing().unwrap()

    def test_unwrap_success(self) -> None:
        """Unwrapping a Some() returns the wrapped value."""
        assert Some("thing").unwrap() == "thing"

    @pytest.mark.parametrize("opt, exp", ((Some(2), 2), (Nothing(), 42)))
    def test_unwrap_or(self, opt: Option[int], exp: int) -> None:
        """Unwraps a `Some()` or returns a default."""
        assert opt.unwrap_or(42) == exp

    @pytest.mark.parametrize("opt, exp", ((Some(2), 2), (Nothing(), 42)))
    def test_unwrap_or_else(self, opt: Option[int], exp: int) -> None:
        """Unwraps a `Some()` or returns a default."""
        assert opt.unwrap_or_else(lambda: 42) == exp

    @pytest.mark.parametrize(
        "inst, other, eq",
        (
            (Some(1), Some(1), True),
            (Some(1), Some(2), False),
            (Some(1), Nothing(), False),
            (Some(1), 1, False),
            (Nothing(), Nothing(), True),
            (Nothing(), Some(1), False),
            (Nothing(), None, False),
        ),
    )
    def test_equality_inequality(self, inst: t.Any, other: t.Any,
                                 eq: bool) -> None:
        """Test equality and inequality of results."""
        assert (inst == other) is eq
        assert (inst != other) is not eq

    def test_stringify(self) -> None:
        """Repr and str representations are equivalent."""
        assert repr(Some(1)) == str(Some(1)) == "Some(1)"
        assert repr(Nothing()) == str(Nothing()) == "Nothing()"
Пример #10
0
 def test_stringify(self) -> None:
     """Repr and str representations are equivalent."""
     assert repr(Some(1)) == str(Some(1)) == "Some(1)"
     assert repr(Nothing()) == str(Nothing()) == "Nothing()"
Пример #11
0
 def test_unwrap_raising(self) -> None:
     """Unwraping a Nothing() raises an error."""
     with pytest.raises(RuntimeError):
         Nothing().unwrap()
Пример #12
0
class TestResultConstructors:
    """Test Result constructors."""
    @pytest.mark.parametrize(
        "fn, exp",
        (
            (lambda: 5, Ok(5)),
            (lambda: _raises(TypeError), Err(TypeError)),
            (Nothing, Ok(Nothing())),
        ),
    )
    def test_of(self, fn: t.Callable, exp: Result) -> None:
        """Test getting a result from a callable."""
        if exp.is_err():
            assert isinstance(Result.of(fn).unwrap_err(), exp.unwrap_err())
        else:
            assert Result.of(fn) == exp

    def test_of_with_args(self) -> None:
        """Test getting a result from a callable with args."""
        assert Result.of(lambda x: bool(x > 0), 1).unwrap() is True

    def test_of_with_kwargs(self) -> None:
        """Test getting a result from a callable with args."""
        def foo(a: int, b: str = None) -> t.Optional[str]:
            return b

        assert Result.of(foo, 1, b="a").unwrap() == "a"

    @pytest.mark.parametrize(
        "iterable, exp",
        (
            ((Ok(1), Ok(2), Ok(3)), Ok((1, 2, 3))),
            ((Ok(1), Err("no"), Ok(3)), Err("no")),
            (iter([Ok(1), Ok(2)]), Ok((1, 2))),
            ([Err("no")], Err("no")),
            ([Ok(1)], Ok((1, ))),
            ([], Ok(())),
        ),
    )
    def test_collect(self, iterable: t.Iterable[Result[int, str]],
                     exp: Result[int, str]) -> None:
        """Test collecting an iterable of results into a single result."""
        assert Result.collect(iterable) == exp

    def test_collect_short_circuits(self) -> None:
        """Ensure collect does not iterate after an err is reached."""
        until_err: t.List[Result[int, str]] = [Ok(1), Ok(2), Err("no")]

        def _iterable() -> t.Iterable[Result[int, str]]:
            yield from until_err
            # If we continue iterating after the err, we will raise a
            # runtime Error.
            assert False, "Result.collect() did not short circuit on err!"

        assert Result.collect(_iterable()) == Err("no")

    @pytest.mark.parametrize(
        "predicate, val, exp",
        (
            (lambda x: x is True, True, Ok(True)),
            (lambda x: x is True, False, Err(False)),
            (lambda x: x > 0, 1, Ok(1)),
            (lambda x: x > 0, -2, Err(-2)),
        ),
    )
    def test_ok_if(self, predicate: t.Callable, val: t.Any,
                   exp: Result) -> None:
        """Test constructing based on some predicate."""
        assert Result.ok_if(predicate, val) == exp

    @pytest.mark.parametrize(
        "predicate, val, exp",
        (
            (lambda x: x is True, True, Err(True)),
            (lambda x: x is True, False, Ok(False)),
            (lambda x: x > 0, 1, Err(1)),
            (lambda x: x > 0, -2, Ok(-2)),
        ),
    )
    def test_err_if(self, predicate: t.Callable, val: t.Any,
                    exp: Result) -> None:
        """Test constructing based on some predicate."""
        assert Result.err_if(predicate, val) == exp
Пример #13
0
class TestResult:
    """Test the result type."""
    @pytest.mark.parametrize(
        "left, right, exp",
        (
            (Ok(2), Err("late error"), Err("late error")),
            (Err("early error"), Ok(2), Err("early error")),
            (Err("early error"), Err("late error"), Err("early error")),
            (Ok(2), Ok(3), Ok(3)),
        ),
    )
    def test_and(
        self,
        left: Result[int, str],
        right: Result[int, str],
        exp: Result[int, str],
    ) -> None:
        """Test that `and` returns an alternative for `Ok` values."""
        assert left.and_(right) == exp

    def test_and_multichain(self) -> None:
        """.and() calls can be chained indefinitely."""
        assert Ok(2).and_(Ok(3)).and_(Ok(4)).and_(Ok(5)) == Ok(5)

    @pytest.mark.parametrize(
        "start, first, second, exp",
        (
            (Ok(2), _sq, _sq, Ok(16)),
            (Ok(2), _sq, _err, Err(4)),
            (Ok(2), _err, _sq, Err(2)),
            (Ok(2), _err, _err, Err(2)),
            (Err(3), _sq, _sq, Err(3)),
        ),
    )
    def test_and_then(
        self,
        start: Result[int, int],
        first: t.Callable[[int], Result[int, int]],
        second: t.Callable[[int], Result[int, int]],
        exp: Result[int, int],
    ) -> None:
        """Test that and_then chains result-generating functions."""
        assert start.and_then(first).and_then(second) == exp

    def test_flatmap(self) -> None:
        """Flatmap is an alias for and_then"""
        ok: Result[int, int] = Ok(2)
        err: Result[int, int] = Err(2)
        assert ok.flatmap(_sq) == Ok(4)
        assert err.flatmap(_sq) == Err(2)

    @pytest.mark.parametrize(
        "start, first, second, exp",
        (
            (Ok(2), _sq, _sq, Ok(2)),
            (Ok(2), _err, _sq, Ok(2)),
            (Err(3), _sq, _err, Ok(9)),
            (Err(3), _err, _err, Err(3)),
        ),
    )
    def test_or_else(
        self,
        start: Result[int, int],
        first: t.Callable[[int], Result[int, int]],
        second: t.Callable[[int], Result[int, int]],
        exp: Result[int, int],
    ) -> None:
        """Test that and_then chains result-generating functions."""
        assert start.or_else(first).or_else(second) == exp

    @pytest.mark.parametrize("start, exp",
                             ((Ok(2), Nothing()), (Err("err"), Some("err"))))
    def test_err(self, start: Result[int, str], exp: Option[str]) -> None:
        """Test converting a result to an option."""
        assert start.err() == exp

    @pytest.mark.parametrize("exc_cls", (None, IOError))
    def test_expect_raising(self, exc_cls: t.Type[Exception]) -> None:
        """Test expecting a value to be Ok()."""
        exp_exc = exc_cls if exc_cls else RuntimeError
        kwargs = {"exc_cls": exc_cls} if exc_cls else {}
        msg = "not what I expected"

        with pytest.raises(exp_exc) as exc_info:
            Err(2).expect(msg, **kwargs)

        assert msg in str(exc_info.value)

    def test_expect_ok(self) -> None:
        """Expecting an Ok() value returns the value."""
        assert Ok(2).expect("err") == 2

    @pytest.mark.parametrize("exc_cls", (None, IOError))
    def test_expect_err_raising(self, exc_cls: t.Type[Exception]) -> None:
        """Test expecting a value to be Ok()."""
        exp_exc = exc_cls if exc_cls else RuntimeError
        kwargs = {"exc_cls": exc_cls} if exc_cls else {}
        msg = "not what I expected"

        with pytest.raises(exp_exc) as exc_info:
            Ok(2).expect_err(msg, **kwargs)

        assert msg in str(exc_info.value)

    def test_expect_err_err(self) -> None:
        """Expecting an Ok() value returns the value."""
        assert Err(2).expect_err("err") == 2

    def test_is_err(self) -> None:
        """Err() returns true for is_err()."""
        assert Err(1).is_err()
        assert not Ok(1).is_err()

    def test_is_ok(self) -> None:
        """Ok() returns true for is_ok()."""
        assert Ok(1).is_ok()
        assert not Err(1).is_ok()

    @pytest.mark.parametrize("start, exp", ((Ok(1), (1, )), (Err(1), ())))
    def test_iter(self, start: Result[int, int], exp: t.Tuple[int,
                                                              ...]) -> None:
        """iter() returns a 1-member iterator on Ok(), 0-member for Err()."""
        assert tuple(start.iter()) == exp

    @pytest.mark.parametrize("start, exp",
                             ((Ok(2), Ok(4)), (Err("foo"), Err("foo"))))
    def test_map(self, start: Result[int, str], exp: Result[int, str]) -> None:
        """.map() will map onto Ok() and ignore Err()."""
        assert start.map(lambda x: int(x**2)) == exp

    @pytest.mark.parametrize("start, exp",
                             ((Ok("foo"), Ok("foo")), (Err(2), Err("2"))))
    def test_map_err(self, start: Result[str, int], exp: Result[str,
                                                                str]) -> None:
        """.map_err() will map onto Err() and ignore Ok()."""
        assert start.map_err(str) == exp

    @pytest.mark.parametrize("start, exp",
                             ((Ok(1), Some(1)), (Err(1), Nothing())))
    def test_ok(self, start: Result[int, int], exp: Option[int]) -> None:
        """.ok() converts a result to an Option."""
        assert start.ok() == exp

    @pytest.mark.parametrize(
        "left, right, exp",
        (
            (Ok(5), Ok(6), Ok(5)),
            (Ok(5), Err(6), Ok(5)),
            (Err(5), Ok(6), Ok(6)),
            (Err(5), Err(6), Err(6)),
        ),
    )
    def test_or(
        self,
        left: Result[int, str],
        right: Result[int, str],
        exp: Result[int, str],
    ) -> None:
        """.or_() returns the first available non-err value."""
        assert left.or_(right) == exp

    def test_or_multichain(self) -> None:
        """.or_() calls can be chained indefinitely."""
        err: Result[int, int] = Err(5)
        assert err.or_(Err(6)).or_(Err(7)).or_(Ok(8)) == Ok(8)

    def test_unwrap_is_ok(self) -> None:
        """.unwrap() returns an ok() value."""
        assert Ok(5).unwrap() == 5

    def test_unwrap_is_err(self) -> None:
        """.unwrap() raises for an error value."""
        with pytest.raises(RuntimeError):
            Err(5).unwrap()

    def test_unwrap_err_is_ok(self) -> None:
        """.unwrap_err() raises for an ok value."""
        with pytest.raises(RuntimeError):
            Ok(5).unwrap_err()

    def test_unwrap_err_is_err(self) -> None:
        """.unwrap_err() returns an error value."""
        assert Err(5).unwrap_err() == 5

    @pytest.mark.parametrize("start, alt, exp",
                             ((Ok(5), 6, 5), (Err(5), 6, 6)))
    def test_unwrap_or(self, start: Result[int, int], alt: int,
                       exp: int) -> None:
        """.unwrap_or() provides the default if the result is Err()."""
        assert start.unwrap_or(alt) == exp

    @pytest.mark.parametrize(
        "start, fn, exp",
        ((Ok(5), lambda i: i + 2, 5), (Err(5), lambda i: i + 2, 7)),
    )
    def test_unwrap_or_else(self, start: Result[int, int],
                            fn: t.Callable[[int], int], exp: int) -> None:
        """Calculates a result from Err() value if present."""
        assert start.unwrap_or_else(fn) == exp

    @pytest.mark.parametrize(
        "inst, other, eq",
        (
            (Ok(1), Ok(1), True),
            (Ok(1), Ok(2), False),
            (Ok(1), Err(1), False),
            (Ok(1), 1, False),
            (Err(1), Err(1), True),
            (Err(1), Err(2), False),
            (Err(1), Ok(1), False),
            (Err(1), 1, False),
        ),
    )
    def test_equality_inequality(self, inst: t.Any, other: t.Any,
                                 eq: bool) -> None:
        """Test equality and inequality of results."""
        assert (inst == other) is eq
        assert (inst != other) is not eq

    def test_stringify(self) -> None:
        """Repr and str representations are equivalent."""
        assert repr(Ok(1)) == str(Ok(1)) == "Ok(1)"
        assert repr(Err(1)) == str(Err(1)) == "Err(1)"
Пример #14
0
 def get(self, key: str) -> Option[t.Any]:
     """Return a value from the store."""
     if key in self._values:
         return Some(self._values[key])
     return Nothing()
Пример #15
0
 def test_nothing_singleton(self) -> None:
     """Ensure Nothing() is a singleton."""
     assert Nothing() is Nothing() is Nothing()
Пример #16
0
def _nothing(_: int) -> Option[int]:
    """Just return nothing."""
    return Nothing()
Пример #17
0
class TestResult:
    """Test the result type."""
    @pytest.mark.parametrize(
        "left, right, exp",
        (
            (Ok(2), Err("late error"), Err("late error")),
            (Err("early error"), Ok(2), Err("early error")),
            (Err("early error"), Err("late error"), Err("early error")),
            (Ok(2), Ok(3), Ok(3)),
        ),
    )
    def test_and(
        self,
        left: Result[int, str],
        right: Result[int, str],
        exp: Result[int, str],
    ) -> None:
        """Test that `and` returns an alternative for `Ok` values."""
        assert left.and_(right) == exp

    def test_and_multichain(self) -> None:
        """.and() calls can be chained indefinitely."""
        assert Ok(2).and_(Ok(3)).and_(Ok(4)).and_(Ok(5)) == Ok(5)

    @pytest.mark.parametrize(
        "start, first, second, exp",
        (
            (Ok(2), _sq, _sq, Ok(16)),
            (Ok(2), _sq, _err, Err(4)),
            (Ok(2), _err, _sq, Err(2)),
            (Ok(2), _err, _err, Err(2)),
            (Err(3), _sq, _sq, Err(3)),
        ),
    )
    def test_and_then(
        self,
        start: Result[int, int],
        first: t.Callable[[int], Result[int, int]],
        second: t.Callable[[int], Result[int, int]],
        exp: Result[int, int],
    ) -> None:
        """Test that and_then chains result-generating functions."""
        assert start.and_then(first).and_then(second) == exp

    def test_and_then_covariance(self) -> None:
        """Covariant errors are acceptable for flatmapping."""
        class MyInt(int):
            """A subclass of int."""

        def process_int(i: int) -> Result[MyInt, RuntimeError]:
            return Err(RuntimeError(f"it broke {i}"))

        start: Result[int, Exception] = Ok(5)
        # We can flatmap w/a function that takes any covariant type of
        # int or Exception. The result remains the original exception type,
        # since we cannot guarantee narrowing to the covariant type.
        flatmapped: Result[int, Exception] = start.and_then(process_int)
        assert flatmapped

    def test_flatmap(self) -> None:
        """Flatmap is an alias for and_then"""
        ok: Result[int, int] = Ok(2)
        err: Result[int, int] = Err(2)
        assert ok.flatmap(_sq) == Ok(4)
        assert err.flatmap(_sq) == Err(2)

    @pytest.mark.parametrize(
        "start, first, second, exp",
        (
            (Ok(2), _sq, _sq, Ok(2)),
            (Ok(2), _err, _sq, Ok(2)),
            (Err(3), _sq, _err, Ok(9)),
            (Err(3), _err, _err, Err(3)),
        ),
    )
    def test_or_else(
        self,
        start: Result[int, int],
        first: t.Callable[[int], Result[int, int]],
        second: t.Callable[[int], Result[int, int]],
        exp: Result[int, int],
    ) -> None:
        """Test that and_then chains result-generating functions."""
        assert start.or_else(first).or_else(second) == exp

    @pytest.mark.parametrize("start, exp",
                             ((Ok(2), Nothing()), (Err("err"), Some("err"))))
    def test_err(self, start: Result[int, str], exp: Option[str]) -> None:
        """Test converting a result to an option."""
        assert start.err() == exp

    @pytest.mark.parametrize("exc_cls", (None, IOError))
    def test_expect_raising(self, exc_cls: t.Type[Exception]) -> None:
        """Test expecting a value to be Ok()."""
        exp_exc: t.Type[Exception] = exc_cls if exc_cls else RuntimeError
        kwargs = {"exc_cls": exc_cls} if exc_cls else {}
        input_val = 2
        msg = "not what I expected"

        with pytest.raises(exp_exc) as exc_info:
            Err(input_val).expect(msg, **kwargs)

        assert msg in str(exc_info.value)
        assert str(input_val) in str(exc_info.value)

    @pytest.mark.parametrize("exc_cls", (None, IOError))
    def test_raise_if_err_raising(self, exc_cls: t.Type[Exception]) -> None:
        """Test raise_if_err for Err() values."""
        exp_exc: t.Type[Exception] = exc_cls if exc_cls else RuntimeError
        kwargs = {"exc_cls": exc_cls} if exc_cls else {}
        input_val = 2
        msg = "not what I expected"

        with pytest.raises(exp_exc) as exc_info:
            Err(input_val).raise_if_err(msg, **kwargs)

        assert msg in str(exc_info.value)
        assert str(input_val) in str(exc_info.value)

    def test_expect_ok(self) -> None:
        """Expecting an Ok() value returns the value."""
        assert Ok(2).expect("err") == 2

    def test_raise_if_err_ok(self) -> None:
        """Raise_if_err returns the value when given an Ok() value."""
        assert Ok(2).raise_if_err("err") == 2

    @pytest.mark.parametrize("exc_cls", (None, IOError))
    def test_expect_err_raising(self, exc_cls: t.Type[Exception]) -> None:
        """Test expecting a value to be Ok()."""
        exp_exc: t.Type[Exception] = exc_cls if exc_cls else RuntimeError
        kwargs = {"exc_cls": exc_cls} if exc_cls else {}
        msg = "not what I expected"

        with pytest.raises(exp_exc) as exc_info:
            Ok(2).expect_err(msg, **kwargs)

        assert msg in str(exc_info.value)

    def test_expect_err_err(self) -> None:
        """Expecting an Ok() value returns the value."""
        assert Err(2).expect_err("err") == 2

    def test_is_err(self) -> None:
        """Err() returns true for is_err()."""
        assert Err(1).is_err()
        assert not Ok(1).is_err()

    def test_is_ok(self) -> None:
        """Ok() returns true for is_ok()."""
        assert Ok(1).is_ok()
        assert not Err(1).is_ok()

    @pytest.mark.parametrize("start, exp", ((Ok(1), (1, )), (Err(1), ())))
    def test_iter(self, start: Result[int, int], exp: t.Tuple[int,
                                                              ...]) -> None:
        """iter() returns a 1-member iterator on Ok(), 0-member for Err()."""
        assert tuple(start.iter()) == exp

    @pytest.mark.parametrize("start, exp",
                             ((Ok(2), Ok(4)), (Err("foo"), Err("foo"))))
    def test_map(self, start: Result[int, str], exp: Result[int, str]) -> None:
        """.map() will map onto Ok() and ignore Err()."""
        assert start.map(lambda x: int(x**2)) == exp

    def test_map_covariance(self) -> None:
        """The input type to the map fn is covariant."""
        class MyStr(str):
            """Subclass of str."""

        def to_mystr(string: str) -> MyStr:
            return MyStr(string) if not isinstance(string, MyStr) else string

        start: Result[str, str] = Ok("foo")
        # We can assign the result to [str, str] even though we know it's
        # actually a MyStr, since MyStr is covariant with str
        end: Result[str, str] = start.map(to_mystr)
        assert end == Ok(MyStr("foo"))

    @pytest.mark.parametrize("start, exp",
                             ((Ok("foo"), Ok("foo")), (Err(2), Err("2"))))
    def test_map_err(self, start: Result[str, int], exp: Result[str,
                                                                str]) -> None:
        """.map_err() will map onto Err() and ignore Ok()."""
        assert start.map_err(str) == exp

    @pytest.mark.parametrize("start, exp",
                             ((Ok(1), Some(1)), (Err(1), Nothing())))
    def test_ok(self, start: Result[int, int], exp: Option[int]) -> None:
        """.ok() converts a result to an Option."""
        assert start.ok() == exp

    @pytest.mark.parametrize(
        "left, right, exp",
        (
            (Ok(5), Ok(6), Ok(5)),
            (Ok(5), Err(6), Ok(5)),
            (Err(5), Ok(6), Ok(6)),
            (Err(5), Err(6), Err(6)),
        ),
    )
    def test_or(
        self,
        left: Result[int, str],
        right: Result[int, str],
        exp: Result[int, str],
    ) -> None:
        """.or_() returns the first available non-err value."""
        assert left.or_(right) == exp

    def test_or_multichain(self) -> None:
        """.or_() calls can be chained indefinitely."""
        err: Result[int, int] = Err(5)
        assert err.or_(Err(6)).or_(Err(7)).or_(Ok(8)) == Ok(8)

    def test_unwrap_is_ok(self) -> None:
        """.unwrap() returns an ok() value."""
        assert Ok(5).unwrap() == 5

    def test_unwrap_is_err(self) -> None:
        """.unwrap() raises for an error value."""
        with pytest.raises(RuntimeError):
            Err(5).unwrap()

    def test_unwrap_err_is_ok(self) -> None:
        """.unwrap_err() raises for an ok value."""
        with pytest.raises(RuntimeError):
            Ok(5).unwrap_err()

    def test_unwrap_err_is_err(self) -> None:
        """.unwrap_err() returns an error value."""
        assert Err(5).unwrap_err() == 5

    @pytest.mark.parametrize("start, alt, exp",
                             ((Ok(5), 6, 5), (Err(5), 6, 6)))
    def test_unwrap_or(self, start: Result[int, int], alt: int,
                       exp: int) -> None:
        """.unwrap_or() provides the default if the result is Err()."""
        assert start.unwrap_or(alt) == exp

    @pytest.mark.parametrize(
        "start, fn, exp",
        ((Ok(5), lambda i: i + 2, 5), (Err(5), lambda i: i + 2, 7)),
    )
    def test_unwrap_or_else(self, start: Result[int, int],
                            fn: t.Callable[[int], int], exp: int) -> None:
        """Calculates a result from Err() value if present."""
        assert start.unwrap_or_else(fn) == exp

    @pytest.mark.parametrize(
        "inst, other, eq",
        (
            (Ok(1), Ok(1), True),
            (Ok(1), Ok(2), False),
            (Ok(1), Err(1), False),
            (Ok(1), 1, False),
            (Err(1), Err(1), True),
            (Err(1), Err(2), False),
            (Err(1), Ok(1), False),
            (Err(1), 1, False),
        ),
    )
    def test_equality_inequality(self, inst: t.Any, other: t.Any,
                                 eq: bool) -> None:
        """Test equality and inequality of results."""
        assert (inst == other) is eq
        assert (inst != other) is not eq

    def test_stringify(self) -> None:
        """Repr and str representations are equivalent."""
        assert repr(Ok(1)) == str(Ok(1)) == "Ok(1)"
        assert repr(Err(1)) == str(Err(1)) == "Err(1)"