Skip to content

Commit

Permalink
Introduce flat_bind
Browse files Browse the repository at this point in the history
Fixes #97
  • Loading branch information
rohanpm committed Jan 6, 2019
1 parent a8c792e commit e707a11
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 1 deletion.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ def fetch_urls(urls):

## Changelog

### v1.16.0

- Introduced flat_bind
([#97](https://github.com/rohanpm/more-executors/issues/97))

### v1.15.0

- Fixed possible deadlock in CancelOnShutdownExecutor
Expand Down
2 changes: 2 additions & 0 deletions docs/api-reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ bind

.. automethod:: more_executors.Executors.bind

.. automethod:: more_executors.Executors.flat_bind


concurrent.futures
..................
Expand Down
4 changes: 4 additions & 0 deletions docs/user-guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ In contrast, without using :meth:`bind`, the *first* step of the pipeline -
:meth:`requests.get` - appears at the *end* of the code, which is harder
to follow.

:meth:`~more_executors.Executors.flat_bind` is also provided for use with
callables returning a future. It behaves in the same way as :meth:`bind`,
but avoids returning a nested future.


Usage of threads
----------------
Expand Down
30 changes: 29 additions & 1 deletion more_executors/_executors.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ class Executors(object):

@classmethod
def bind(cls, executor, fn):
"""Bind a callable to an executor.
"""Bind a synchronous callable to an executor.
If the callable returns a future, consider using :meth:`flat_bind` instead.
Arguments:
executor (~concurrent.futures.Executor): an executor
Expand All @@ -44,6 +46,32 @@ def bind(cls, executor, fn):
"""
return BoundCallable(executor, fn)

@classmethod
def flat_bind(cls, executor, fn):
"""Bind an asynchronous callable to an executor.
This convenience method should be used in preference to :meth:`bind`
when the bound callable returns a future, in order to avoid a nested
future in the returned value. It is equivalent to:
>>> bind(fn).with_flat_map(lambda future: future)
Arguments:
executor (~concurrent.futures.Executor): an executor
fn (callable): any function or callable which returns a future
Returns:
callable:
A new callable which, when invoked, will submit `fn` to `executor` and return
the resulting (flattened) future.
This returned callable provides the `Executors.with_*` methods, which may be
chained to further customize the behavior of the callable.
.. versionadded:: 1.16.0
"""
return cls.bind(executor, fn).with_flat_map(lambda f: f)

@classmethod
def thread_pool(cls, *args, **kwargs):
"""Create a thread pool executor.
Expand Down
4 changes: 4 additions & 0 deletions more_executors/_wrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ def bind(self, *args, **kwargs):
from more_executors._executors import Executors
return Executors.bind(self, *args, **kwargs)

def flat_bind(self, *args, **kwargs):
from more_executors._executors import Executors
return Executors.flat_bind(self, *args, **kwargs)


class CanCustomize(object):
def with_retry(self, *args, **kwargs):
Expand Down
15 changes: 15 additions & 0 deletions tests/test_bind.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ def mult(x, y):
return x*y


def async_mult(x, y):
return Executors.sync().submit(mult, x, y)


def test_single_bind():
async_mult2 = Executors. \
thread_pool(). \
Expand Down Expand Up @@ -52,6 +56,17 @@ def test_bind_then_map():
assert results == [0, 200, 400]


def test_flat_bind():
bound_async_mult = Executors.thread_pool().flat_bind(async_mult)

inputs = [(0, 1), (2, 3), (4, 5)]
expected_results = [0, 6, 20]
futures = [bound_async_mult(x, y) for (x, y) in inputs]
results = [f.result() for f in futures]

assert results == expected_results


def test_no_rebind():
bound = Executors.sync().bind(mult10)

Expand Down

0 comments on commit e707a11

Please sign in to comment.