Flask-TaskX ====================================== .. module:: flask_taskx In the modern era, a recurrent feature required in a web application is the ability to execute non-blocking and asynchronous tasks. The **Flask-TaskX** extension provides a simple interface to set up task functions within your `Flask`_ application and scheduled them for immediate or later execution. Links ----- * `documentation `_ * `source `_ * :doc:`changelog ` .. contents:: Table of Contents :depth: 3 Installing **Flask-TaskX** -------------------------- Install with **pip** and **easy_install**:: pip install Flask-TaskX or download the latest version from version control:: git clone https://github.com/carrasquel/flask-taskx.git cd flask-taskx python setup.py install If you are using **virtualenv**, it is assumed that you are installing ``flask-taskx`` in the same virtualenv as your Flask application(s). How does **Flask-TaskX** work? ------------------------------ **Flask-TaskX** uses the **APScheduler** library as jobs execution engine, this execution that runs the jobs is called **worker** and it manages the execution of scheduled tasks. In the context of **Flask-TaskX** a **task** is a function that has been decorated in order to mark it as task. This tasks are enqueue and executed by the worker when the scheduled time is met. This **queue** is a Database-Backed Queue and it could be defined in the same database used by the Flask application or another SQL Relational Database. Configuring **Flask-TaskX** --------------------------- **Flask-TaskX** is configured through the standard Flask config API. These are the available options (each is explained later in the documentation): * **TASKER_DATABASE_URI** : default **''** * **TASKER_DRIVER** : default **'sqlite'** * **TASKER_LOOP_INTERVAL** : default **5** * **TASKER_DEBUG** : default **app.debug** Tasks are managed by a worker, this can be achieved with an instance of one of the followings, ``BackgroundTaskWorker`` instance or ``BlockingTaskWorker`` instance:: from flask import Flask from flask_taskx import BackgroundTaskWorker app = Flask(__name__) task_worker = BackgroundTaskWorker(app) In this case all tasks will be executed using the configuration values of the application that was passed to the ``BackgroundTaskWorker`` class constructor. Alternatively you can set up your ``BackgroundTaskWorker`` instance later at configuration time, using the ``init_app`` method:: task_worker = BackgroundTaskWorker() app = Flask(__name__) task_worker.init_app(app) In this case tasks will be executed using the configuration values from Flask's ``current_app`` context global. This is useful if you have multiple applications running in the same process but with different configuration options. Running **Flask-TaskX** ----------------------- Finally, once you have configured your application, you can start your task worker:: task_worker.start() app.run() Difference between **BackgroundTaskWorker** and **BlockingTaskWorker** ---------------------------------------------------------------------- **Flask-TaskX** was designed to schedule and execute tasks within the same context of a `Flask`_ application, this means that inside your tasks definitions, you can invoke any function, class or method that depends and requires the application context. So the decision to choose the background or blocking instance will depend on your services topology, if you are planning to run the `Flask`_ application service in the same machine as the **Flask-TaskX** worker, the choice in this case is to use an instance of `BackgroundTaskWorker`, this will not block the `Flask`_ application to start serving. `Flask`_ application and `Flask-TaskX` application on the same machine:: task_worker = BackgroudTaskWorker() app = Flask(__name__) task_worker.init_app(app) task_worker.start() app.run() On the other hand, if you are planning to run the `Flask`_ application service in a different machine than the **Flask-TaskX** worker, the choice in this case is to use an instance of ``BlockingTaskWorker``, this will block the `Flask`_ application to start serving. `Flask`_ application machine:: task_worker = BlockingTaskWorker() app = Flask(__name__) task_worker.init_app(app) app.run() **Flask-TaskX** worker machine:: task_worker = BlockingTaskWorker() app = Flask(__name__) task_worker.init_app(app) task_worker.start() One machine must execute the application service and the other one the task worker. Defining tasks -------------- Tasks are functions to be registered for later execution. To define a task we have to use the decorator method ``define_task`` from a task worker:: @task_worker.define_task def email_task(**kwargs): msg = send_message(**kwargs) This will turn the same function into a appliable function, then you can import this function into another module an schedule the task for inmediate execution by the worker. Executing tasks --------------- Once a task is defined it can be executed from anywhere in a `Flask` application using the ``apply`` method from the decorated function:: from tasks import email_task email_task.apply(**payload) This call will enqueue a task and it will be executed by the task worker as soon as possible. Defining cron tasks ------------------- This is the most powerful of the built-in triggers in `Flask-TaskX`. You can specify a variety of different expressions on each field, and when determining the next execution time, it finds the earliest possible time that satisfies the conditions in every field. This behavior resembles the “Cron” utility found in most UNIX-like operating systems.:: from datetime import date @task_worker.define_cron_task(month='6-8,11-12', day='3rd fri', hour='0-3') def email_task(**kwargs): msg = send_message(**kwargs) Defining date tasks ------------------- This is the simplest possible method of scheduling a task. It schedules a task to be executed once at the specified time. It is `Flask-TaskX` equivalent to the UNIX “at” command. The ``run_date`` can be given either as a date/datetime object or text (in the ISO 8601 format).:: from datetime import date @task_worker.define_date_task(run_date=date(2009, 11, 6), args=['email@email.com']) def email_task(**kwargs): msg = send_message(**kwargs) Queues available in **Flask-TaskX** ----------------------------------- **Flask-TaskX** has it's own implementation of a queue, it is a Database-Backed Queue, this means that the Queue is implemented using a relational database, though we know this is an anti-pattern, we have reasons to believe that for some purposes, a queue implemented this way is the proper approach to enqueue asynchronous tasks. Since **Flask-TaskX** was designed to be implemented in **Flask**, most likely if you are developing a web application you are using some Relational Database(SQL), in order for **Flask-TaskX** to connect to database the config value **TASKER_DATABASE_URI** must be defined, if this config value is not provided **SQLALCHEMY_DATABASE_URI** from the Flask app will be used instead. This way you can use the same relational database used by your Flask models or a different database just to store the **Flask-TaskX** Queue. Running **Flask-TaskX** from CLI -------------------------------- You can execute the task worker from the command line using the ``taskx`` command:: taskx run This will try to find a Flask application instance the same way the command ``flask run`` does it, using the **FLASK_APP** environment variable or inside a ``app.py`` python module. If a task worker has been appropriately instantiated and configured in the codebase, the task worker will be found and started. API --- .. module:: flask_taskx .. autoclass:: BackgroundTaskWorker :members: init_app .. autoclass:: BlockingTaskWorker :members: init_app .. autoclass:: BaseTaskWorker :members: define_task .. autoclass:: BaseTaskWorker :members: define_cron_task .. autoclass:: BaseTaskWorker :members: define_date_task .. autoclass:: BaseTask :members: apply .. _Flask: https://flask.pocoo.org .. _GitHub: https://github.com/carrasquel/flask-taskx .. _Redis: https://redis.io/ .. _RabbitMQ: https://www.rabbitmq.com/