Skip to content

Commit b968d69

Browse files
committed
Adds async bind with futures, refs #274
1 parent 84a7bc4 commit b968d69

File tree

5 files changed

+190
-3
lines changed

5 files changed

+190
-3
lines changed

returns/_generated/futures/_future_result.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,17 @@ async def async_bind_future(
110110
return container # type: ignore[return-value]
111111

112112

113+
async def async_bind_async_future(
114+
function: Callable[[_ValueType], Awaitable['Future[_NewValueType]']],
115+
inner_value: Awaitable[Result[_ValueType, _ErrorType]],
116+
) -> Result[_NewValueType, _ErrorType]:
117+
"""Async binds a container returning ``IO`` over a value."""
118+
container = await inner_value
119+
if isinstance(container, Result.success_type):
120+
return await async_from_success(await function(container.unwrap()))
121+
return container # type: ignore[return-value]
122+
123+
113124
async def async_fix(
114125
function: Callable[[_ErrorType], _NewValueType],
115126
inner_value: Awaitable[Result[_ValueType, _ErrorType]],

returns/context/requires_context_future_result.py

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -600,7 +600,7 @@ def bind_future(
600600
601601
>>> failed = RequiresContextFutureResult.from_failure(':(')
602602
>>> assert anyio.run(
603-
... failed.bind_future_result(function),
603+
... failed.bind_future(function),
604604
... RequiresContextFutureResult.empty,
605605
... ) == IOFailure(':(')
606606
@@ -647,6 +647,81 @@ def bind_future_result(
647647
lambda deps: self(deps).bind(function),
648648
)
649649

650+
def bind_async_future(
651+
self,
652+
function: Callable[[_ValueType], Awaitable[Future[_NewValueType]]],
653+
) -> 'RequiresContextFutureResult[_EnvType, _NewValueType, _ErrorType]':
654+
"""
655+
Binds ``Future`` returning async function to the current container.
656+
657+
.. code:: python
658+
659+
>>> import anyio
660+
>>> from returns.context import RequiresContextFutureResult
661+
>>> from returns.future import Future
662+
>>> from returns.io import IOSuccess, IOFailure
663+
664+
>>> def function(num: int) -> Future[int]:
665+
... return Future.from_value(num + 1)
666+
667+
>>> assert anyio.run(
668+
... RequiresContextFutureResult.from_value(1).bind_future(
669+
... function,
670+
... ),
671+
... RequiresContextFutureResult.empty,
672+
... ) == IOSuccess(2)
673+
674+
>>> failed = RequiresContextFutureResult.from_failure(':(')
675+
>>> assert anyio.run(
676+
... failed.bind_future(function),
677+
... RequiresContextFutureResult.empty,
678+
... ) == IOFailure(':(')
679+
680+
"""
681+
return RequiresContextFutureResult(
682+
lambda deps: self(deps).bind_async_future(function),
683+
)
684+
685+
def bind_async_future_result(
686+
self,
687+
function: Callable[
688+
[_ValueType],
689+
Awaitable[FutureResult[_NewValueType, _ErrorType]],
690+
],
691+
) -> 'RequiresContextFutureResult[_EnvType, _NewValueType, _ErrorType]':
692+
"""
693+
Bind ``FutureResult`` returning async function to the current container.
694+
695+
.. code:: python
696+
697+
>>> import anyio
698+
>>> from returns.context import RequiresContextFutureResult
699+
>>> from returns.future import FutureResult
700+
>>> from returns.io import IOSuccess, IOFailure
701+
702+
>>> async def function(num: int) -> FutureResult[int, str]:
703+
... return FutureResult.from_value(num + 1)
704+
705+
>>> assert anyio.run(
706+
... RequiresContextFutureResult.from_value(
707+
... 1,
708+
... ).bind_async_future_result(
709+
... function,
710+
... ),
711+
... RequiresContextFutureResult.empty,
712+
... ) == IOSuccess(2)
713+
714+
>>> failed = RequiresContextFutureResult.from_failure(':(')
715+
>>> assert anyio.run(
716+
... failed.bind_async_future_result(function),
717+
... RequiresContextFutureResult.empty,
718+
... ) == IOFailure(':(')
719+
720+
"""
721+
return RequiresContextFutureResult(
722+
lambda deps: self(deps).bind_async(function),
723+
)
724+
650725
def fix(
651726
self, function: Callable[[_ErrorType], _NewValueType],
652727
) -> 'RequiresContextFutureResult[_EnvType, _NewValueType, _ErrorType]':

returns/future.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -879,6 +879,39 @@ def bind_future(
879879
function, self._inner_value,
880880
))
881881

882+
def bind_async_future(
883+
self,
884+
function: Callable[[_ValueType], Awaitable['Future[_NewValueType]']],
885+
) -> 'FutureResult[_NewValueType, _ErrorType]':
886+
"""
887+
Composes a container and ``async`` function returning ``Future``.
888+
889+
Similar to :meth:`~FutureResult.bind_future`
890+
but works with async functions.
891+
892+
.. code:: python
893+
894+
>>> import anyio
895+
>>> from returns.future import Future, FutureResult
896+
>>> from returns.io import IOSuccess, IOFailure
897+
898+
>>> async def coroutine(x: int) -> Future[str]:
899+
... return Future.from_value(str(x + 1))
900+
901+
>>> assert anyio.run(
902+
... FutureResult.from_value(1).bind_async_future,
903+
... coroutine,
904+
... ) == IOSuccess('2')
905+
>>> assert anyio.run(
906+
... FutureResult.from_failure(1).bind_async,
907+
... coroutine,
908+
... ) == IOFailure(1)
909+
910+
"""
911+
return FutureResult(_future_result.async_bind_async_future(
912+
function, self._inner_value,
913+
))
914+
882915
def unify(
883916
self,
884917
function: Callable[

typesafety/test_context/test_requires_context_future_result/test_requires_context_future_result.yml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,20 @@
101101
reveal_type(x.bind_io(test)) # N: Revealed type is 'returns.context.requires_context_future_result.RequiresContextFutureResult[builtins.str, builtins.bool*, builtins.float]'
102102
103103
104+
- case: requires_context_futureresult_bind_future
105+
disable_cache: true
106+
main: |
107+
from returns.context import RequiresContextFutureResult
108+
from returns.future import Future
109+
110+
x: RequiresContextFutureResult[str, int, float]
111+
112+
def test(param: int) -> Future[bool]:
113+
...
114+
115+
reveal_type(x.bind_future(test)) # N: Revealed type is 'returns.context.requires_context_future_result.RequiresContextFutureResult[builtins.str, builtins.bool*, builtins.float]'
116+
117+
104118
- case: requires_context_futureresult_bind_future_result
105119
disable_cache: true
106120
main: |
@@ -115,6 +129,34 @@
115129
reveal_type(x.bind_future_result(test)) # N: Revealed type is 'returns.context.requires_context_future_result.RequiresContextFutureResult[builtins.str, builtins.bool*, builtins.float]'
116130
117131
132+
- case: requires_context_futureresult_bind_async_future
133+
disable_cache: true
134+
main: |
135+
from returns.context import RequiresContextFutureResult
136+
from returns.future import Future
137+
138+
x: RequiresContextFutureResult[str, int, float]
139+
140+
async def test(param: int) -> Future[bool]:
141+
...
142+
143+
reveal_type(x.bind_async_future(test)) # N: Revealed type is 'returns.context.requires_context_future_result.RequiresContextFutureResult[builtins.str, builtins.bool*, builtins.float]'
144+
145+
146+
- case: requires_context_futureresult_bind_async_future_result
147+
disable_cache: true
148+
main: |
149+
from returns.context import RequiresContextFutureResult
150+
from returns.future import FutureResult
151+
152+
x: RequiresContextFutureResult[str, int, float]
153+
154+
async def test(param: int) -> FutureResult[bool, float]:
155+
...
156+
157+
reveal_type(x.bind_async_future_result(test)) # N: Revealed type is 'returns.context.requires_context_future_result.RequiresContextFutureResult[builtins.str, builtins.bool*, builtins.float]'
158+
159+
118160
- case: requires_context_future_result_bind_context
119161
disable_cache: true
120162
main: |

typesafety/test_future/test_future_result_container/test_future_result_base.yml

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@
1515
main: |
1616
from returns.future import FutureResult
1717
18-
def bind_future(arg: int) -> FutureResult[float, str]:
18+
def bind(arg: int) -> FutureResult[float, str]:
1919
...
2020
2121
first: FutureResult[int, str]
2222
23-
reveal_type(first.bind(bind_future)) # N: Revealed type is 'returns.future.FutureResult[builtins.float*, builtins.str]'
23+
reveal_type(first.bind(bind)) # N: Revealed type is 'returns.future.FutureResult[builtins.float*, builtins.str]'
2424
2525
2626
- case: future_result_bind_awaitable
@@ -77,6 +77,32 @@
7777
reveal_type(first.bind_ioresult(bind)) # N: Revealed type is 'returns.future.FutureResult[builtins.float*, builtins.str]'
7878
7979
80+
- case: future_result_bind_future
81+
disable_cache: true
82+
main: |
83+
from returns.future import Future, FutureResult
84+
85+
def bind_future(arg: int) -> Future[float]:
86+
...
87+
88+
first: FutureResult[int, str]
89+
90+
reveal_type(first.bind_future(bind_future)) # N: Revealed type is 'returns.future.FutureResult[builtins.float*, builtins.str]'
91+
92+
93+
- case: future_result_bind_async_future
94+
disable_cache: true
95+
main: |
96+
from returns.future import Future, FutureResult
97+
98+
async def bind_future(arg: int) -> Future[float]:
99+
...
100+
101+
first: FutureResult[int, str]
102+
103+
reveal_type(first.bind_async_future(bind_future)) # N: Revealed type is 'returns.future.FutureResult[builtins.float*, builtins.str]'
104+
105+
80106
- case: future_result_map
81107
disable_cache: true
82108
main: |

0 commit comments

Comments
 (0)