Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

celery.execute.send_task should respect CELERY_ALWAYS_EAGER #581

Closed
kmike opened this issue Dec 30, 2011 · 14 comments
Closed

celery.execute.send_task should respect CELERY_ALWAYS_EAGER #581

kmike opened this issue Dec 30, 2011 · 14 comments

Comments

@kmike
Copy link
Contributor

kmike commented Dec 30, 2011

Trying to figure out why CELERY_ALWAYS_EAGER doesn't work I found that celery.app.base.BaseApp.send_task (and thus celery.execute.send_task) doesn't respect CELERY_ALWAYS_EAGER.

I think CELERY_ALWAYS_EAGER should be respected both for direct task invocations (via task.delay) and for indirect invocations (via execute.send_task).

@mher
Copy link
Contributor

mher commented Dec 30, 2011

send_task can be used when there is no access to task class. In that case it is not possible to execute the task locally when CELERY_ALWAYS_EAGER is enabled.

@ask
Copy link
Contributor

ask commented Jan 13, 2012

Yeah, that would be weird, as the task may not be registered. It should be fairly easy to add this behavior to your own code though.

@ask ask closed this as completed Jan 13, 2012
@kmike
Copy link
Contributor Author

kmike commented Jan 13, 2012

This make sense. Maybe docs are worth changing then? This behavior is not clear from the docs:

http://celery.readthedocs.org/en/latest/configuration.html#celery-always-eager

CELERY_ALWAYS_EAGER
If this is True, all tasks will be executed locally by blocking until the task returns.

http://celery.readthedocs.org/en/latest/userguide/executing.html#basics

You can also execute a task by name using send_task(), if you don’t have access to the task class:

@litchfield
Copy link

This has bitten me too- any reason we can't pull the task class itself from the registry and run it so we get some consistent behaviour for CELERY_ALWAYS_EAGER?

Otherwise we should call it CELERY_MOSTLY_EAGER ;-)

@ask
Copy link
Contributor

ask commented Feb 2, 2012

Closed by 703cba6

unittests should mock the call to send_task, here is an example using the mock library:

from mock import Mock

def test_task_sent(self):
    from celery import current_app as celery 
    prev = celery.send_task
    celery.send_task = Mock()
    try:
          some_code_sending_task()
          self.assertTrue(celery.send_task.called)
          # or:
          ## celery.send_task.assert_called_with(*expected_args, **expected_kwargs)
    finally:
        celery.send_task = sent

@litchfield
Copy link

great! the issue isn't resolved though, so why are we closing it? it either needs to be fixed, or the inconsistency documented.

@Ignas
Copy link
Contributor

Ignas commented Feb 3, 2012

I think 703cba6 is the way it got documented.

@ask
Copy link
Contributor

ask commented Feb 3, 2012

There is a semantic difference between task.apply_async and send_task; i.e It is immediately obvious what eager task.apply_async would do, but not for send_task. Calling tasks that happen to be registered then would be like defining the value of a division by zero. If you want this behavior you could easily write your own send_task that does exactly that.

I don't mind this being documented with the CELERY_ALWAYS_EAGER setting, would you like to write a doc-patch?

@davidmarble
Copy link

When testing django apps, I do some monkey patching to get the same result as CELERY_ALWAYS_EAGER = True:

In settings.py:

if 'test' in sys.argv:
    # Monkey patch send_task to use a sync version
    from celery import execute
    from tests.celery import send_task as send_task_test
    execute.send_task = send_task_test

Then in tests/celery.py I have (could use some feedback on this):

def send_task(name, args=None, kwargs=None, countdown=None,
            eta=None, task_id=None, publisher=None, connection=None,
            result_cls=None, expires=None, queues=None, **options):
    module = name[:name.rfind('.')]
    func_name = name[name.rfind('.')+1:]
    exec 'from %s import %s as func' % (module, func_name)
    if args and kwargs:
        result = func(*args, **kwargs)
    elif args:
        result = func(*args)
    elif kwargs:
        result = func(**kwargs)
    return result

@ask
Copy link
Contributor

ask commented May 14, 2012

@davidmarble

Thanks for posting your solution, you could shorten it a bit:

from celery import current_app

def send_task(name, args=(), kwargs={}, **opts):
    task = current_app.tasks[name]
    return task.apply(args, kwargs, **opts)

if 'test' in sys.argv:
    current_app.send_task = send_task

This will return a EagerResult though, like with ALWAYS_EAGER,
if you want the result directly you can use return task(*args, **kwargs)

@BeOleg
Copy link

BeOleg commented Apr 29, 2017

Proper solution?

@skinderis
Copy link

Would be great to have proper solution. Is there any?

@BeOleg
Copy link

BeOleg commented Oct 11, 2019

@ask ?

@auvipy
Copy link
Member

auvipy commented Oct 22, 2019

ask do not maintain celery anymore.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants