blob: 24866b2e389a5f3da53d26b54f6ef7d0b5d276d2 [file] [log] [blame]
Proxy futures: ``f_proxy``
==========================
The ``f_proxy`` function allows wrapping a future with a proxy which will
pass attribute lookups and method calls through to the underlying result,
blocking as needed.
The primary goal of ``f_proxy`` is to make it possible to define APIs
which support both blocking and non-blocking coding styles.
Example
-------
Imagine we have defined some classes as follows:
.. code-block:: python
class DatabaseService:
def get_connection(self):
"""Obtain a connection to this application's database.
Returns a Future[DatabaseConnection]."""
...
class DatabaseConnection:
def query(self, sql, *params):
"""Perform an SQL query using this connection.
Returns a Future[DatabaseCursor]."""
...
class DatabaseCursor:
def __iter__(self):
"""Iterate over all results referenced by this cursor."""
...
This API could be used with a non-blocking coding style by composing
futures:
.. code-block:: python
def process_results(cursor):
for row in cursor:
# assume it does something useful with each row
...
connection = db_service.get_connection()
cursor = f_flat_map(connection, lambda c: c.query(some_sql))
results = f_map(cursor, process_results)
Or it could be used in a blocking coding style by adding calls to
``.result()``:
.. code-block:: python
connection = db_service.get_connection().result()
cursor = connection.query(some_sql).result()
results = process_results(cursor)
If we define the API as returning proxy futures (i.e. wrap each returned
future using ``f_proxy``), the above two code snippets will continue to
work, and the below example also becomes possible: using the API in a
blocking coding style without requiring explicit calls to ``.result()``:
.. code-block:: python
connection = db_service.get_connection()
cursor = connection.query(some_sql)
results = process_results(cursor)
.. autofunction:: more_executors.f_proxy(f, timeout=None)