Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Decorator after @task decorator in celery

I'm trying to apply a decorator after the celery @task decorator, something like.

@send_email
@task
def any_function():

   print "inside the function"

I can get it to work in the way it is recommended in the docs, i.e. to put the decorator before the task decorator, but in this case I would like to access the task instance in my decorator.

The @send_email would have to be a class decorator, this is what I tried without success:

class send_email(object):
    ''' wraps a Task celery class '''
    def __init__(self, obj):

        self.wrapped_obj = obj

        functools.update_wrapper(self, obj)

    def __call__(self, *args, **kwargs):

        print "call"

        return self.wrapped_obj.__call__(*args, **kwargs)

    def run(self, *args, **kwargs):

        print "run"

        return self.wrapped_obj.__call__(*args, **kwargs)

    def __getattr__(self, attr):

        if attr in self.__dict__:

            return getattr(self, attr)

        return getattr(self.wrapped_obj, attr)

I can never get the print statement in the call or run function function to appear in either the worker or the caller.

How can we decorate a celery Task, without resorting to class-based task definitions (so the decorator would be above @task above a function definition.

Thanks for any help!

Miguel

like image 764
mpaf Avatar asked Oct 16 '13 07:10

mpaf


2 Answers

The task decorator does not return a class, it returns an instance.

It seems that your question should really be "How can I have access to the task inside the decorator" rather than how you can apply the decorator first.

In the upcoming 3.1 (development version) you can use bound tasks to accomplish this:

def send_email(fun):
    @wraps(fun)
    def outer(self, *args, **kwargs):
        print('decorated and task is {0!r}'.format(self))
        return fun(self, *args, **kwargs)

    return outer

@task(bind=True)
@send_email
def any_function(self):
    print('inside the function')

For previous versions you can use current_task:

from celery import current_task

def send_email(fun):

    @wraps(fun)
    def outer(*args, **kwargs):
        print('decorated and task is: %r' % (current_task, ))
        return fun(*args, **kwargs)

    return outer

@task
@send_email
def any_function():
    print('inside the function')
like image 147
asksol Avatar answered Nov 09 '22 17:11

asksol


"before" looks like "after" visually.

Eg, this:

@decorator1
@decorator2
@decorator3
def func():
  pass

is equivalent to:

def func():
  pass

func = decorator1(decorator2(decorator3(func)))

This means you have to write @send_email after @task to get it applied before @task. Eg:

@task
@send_email
def any_function():
    print "inside the function"
like image 5
ionelmc Avatar answered Nov 09 '22 18:11

ionelmc