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)