from string import printable import pytest from hypothesis import assume, given from hypothesis.strategies import text from pfun import Dict from pfun.hypothesis_strategies import anything, dicts from pfun.maybe import Just, Nothing @given(dicts(text(printable), anything())) def test_setitem(d): new_d = d.set('key', 'value') assert new_d == Dict(d).set('key', 'value') assert d != new_d assert 'key' not in d assert Dict({'key': 'value'}) == Dict().set('key', 'value') @given(dicts(text(printable), anything())) def test_get_existing_key(d): d = d.set('key', 'value') assert d['key'] == 'value' assert d.get('key') == Just('value') @given(dicts(text(printable), anything())) def test_get_missing_key(d): assume('key' not in d)
class TestList(MonadTest): @given(lists(anything()), anything()) def test_append(self, l1, l2): assert l1.append(l2) == l1 + (l2, ) def test_empty(self): assert List().empty() == List() @given(lists(anything())) def test_left_append_identity_law(self, l): assert List() + l == l @given(lists_(anything())) def test_getitem(self, l): assume(l != []) assert l[0] == l[0] @given(lists(anything())) def test_right_append_identity_law(self, l): assert l + List() == l @given(lists(anything()), lists(anything()), lists(anything())) def test_append_associativity_law(self, x, y, z): assert (x + y) + z == x + (y + z) @given( lists(anything()), unaries(lists(anything())), unaries(lists(anything())) ) def test_associativity_law(self, l: List, f, g): assert l.and_then(f).and_then(g) == l.and_then( lambda x: f(x).and_then(g) ) @given(lists_(anything())) def test_equality(self, t): assert List(t) == List(t) @given(unaries(anything()), unaries(anything()), lists(anything())) def test_composition_law(self, f, g, l): h = compose(f, g) assert l.map(h) == l.map(g).map(f) @given(lists(anything())) def test_identity_law(self, l): assert l.map(identity) == l @given(lists_(anything()), lists_(anything())) def test_inequality(self, first, second): assume(first != second) assert List(first) != List(second) @given(anything(), unaries(lists(anything()))) def test_left_identity_law(self, v, f): assert List([v]).and_then(f) == f(v) @given(lists(anything())) def test_right_identity_law(self, l): assert l.and_then(lambda v: List([v])) == l @given(lists_(anything())) def test_reverse(self, l): assert List(l).reverse() == List(reversed(l)) @given(lists_(anything())) def test_filter(self, l): def p(v): return id(v) % 2 == 0 assert List(l).filter(p) == List(builtins.filter(p, l)) @given(lists_(integers())) def test_reduce(self, l): i = sum(l) assert List(l).reduce(lambda a, b: a + b, 0) == i @given(lists(anything(), min_size=1), anything()) def test_setitem(self, l, value): index = random.choice(range(len(l))) with pytest.raises(TypeError): l[index] = value @given(lists(anything(), min_size=1)) def test_delitem(self, l): index = random.choice(range(len(l))) with pytest.raises(TypeError): del l[index] @given(lists(anything()), lists_(anything())) def test_extend(self, l1, l2): assert l1.extend(l2) == l1 + l2 @given(lists(anything()), lists(anything())) def test_zip(self, l1, l2): assert List(l1.zip(l2)) == List(zip(l1, l2)) def test_sequence(self): assert sequence([value(v) for v in range(3)]) == value((0, 1, 2)) def test_stack_safety(self): with recursion_limit(100): sequence([value(v) for v in range(500)]) def test_filter_(self): assert filter_(lambda v: value(v % 2 == 0), range(3)) == value((0, 2)) def test_for_each(self): assert for_each(value, range(3)) == value((0, 1, 2))
class TestMaybe(MonadTest): def test_equality(self): self._test_just_equality() self._test_nothing_equality() def test_inequality(self): self._test_just_inequality() self._test_nothing_inequality() def test_identity_law(self): self._test_just_identity_law() self._test_nothing_identity_law() @given(maybes(anything())) def test_right_identity_law(self, maybe: Maybe): assert maybe.and_then(Just) == maybe @given(anything(), unaries(maybes(anything()))) def test_left_identity_law(self, value, f: Unary[Any, Maybe]): assert Just(value).and_then(f) == f(value) @given(maybes(anything()), unaries(maybes(anything())), unaries(maybes(anything()))) def test_associativity_law(self, maybe: Maybe, f: Unary[Any, Maybe], g: Unary[Any, Maybe]): assert maybe.and_then(f).and_then(g) == maybe.and_then( # type: ignore lambda x: f(x).and_then(g)) @given(anything()) def _test_just_equality(self, value): assert Just(value) == Just(value) def _test_nothing_equality(self): assert Nothing() == Nothing() @given(anything()) def _test_just_inequality(self, value): assert Just(value) != Nothing() @given(anything(), anything()) def _test_nothing_inequality(self, first, second): assume(first != second) assert Just(first) != Just(second) @given(anything()) def _test_just_identity_law(self, value): assert Just(value).map(identity) == Just(value) def _test_nothing_identity_law(self): assert Nothing().map(identity) == Nothing() @given(unaries(anything()), unaries(anything()), anything()) def test_composition_law(self, f: Unary, g: Unary, value): h = compose(f, g) assert Just(value).map(h) == Just(value).map(g).map(f) assert Nothing().map(h) == Nothing().map(g).map(f) @given(anything()) def test_just_or_else(self, value): assert Just(value).or_else(None) == value @given(anything()) def test_nothing_or_else(self, value): assert Nothing().or_else(value) is value def test_maybe_decorater(self): maybe_int = maybe(int) assert maybe_int('1') == Just(1) assert maybe_int('whoops') == Nothing() @given(anything()) def test_just_bool(self, value): assert bool(Just(value)) def test_nothing_bool(self): assert not bool(Nothing()) @given(lists(maybes(anything()))) def test_flatten(self, maybe_list): assert flatten(maybe_list) == List(m.get for m in maybe_list if m) def test_sequence(self): assert sequence([Just(v) for v in range(3)]) == Just((0, 1, 2)) def test_stack_safety(self): with recursion_limit(100): sequence([Just(v) for v in range(500)]) def test_filter(self): assert filter_(lambda v: Just(v % 2 == 0), range(3)) == Just((0, 2)) def test_for_each(self): assert for_each(Just, range(3)) == Just((0, 1, 2))
class TestTrampoline(MonadTest): @given(trampolines(anything())) def test_right_identity_law(self, trampoline): assert trampoline.and_then(Done).run() == trampoline.run() @given(anything(), unaries(trampolines(anything()))) def test_left_identity_law(self, value, f): assert Done(value).and_then(f).run() == f(value).run() @given(trampolines(anything()), unaries(trampolines(anything())), unaries(trampolines(anything()))) def test_associativity_law(self, trampoline, f, g): assert trampoline.and_then(f).and_then(g).run() == trampoline.and_then( lambda x: f(x).and_then(g)).run() @given(anything()) def test_equality(self, value): assert Done(value) == Done(value) @given(anything(), anything()) def test_inequality(self, first, second): assume(first != second) assert Done(first) != Done(second) @given(anything()) def test_identity_law(self, value): assert Done(value).map(identity).run() == Done(value).run() @given(unaries(anything()), unaries(anything()), anything()) def test_composition_law(self, f, g, value): h = compose(f, g) assert Done(value).map(g).map(f).run() == Done(value).map(h).run() def test_sequence(self): assert sequence([Done(v) for v in range(3)]).run() == (0, 1, 2) def test_stack_safety(self): with recursion_limit(100): sequence([Done(v) for v in range(500)]).run() def test_filter(self): assert filter_(lambda v: Done(v % 2 == 0), range(3)).run() == (0, 2) def test_for_each(self): assert for_each(Done, range(3)).run() == (0, 1, 2)
from string import printable from hypothesis import given from hypothesis.strategies import text from pfun import functions from pfun.hypothesis_strategies import anything, dicts, lists, unaries @given(anything(allow_nan=False)) def test_identity(a): assert functions.identity(a) == a @given(unaries(anything()), unaries(anything()), anything()) def test_compose(f, g, arg): h = functions.compose(f, g) assert h(arg) == f(g(arg)) @given(anything(), lists(anything()), dicts(text(printable), anything())) def test_always(value, args, kwargs): f = functions.always(value) assert f(*args, **kwargs) == value
class TestTrampoline(MonadTest): @pytest.mark.asyncio @given(aio_trampolines(anything())) async def test_right_identity_law(self, trampoline): assert (await trampoline.and_then(Done).run()) == (await trampoline.run()) @pytest.mark.asyncio @given(anything(), unaries(aio_trampolines(anything()))) async def test_left_identity_law(self, value, f): assert (await Done(value).and_then(f).run()) == (await f(value).run()) @pytest.mark.asyncio @given( aio_trampolines(anything()), unaries(aio_trampolines(anything())), unaries(aio_trampolines(anything())) ) async def test_associativity_law(self, trampoline, f, g): assert (await trampoline.and_then(f).and_then(g).run( )) == (await trampoline.and_then(lambda x: f(x).and_then(g)).run()) @given(anything()) def test_equality(self, value): assert Done(value) == Done(value) @given(anything(), anything()) def test_inequality(self, first, second): assume(first != second) assert Done(first) != Done(second) @pytest.mark.asyncio @given(anything()) async def test_identity_law(self, value): assert (await Done(value).map(identity).run()) == (await Done(value).run()) @pytest.mark.asyncio @given(unaries(anything()), unaries(anything()), anything()) async def test_composition_law(self, f, g, value): h = compose(f, g) assert (await Done(value).map(g).map(f).run() ) == (await Done(value).map(h).run())
import pytest from hypothesis import given from pfun import Immutable from pfun.hypothesis_strategies import anything class C(Immutable): a: Any class D(C): a2: Any @given(anything()) def test_is_immutable(a): c = C(a) with pytest.raises(FrozenInstanceError): c.a = a @given(anything(), anything()) def test_derived_is_immutable(a, a2): d = D(a, a2) with pytest.raises(FrozenInstanceError): d.a = a with pytest.raises(FrozenInstanceError): d.a2 = a2
class TestEffect(MonadTest): @settings(deadline=None) @given( effects(anything()), unaries(effects(anything())), unaries(effects(anything())), anything() ) def test_associativity_law(self, e, f, g, env): assert ( e.and_then(f).and_then(g).run(env) == e.and_then(lambda x: f(x).and_then(g)).run(env) ) @given(unaries(anything()), unaries(anything()), anything(), anything()) def test_composition_law(self, f, g, value, env): h = compose(f, g) assert ( effect.success(value).map(h).run(env) == effect.success(value).map(g).map(f).run(env) ) @given(anything(), anything()) def test_identity_law(self, value, env): assert ( effect.success(value).map(identity).run(env) == effect.success(value).run(env) ) @settings(deadline=None) @given(unaries(effects(anything())), anything(), anything()) def test_left_identity_law(self, f, value, env): assert ( effect.success(value).and_then(f).run(env) == f(value).run(env) ) @given(anything(), anything()) def test_right_identity_law(self, value, env): assert ( effect.success(value).and_then( effect.success ).run(env) == effect.success(value).run(env) ) @given(anything(), anything()) def test_equality(self, value, env): assert effect.success(value).run(env) == effect.success(value).run(env) @given(anything(), anything(), anything()) def test_inequality(self, first, second, env): assume(first != second) assert effect.success(first).run(env) != effect.success(second ).run(env) def test_depend(self): assert effect.depend().run('env') == 'env' def test_from_awaitable(self): async def f(): return 1 assert effect.from_awaitable(f()).run(None) == 1 def test_sequence(self): assert effect.sequence_async([effect.success(v) for v in range(3)] ).run(None) == (0, 1, 2) def test_sequence_generator(self): e = effect.sequence_async(effect.success(v) for v in range(3)) assert e.run(None) == (0, 1, 2) assert e.run(None) == (0, 1, 2) def test_stack_safety(self): with recursion_limit(100): effect.sequence_async([effect.success(v) for v in range(500)]).run(None) e = effect.error('') for _ in range(500): e = e.recover(lambda _: effect.error('')) e = e.recover(lambda _: effect.success('')) with recursion_limit(100): e.run(None) e = effect.success('') for _ in range(500): e = e.either() with recursion_limit(100): e.run(None) def test_filter(self): assert effect.filter_(lambda v: effect.success(v % 2 == 0), range(5)).run(None) == (0, 2, 4) def test_filter_generator(self): e = effect.filter_( lambda v: effect.success(v % 2 == 0), (v for v in range(5)) ) assert e.run(None) == (0, 2, 4) assert e.run(None) == (0, 2, 4) def test_for_each(self): assert effect.for_each(effect.success, range(3)).run(None) == (0, 1, 2) def test_either(self): success = effect.success(1) error = effect.error('error') assert success.either().run(None) == either.Right(1) error.either().run(None) == either.Left('error') def test_recover(self): success = effect.success(1) error = effect.error('error') assert success.recover(lambda e: effect.success(2)).run(None) == 1 assert error.recover(lambda e: effect.success(2)).run(None) == 2 def test_absolve(self): right = either.Right(1) left = either.Left('error') right_effect = effect.success(right) left_effect = effect.success(left) assert effect.absolve(right_effect).run(None) == 1 with pytest.raises(Exception): # todo effect.absolve(left_effect).run(None) def test_error(self): with pytest.raises(Exception): # todo effect.error('error').run(None) def test_combine(self): def f(a, b): return a + b assert effect.combine(effect.success('a'), effect.success('b'))(f).run(None) == 'ab' def test_lift(self): def f(a, b): return a + b assert effect.lift(f)(effect.success(2), effect.success(2)).run(None) == 4 def test_catch(self): def f(fail): if fail: raise ValueError() else: return 1 assert effect.catch(ValueError)(f)(False).run(None) == 1 catched_error = effect.catch(ValueError)(f)(True) with pytest.raises(ValueError): catched_error.run(None) def test_from_callable(self): def f(s: str) -> either.Either[str, str]: return either.Right(s) async def g(s: str) -> either.Either[str, str]: return f(s) assert effect.from_callable(f).run('env') == 'env' assert effect.from_callable(g).run('env') == 'env' def test_memoize(self): state = ref.Ref(()) e = state.modify(lambda t: t + ('modify was called', ) ).discard_and_then(effect.success('result')).memoize() double_e = e.discard_and_then(e) assert double_e.run(None) == 'result' assert state.value == ('modify was called', ) @settings(deadline=None) @given(effects(anything()), effects(anything())) def test_lift_cpu_bound(self, e1, e2): effect.lift_cpu_bound(lambda v1, v2: (v1, v2) )(e1, e2).run(None) == (e1.run(None), e2.run(None)) @settings(deadline=None) @given(effects(anything()), effects(anything())) def test_lift_io_bound(self, e1, e2): effect.lift_io_bound(lambda v1, v2: (v1, v2) )(e1, e2).run(None) == (e1.run(None), e2.run(None)) @settings(deadline=None) @given(effects(anything()), effects(anything())) def test_combine_cpu_bound(self, e1, e2): effect.combine_cpu_bound( e1, e2 )(lambda v1, v2: (v1, v2)).run(None) == (e1.run(None), e2.run(None)) @settings(deadline=None) @given(effects(anything()), effects(anything())) def test_combine_io_bound(self, e1, e2): effect.combine_io_bound( e1, e2 )(lambda v1, v2: (v1, v2)).run(None) == (e1.run(None), e2.run(None)) @settings(deadline=None) @given(unaries(rights(anything()))) def test_from_callable_cpu_bound(self, f): assert effect.from_cpu_bound_callable(f).run(None) == f(None).get @given(unaries(rights(anything()))) def test_from_callable_io_bound(self, f): assert effect.from_io_bound_callable(f).run(None) == f(None).get @settings(deadline=None) @given(unaries(anything())) def test_catch_cpu_bound(self, f): assert effect.catch_cpu_bound(Exception)(f)(None).run(None) == f(None) @given(unaries(anything())) def test_catch_io_bound(self, f): assert effect.catch_io_bound(Exception)(f)(None).run(None) == f(None) def test_success_repr(self): assert repr(effect.success('value')) == 'success(\'value\')' def test_error_repr(self): assert repr(effect.error('reason')) == 'error(\'reason\')' def test_and_then_repr(self): f = lambda _: _ assert ( repr(effect.success('value').and_then(f) ) == f'success(\'value\').and_then({repr(f)})' ) def test_map_repr(self): f = lambda _: _ assert ( repr(effect.success('value').map(f) ) == f'success(\'value\').map({repr(f)})' ) def test_discard_and_then_repr(self): e = effect.success('value') assert ( repr(e.discard_and_then(e) ) == 'success(\'value\').discard_and_then(success(\'value\'))' ) def test_either_repr(self): e = effect.success(0) assert repr(e.either()) == 'success(0).either()' def test_recover_repr(self): e = effect.success(0) f = lambda _: _ assert repr(e.recover(f)) == f'success(0).recover({repr(f)})' def test_memoize_repr(self): e = effect.success(0) assert repr(e.memoize()) == 'success(0).memoize()' def test_ensure_repr(self): e = effect.success(0) assert repr(e.ensure(e)) == 'success(0).ensure(success(0))' def test_depend_repr(self): assert repr(effect.depend()) == 'depend()' def test_from_awaitable_repr(self): async def f(): pass a = f() assert repr(effect.from_awaitable(a)) == f'from_awaitable({repr(a)})' def test_from_callable_repr(self): f = lambda _: _ assert repr(effect.from_callable(f)) == f'from_callable({repr(f)})' def test_from_io_bound_callable_repr(self): f = lambda _: _ assert ( repr(effect.from_io_bound_callable(f) ) == f'from_io_bound_callable({repr(f)})' ) def test_from_cpu_bound_callable_repr(self): f = lambda _: _ assert ( repr(effect.from_cpu_bound_callable(f) ) == f'from_cpu_bound_callable({repr(f)})' ) def test_catch_repr(self): f = lambda _: _ assert ( repr(effect.catch(Exception)(f)(0) ) == f'catch(<class \'Exception\'>)({repr(f)})(0)' ) def test_catch_io_bound_repr(self): f = lambda _: _ assert ( repr(effect.catch_io_bound(Exception)(f)(0) ) == f'catch_io_bound(<class \'Exception\'>)({repr(f)})(0)' ) def test_catch_cpu_bound_repr(self): f = lambda _: _ assert ( repr(effect.catch_cpu_bound(Exception)(f)(0) ) == f'catch_cpu_bound(<class \'Exception\'>)({repr(f)})(0)' ) def test_sequence_async_repr(self): e = effect.sequence_async([effect.success(0)]) assert (repr(e)) == 'sequence_async((success(0),))' def test_sequence_repr(self): e = effect.sequence([effect.success(0)]) assert (repr(e)) == 'sequence((success(0),))' def test_lift_repr(self): f = lambda _: _ assert ( repr(effect.lift(f)(effect.success(0)) ) == f'lift({repr(f)})(success(0))' ) def test_lift_io_bound_repr(self): f = lambda _: _ assert ( repr(effect.lift_io_bound(f)(effect.success(0)) ) == f'lift_io_bound({repr(f)})(success(0))' ) def test_lift_cpu_bound_repr(self): f = lambda _: _ assert ( repr(effect.lift_cpu_bound(f)(effect.success(0)) ) == f'lift_cpu_bound({repr(f)})(success(0))' ) def test_combine_repr(self): f = lambda _: _ assert ( repr(effect.combine(effect.success(0))(f) ) == f'combine(success(0))({repr(f)})' ) def test_combine_io_bound_repr(self): f = lambda _: _ assert ( repr(effect.combine_io_bound(effect.success(0))(f) ) == f'combine_io_bound(success(0))({repr(f)})' ) def test_combine_cpu_bound_repr(self): f = lambda _: _ assert ( repr(effect.combine_cpu_bound(effect.success(0))(f) ) == f'combine_cpu_bound(success(0))({repr(f)})' ) def test_filter_repr(self): f = lambda _: effect.success(True) assert repr(effect.filter_(f, [0])) == f'filter_({repr(f)})((0,))' def test_for_each_repr(self): f = lambda _: effect.success(True) assert repr(effect.for_each(f, [0])) == f'for_each({repr(f)})((0,))' def test_absolve_repr(self): assert repr(effect.absolve(effect.success(0))) == 'absolve(success(0))'
class TestEither(MonadTest): @given(eithers(anything())) def test_right_identity_law(self, either: Either): assert either.and_then(Right) == either @given(anything(), unaries(eithers(anything()))) def test_left_identity_law(self, value, f: Unary[Any, Either]): assert Right(value).and_then(f) == f(value) @given( eithers(anything()), unaries(eithers(anything())), unaries(eithers(anything())) ) def test_associativity_law( self, either: Either, f: Unary[Any, Either], g: Unary[Any, Either] ): assert either.and_then(f).and_then( g ) == either.and_then( # type: ignore lambda x: f(x).and_then(g) ) @given(anything()) def test_equality(self, value): assert Left(value) == Left(value) assert Right(value) == Right(value) @given(anything(), anything()) def test_inequality(self, first, second): assume(first != second) assert Left(first) != Left(second) assert Right(first) != Right(second) assert Left(first) != Right(first) @given(anything()) def test_identity_law(self, value): assert Left(value).map(identity) == Left(value) assert Right(value).map(identity) == Right(value) @given(unaries(anything()), unaries(anything()), anything()) def test_composition_law(self, f: Unary, g: Unary, value): h = compose(f, g) assert Left(value).map(h) == Left(value).map(g).map(f) assert Right(value).map(h) == Right(value).map(g).map(f) @given(anything(), anything()) def test_or_else(self, value, default): assert Right(value).or_else(default) == value assert Left(value).or_else(default) == default @given(anything()) def test_bool(self, value): assert bool(Right(value)) assert not bool(Left(value)) def test_either_decorator(self): result_int = either(int) assert result_int('1') == Right(1) def test_gather(self): assert gather([Right(v) for v in range(3)]) == Right((0, 1, 2)) def test_stack_safety(self): with recursion_limit(100): gather([Right(v) for v in range(500)]) def test_filter(self): assert filter_(lambda v: Right(v % 2 == 0), range(3)) == Right((0, 2)) def test_for_each(self): assert for_each(Right, range(3)) == Right((0, 1, 2))
class TestEffect(MonadTest): @settings(deadline=None) @given( effects(anything()), unaries(effects(anything())), unaries(effects(anything())), anything() ) def test_associativity_law(self, e, f, g, env): assert ( e.and_then(f).and_then(g).run(env) == e.and_then(lambda x: f(x).and_then(g)).run(env) ) @given(unaries(anything()), unaries(anything()), anything(), anything()) def test_composition_law(self, f, g, value, env): h = compose(f, g) assert ( effect.success(value).map(h).run(env) == effect.success(value).map(g).map(f).run(env) ) @given(anything(), anything()) def test_identity_law(self, value, env): assert ( effect.success(value).map(identity).run(env) == effect.success(value).run(env) ) @settings(deadline=None) @given(unaries(effects(anything())), anything(), anything()) def test_left_identity_law(self, f, value, env): assert ( effect.success(value).and_then(f).run(env) == f(value).run(env) ) @given(anything(), anything()) def test_right_identity_law(self, value, env): assert ( effect.success(value).and_then( effect.success ).run(env) == effect.success(value).run(env) ) @given(anything(), anything()) def test_equality(self, value, env): assert effect.success(value).run(env) == effect.success(value).run(env) @settings(deadline=None) @given(anything(), anything(), anything()) def test_inequality(self, first, second, env): assume(first != second) assert effect.success(first).run(env) != effect.success(second ).run(env) def test_depend(self): assert effect.depend().run('env') == 'env' def test_from_awaitable(self): async def f(): return 1 assert effect.from_awaitable(f()).run(None) == 1 def test_gather(self): assert effect.gather_async( [effect.success(v) for v in range(3)] ).run(None) == (0, 1, 2) assert effect.gather( [effect.success(v) for v in range(3)] ).run(None) == (0, 1, 2) def test_gather_generator(self): e = effect.gather_async(effect.success(v) for v in range(3)) assert e.run(None) == (0, 1, 2) assert e.run(None) == (0, 1, 2) def test_stack_safety(self): with recursion_limit(100): effect.gather_async([effect.success(v) for v in range(500)]).run(None) e = effect.error('') for _ in range(500): e = e.recover(lambda _: effect.error('')) e = e.recover(lambda _: effect.success('')) with recursion_limit(100): e.run(None) e = effect.success('') for _ in range(500): e = e.either() with recursion_limit(100): e.run(None) def test_filter(self): assert effect.filter_(lambda v: effect.success(v % 2 == 0), range(5)).run(None) == (0, 2, 4) def test_filter_async(self): assert effect.filter_async(lambda v: effect.success(v % 2 == 0), range(5)).run(None) == (0, 2, 4) def test_filter_generator(self): e = effect.filter_( lambda v: effect.success(v % 2 == 0), (v for v in range(5)) ) assert e.run(None) == (0, 2, 4) assert e.run(None) == (0, 2, 4) def test_for_each(self): assert effect.for_each(effect.success, range(3)).run(None) == (0, 1, 2) def test_for_each_async(self): assert (effect.for_each_async(effect.success, range(3)).run(None) == (0, 1, 2)) def test_either(self): success = effect.success(1) error = effect.error('error') assert success.either().run(None) == either.Right(1) assert error.either().run(None) == either.Left('error') def test_recover(self): success = effect.success(1) error = effect.error('error') assert success.recover(lambda e: effect.success(2)).run(None) == 1 assert error.recover(lambda e: effect.success(2)).run(None) == 2 def test_absolve(self): right = either.Right(1) left = either.Left('error') right_effect = effect.success(right) left_effect = effect.success(left) assert effect.absolve(right_effect).run(None) == 1 with pytest.raises(Exception): # todo effect.absolve(left_effect).run(None) def test_error(self): with pytest.raises(Exception): # todo effect.error('error').run(None) def test_combine(self): def f(a, b): return a + b assert effect.combine(effect.success('a'), effect.success('b'))(f).run(None) == 'ab' def test_combine_async(self): def f(a, b): return a + b assert effect.combine_async(effect.success('a'), effect.success('b'))(f).run(None) == 'ab' def test_lift(self): def f(a, b): return a + b assert effect.lift(f)(effect.success(2), effect.success(2)).run(None) == 4 def test_lift_async(self): def f(a, b): return a + b assert effect.lift_async(f)(effect.success(2), effect.success(2)).run(None) == 4 def test_catch(self): def f(fail): if fail: raise ValueError() else: return 1 assert effect.catch(ValueError)(f)(False).run(None) == 1 catched_error = effect.catch(ValueError)(f)(True) with pytest.raises(ValueError): catched_error.run(None) def test_from_callable(self): def f(s: str) -> either.Either[str, str]: return either.Right(s) async def g(s: str) -> either.Either[str, str]: return f(s) assert effect.from_callable(f).run('env') == 'env' assert effect.from_callable(g).run('env') == 'env' def test_memoize(self): s = state.State(()) e = s.modify( lambda t: t + ('modify was called', ) ).discard_and_then(effect.success('result')).memoize() double_e = e.discard_and_then(e) assert double_e.run(None) == 'result' assert s.value == ('modify was called', ) @settings(deadline=None) @given(effects(anything()), effects(anything())) def test_lift_cpu_bound(self, e1, e2): assert effect.lift_cpu_bound( lambda v1, v2: (v1, v2) )(e1, e2).run(None) == (e1.run(None), e2.run(None)) @settings(deadline=None) @given(effects(anything()), effects(anything())) def test_lift_io_bound(self, e1, e2): assert effect.lift_io_bound( lambda v1, v2: (v1, v2) )(e1, e2).run(None) == (e1.run(None), e2.run(None)) @settings(deadline=None) @given(effects(anything()), effects(anything())) def test_combine_cpu_bound(self, e1, e2): assert effect.combine_cpu_bound( e1, e2 )(lambda v1, v2: (v1, v2)).run(None) == (e1.run(None), e2.run(None)) @settings(deadline=None) @given(effects(anything()), effects(anything())) def test_combine_io_bound(self, e1, e2): assert effect.combine_io_bound( e1, e2 )(lambda v1, v2: (v1, v2)).run(None) == (e1.run(None), e2.run(None)) @settings(deadline=None) @given(unaries(rights(anything()))) def test_from_callable_cpu_bound(self, f): assert effect.from_cpu_bound_callable(f).run(None) == f(None).get @given(unaries(rights(anything()))) def test_from_callable_io_bound(self, f): assert effect.from_io_bound_callable(f).run(None) == f(None).get @settings(deadline=None) @given(unaries(anything())) def test_catch_cpu_bound(self, f): assert effect.catch_cpu_bound(Exception)(f)(None).run(None) == f(None) @given(unaries(anything())) def test_catch_io_bound(self, f): assert effect.catch_io_bound(Exception)(f)(None).run(None) == f(None) @pytest.mark.asyncio @given(anything()) async def test_process_and_thread_pool_initialized_lazily(self, value): with ExitStack() as stack: env = effect.RuntimeEnv(None, stack, 1, 1) await effect.success(value).do(env) assert env.process_executor is None assert env.thread_executor is None def test_race(self): async def f(): await asyncio.sleep(10) return 'f' async def g(): return 'g' e1 = effect.from_awaitable(f()) e2 = effect.from_awaitable(g()) assert e1.race(e2).run(None) == 'g' def test_timeout(self): async def f(): await asyncio.sleep(10) return 'f' e = effect.from_awaitable(f()) with pytest.raises(asyncio.TimeoutError): e.timeout(timedelta(milliseconds=1)).run(MockModules()) def test_repeat(self): s = schedule.recurs(2, schedule.spaced(timedelta(seconds=1))) assert effect.success(0).repeat(s).run(MockModules()) == (0, 0) def test_repeat_error(self): s = schedule.recurs(2, schedule.spaced(timedelta(seconds=1))) assert (effect.error('whoops').repeat(s).either().run(MockModules()) == either.Left('whoops')) def test_retry(self): s = schedule.recurs(2, schedule.spaced(timedelta(seconds=1))) assert (effect.error('whoops').retry(s).either().run(MockModules()) == either.Left(('whoops', 'whoops'))) def test_retry_success(self): s = schedule.recurs(2, schedule.spaced(timedelta(seconds=1))) assert effect.success(0).retry(s).run(MockModules()) == 0 @settings(deadline=None) @given(anything(), unaries(anything())) def test_purify(self, x, f): assert effect.purify(f)(x).run(None) == f(x) assert effect.purify_io_bound(f)(x).run(None) == f(x) assert effect.purify_cpu_bound(f)(x).run(None) == f(x) def test_success_repr(self): assert repr(effect.success('value')) == 'success(\'value\')' def test_error_repr(self): assert repr(effect.error('reason')) == 'error(\'reason\')' def test_and_then_repr(self): f = lambda _: _ assert ( repr(effect.success('value').and_then(f) ) == f'success(\'value\').and_then({repr(f)})' ) def test_map_repr(self): f = lambda _: _ assert ( repr(effect.success('value').map(f) ) == f'success(\'value\').map({repr(f)})' ) def test_discard_and_then_repr(self): e = effect.success('value') assert ( repr(e.discard_and_then(e) ) == 'success(\'value\').discard_and_then(success(\'value\'))' ) def test_either_repr(self): e = effect.success(0) assert repr(e.either()) == 'success(0).either()' def test_recover_repr(self): e = effect.success(0) f = lambda _: _ assert repr(e.recover(f)) == f'success(0).recover({repr(f)})' def test_memoize_repr(self): e = effect.success(0) assert repr(e.memoize()) == 'success(0).memoize()' def test_ensure_repr(self): e = effect.success(0) assert repr(e.ensure(e)) == 'success(0).ensure(success(0))' def test_depend_repr(self): assert repr(effect.depend()) == 'depend()' @pytest.mark.filterwarnings("ignore:coroutine .+ was never awaited") def test_from_awaitable_repr(self): async def f(): pass a = f() assert repr(effect.from_awaitable(a)) == f'from_awaitable({repr(a)})' def test_from_callable_repr(self): f = lambda _: _ assert repr(effect.from_callable(f)) == f'from_callable({repr(f)})' def test_from_io_bound_callable_repr(self): f = lambda _: _ assert ( repr(effect.from_io_bound_callable(f) ) == f'from_io_bound_callable({repr(f)})' ) def test_from_cpu_bound_callable_repr(self): f = lambda _: _ assert ( repr(effect.from_cpu_bound_callable(f) ) == f'from_cpu_bound_callable({repr(f)})' ) def test_catch_repr(self): f = lambda _: _ assert ( repr(effect.catch(Exception)(f)(0) ) == f'catch(<class \'Exception\'>)({repr(f)})(0)' ) def test_catch_io_bound_repr(self): f = lambda _: _ assert ( repr(effect.catch_io_bound(Exception)(f)(0) ) == f'catch_io_bound(<class \'Exception\'>)({repr(f)})(0)' ) def test_catch_cpu_bound_repr(self): f = lambda _: _ assert ( repr(effect.catch_cpu_bound(Exception)(f)(0) ) == f'catch_cpu_bound(<class \'Exception\'>)({repr(f)})(0)' ) def test_gather_async_repr(self): e = effect.gather_async([effect.success(0)]) assert (repr(e)) == 'gather_async((success(0),))' def test_gather_repr(self): e = effect.gather([effect.success(0)]) assert (repr(e)) == 'gather((success(0),))' def test_lift_repr(self): f = lambda _: _ assert ( repr(effect.lift(f)(effect.success(0)) ) == f'lift({repr(f)})(success(0))' ) def test_lift_io_bound_repr(self): f = lambda _: _ assert ( repr(effect.lift_io_bound(f)(effect.success(0)) ) == f'lift_io_bound({repr(f)})(success(0))' ) def test_lift_cpu_bound_repr(self): f = lambda _: _ assert ( repr(effect.lift_cpu_bound(f)(effect.success(0)) ) == f'lift_cpu_bound({repr(f)})(success(0))' ) def test_combine_repr(self): f = lambda _: _ assert ( repr(effect.combine(effect.success(0))(f) ) == f'combine(success(0))({repr(f)})' ) def test_combine_io_bound_repr(self): f = lambda _: _ assert ( repr(effect.combine_io_bound(effect.success(0))(f) ) == f'combine_io_bound(success(0))({repr(f)})' ) def test_combine_cpu_bound_repr(self): f = lambda _: _ assert ( repr(effect.combine_cpu_bound(effect.success(0))(f) ) == f'combine_cpu_bound(success(0))({repr(f)})' ) def test_filter_repr(self): f = lambda _: effect.success(True) assert repr(effect.filter_(f, [0])) == f'filter_({repr(f)})((0,))' def test_for_each_repr(self): f = lambda _: effect.success(True) assert repr(effect.for_each(f, [0])) == f'for_each({repr(f)})((0,))' def test_absolve_repr(self): assert repr(effect.absolve(effect.success(0))) == 'absolve(success(0))' def test_race_repr(self): assert (repr(effect.success(0).race(effect.success(0))) == 'success(0).race(success(0))') def test_timeout_repr(self): assert (repr(effect.success(0).timeout(timedelta(seconds=1))) == 'success(0).timeout(datetime.timedelta(seconds=1))')