Core Application & Configuration
The core of the Celery framework is the application object and its configuration system. This implementation relies on a lazy-loading mechanism that allows developers to define tasks and set configuration parameters before the application is fully initialized.
The Celery Application Object
The Celery class, defined in celery/app/base.py, serves as the central entry point for the framework. It manages the task registry, configuration, and broker connections.
When instantiating a Celery app, the main argument is used as the namespace for auto-generated task names. This is particularly important when running standalone scripts.
from celery import Celery
# 'myapp' is the 'main' name for this application
app = Celery('myapp', broker='amqp://guest@localhost//')
@app.task
def add(x, y):
return x + y
In the example above (from examples/app/myapp.py), the add task will be named myapp.add. If main were omitted, Celery would attempt to determine the module name automatically.
Lazy Configuration Management
Configuration in this codebase is managed lazily through the app.conf attribute. During initialization in Celery.__init__, app.conf is set to a Settings object wrapping a PendingConfiguration proxy.
PendingConfiguration
The PendingConfiguration class (in celery/app/base.py) allows you to set configuration values on the app before it is finalized. However, as soon as any key is accessed, it triggers the finalization of the configuration.
# In celery/app/base.py
class PendingConfiguration(UserDict, AttributeDictMixin):
# ...
@cached_property
def data(self):
return self.callback() # Triggers _finalize_pending_conf
Settings Object
Once finalized, app.conf becomes a Settings object (defined in celery/app/utils.py). This object is a specialized dictionary that provides:
- Dual Key Support: It handles both the old uppercase (e.g.,
CELERY_BROKER_URL) and new lowercase (e.g.,broker_url) setting names using_old_key_to_newand_new_key_to_oldmapping functions. - Property Access: Common settings like
broker_urlandresult_backendare available as properties that also check environment variables.
Loading Configuration
There are several ways to provide configuration to the application.
config_from_object
The most common method is config_from_object, which can take a module name or a configuration object.
# examples/django/proj/celery.py
app = Celery('proj')
app.config_from_object('django.conf:settings', namespace='CELERY')
When a namespace is provided, Celery will only look for settings starting with that prefix (e.g., CELERY_BROKER_URL).
config_from_envvar
The config_from_envvar method allows loading configuration from a module specified in an environment variable:
# In celery/app/base.py
def config_from_envvar(self, variable_name, silent=False, force=False):
module_name = os.environ.get(variable_name)
# ...
return self.config_from_object(module_name, silent=silent, force=force)
detect_settings
The detect_settings function in celery/app/utils.py is responsible for merging different configuration sources. It enforces a strict rule: you cannot mix old and new setting formats in the same source. If a mixture is detected, it raises an ImproperlyConfigured error with suggestions for renaming.
Loaders
Loaders are responsible for the actual mechanics of reading configuration and importing modules.
- AppLoader: The default loader used when a custom
Celeryinstance is created. - DefaultLoader: Used for the "default app" (when no explicit app is created). It looks for a
celeryconfig.pymodule by default.
Loaders also manage the lifecycle of the worker, providing hooks like on_worker_init and on_worker_shutdown (defined in celery/loaders/base.py).
Application Finalization
Finalization is the process where the application transitions from a pending state to a ready state. This happens automatically when app.conf is accessed or when tasks are registered, provided autofinalize is set to True (the default).
The finalize method in celery/app/base.py performs several critical steps:
- Loads built-in tasks: Imports tasks required for Celery's internal operations.
- Triggers Fixups: Executes "fixups" like the Django integration (
celery.fixups.django:fixup). - Evaluates Pending Tasks: Any tasks defined using the
@app.taskdecorator before finalization are now fully bound to the app. - Signals: Dispatches the
on_after_finalizesignal.
If autofinalize is disabled, attempting to use the task registry before calling app.finalize() manually will result in a RuntimeError.