import logging.config from kombu import Queue from celery import Celery from celery.result import AsyncResult from celery.signals import setup_logging, task_postrun from functools import partial from lightflow.queue.const import DefaultJobQueueName from lightflow.queue.pickle import patch_celery from lightflow.models.exceptions import ConfigOverwriteError LIGHTFLOW_INCLUDE = ['lightflow.queue.jobs', 'lightflow.models'] def create_app(config): """ Create a fully configured Celery application object. Args: config (Config): A reference to a lightflow configuration object. Returns: Celery: A fully configured Celery application object. """ # configure the celery logging system with the lightflow settings setup_logging.connect(partial(_initialize_logging, config), weak=False) task_postrun.connect(partial(_cleanup_workflow, config), weak=False) # patch Celery to use cloudpickle instead of pickle for serialisation patch_celery() # create the main celery app and load the configuration app = Celery('lightflow') app.conf.update(**config.celery) # overwrite user supplied settings to make sure celery works with lightflow app.conf.update( task_serializer='pickle', accept_content=['pickle'], result_serializer='pickle', task_default_queue=DefaultJobQueueName.Task ) if isinstance(app.conf.include, list): app.conf.include.extend(LIGHTFLOW_INCLUDE) else: if len(app.conf.include) > 0: raise ConfigOverwriteError( 'The content in the include config will be overwritten') app.conf.include = LIGHTFLOW_INCLUDE return app def _initialize_logging(config, **kwargs): """ Hook into the logging system of celery. Connects the local logging system to the celery logging system such that both systems can coexist next to each other. Args: config (Config): Reference to the configuration object from which the logging settings are retrieved. **kwargs: Keyword arguments from the hook. """ logging.config.dictConfig(config.logging) def _cleanup_workflow(config, task_id, args, **kwargs): """ Cleanup the results of a workflow when it finished. Connects to the postrun signal of Celery. If the signal was sent by a workflow, remove the result from the result backend. Args: task_id (str): The id of the task. args (tuple): The arguments the task was started with. **kwargs: Keyword arguments from the hook. """ from lightflow.models import Workflow if isinstance(args[0], Workflow): if config.celery['result_expires'] == 0: AsyncResult(task_id).forget()