.. Flask-Executor documentation master file, created by
   sphinx-quickstart on Sun Sep 23 18:52:39 2018.
   You can adapt this file completely to your liking, but it should at least
   contain the root `toctree` directive.

Flask-Executor
==============

.. module:: flask_executor

Flask-Executor is a `Flask`_ extension that makes it easy to work with :py:mod:`concurrent.futures`
in your application.

Installation
------------

Flask-Executor is available on PyPI and can be installed with pip::

    $ pip install flask-executor

Setup
------

The Executor extension can either be initialized directly::

    from flask import Flask
    from flask_executor import Executor

    app = Flask(__name__)
    executor = Executor(app)

Or through the factory method::

    executor = Executor()
    executor.init_app(app)


Configuration
-------------

To specify the type of executor to initialise, set ``EXECUTOR_TYPE`` inside your app configuration.
Valid values are ``'thread'`` (default) to initialise a
:class:`~concurrent.futures.ThreadPoolExecutor`, or ``'process'`` to initialise a
:class:`~concurrent.futures.ProcessPoolExecutor`::

    app.config['EXECUTOR_TYPE'] = 'thread'

To define the number of worker threads for a :class:`~concurrent.futures.ThreadPoolExecutor` or the
number of worker processes for a :class:`~concurrent.futures.ProcessPoolExecutor`, set
``EXECUTOR_MAX_WORKERS`` in your app configuration. Valid values are any integer or ``None`` (default)
to let :py:mod:`concurrent.futures` pick defaults for you::

    app.config['EXECUTOR_MAX_WORKERS'] = 5

If multiple executors are needed, :class:`flask_executor.Executor` can be initialised with a ``name``
parameter. Named executors will look for configuration variables prefixed with the specified ``name``
value, uppercased:

    app.config['CUSTOM_EXECUTOR_TYPE'] = 'thread'
    app.config['CUSTOM_EXECUTOR_MAX_WORKERS'] = 5
    executor = Executor(app, name='custom')


Basic Usage
-----------

Flask-Executor supports the standard :class:`concurrent.futures.Executor` methods,
:meth:`~concurrent.futures.Executor.submit` and :meth:`~concurrent.futures.Executor.map`::

    def fib(n):
        if n <= 2:
            return 1
        else:
            return fib(n-1) + fib(n-2)

    @app.route('/run_fib')
    def run_fib():
        executor.submit(fib, 5)
        executor.map(fib, range(1, 6))
        return 'OK'

Submitting a task via :meth:`~concurrent.futures.Executor.submit` returns a
:class:`flask_executor.FutureProxy` object, a subclass of 
:class:`concurrent.futures.Future` object from which you can retrieve your job status or result.


Contexts
--------

When calling :meth:`~concurrent.futures.Executor.submit` or :meth:`~concurrent.futures.Executor.map`
Flask-Executor will wrap `ThreadPoolExecutor` callables with a copy of both the current application
context and current request context. Code that must be run in these contexts or that depends on
information or configuration stored in :data:`flask.current_app`, :data:`flask.request` or
:data:`flask.g` can be submitted to the executor without modification.

Note: due to limitations in Python's default object serialisation and a lack of shared memory space between subprocesses, contexts cannot be pushed to `ProcessPoolExecutor()` workers. 


Futures
-------

:class:`flask_executor.FutureProxy` objects look and behave like normal :class:`concurrent.futures.Future`
objects, but allow `flask_executor` to override certain methods and add additional behaviours.
When submitting a callable to :meth:`~concurrent.futures.Future.add_done_callback`, callables are
wrapped with a copy of both the current application context and current request context.

You may want to preserve access to Futures returned from the executor, so that you can retrieve the
results in a different part of your application. Flask-Executor allows Futures to be stored within
the executor itself and provides methods for querying and returning them in different parts of your
app::

    @app.route('/start-task')
    def start_task():
        executor.submit_stored('calc_power', pow, 323, 1235)
        return jsonify({'result':'success'})

    @app.route('/get-result')
    def get_result():
        if not executor.futures.done('calc_power'):
            return jsonify({'status': executor.futures._state('calc_power')})
        future = executor.futures.pop('calc_power')
        return jsonify({'status': done, 'result': future.result()})


Decoration
----------

Flask-Executor lets you decorate methods in the same style as distributed task queues when using 'thread' executor type like
`Celery`_::

    @executor.job
    def fib(n):
        if n <= 2:
            return 1
        else:
            return fib(n-1) + fib(n-2)

    @app.route('/decorate_fib')
    def decorate_fib():
        fib.submit(5)
        fib.submit_stored('fibonacci', 5)
        fib.map(range(1, 6))
        return 'OK'


.. toctree::
   :maxdepth: 2
   :caption: Contents:

   api/modules


Default Callbacks
-----------------

:class:`concurrent.futures.Future` objects can have callbacks attached by using 
:meth:`~concurrent.futures.Future.add_done_callback`. Flask-Executor lets you specify default
callbacks that will be applied to all new futures created by the executor::

    def some_callback(future):
        # do something with future
    
    executor.add_default_done_callback(some_callback)

    # Callback will be added to the below task automatically
    executor.submit(pow, 323, 1235)


Propagate Exceptions
--------------------

Normally any exceptions thrown by background threads or processes will be swallowed unless explicitly
checked for. To instead surface all exceptions thrown by background tasks, Flask-Executor can add
a special default callback that raises any exceptions thrown by tasks submitted to the executor::

    app.config['EXECUTOR_PROPAGATE_EXCEPTIONS'] = True    


Indices and tables
==================

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

.. _Flask: http://flask.pocoo.org/
.. _Celery: http://www.celeryproject.org/
