5

I am running a Django website and have just gotten Celery to run, but I am getting confusing errors. Here is how the code is structured.

In tests.py:

from tasks import *
from celery.result import AsyncResult

project = Project.objects.create()
# initalize various sub-objects of the project

c = function.delay(project.id)
r = AsyncResult(c.id).ready()
f = AsyncResult(c.id).failed()
# wait until the task is done  
while not r and not f:
    r = AsyncResult(c.id).ready()
    f = AsyncResult(c.id).failed()

self.assertEqual() #will fail because task fails

in tasks.py:

from __future__ import absolute_import
from celery import shared_task

@shared_task
def function(project_id)
    #a bunch of calculations followed by a save of the project
    project = Project.objects.get(project=project_id)

    for part in project.part_set.all():
        partFunction(part.id)
        p = Part.objects.get(id=part.id)
        # add to various variables in project from variables in p
    project.save()

in mainapp/settings.py:

BROKER_URL = "amqp://ipaddress"
CELERY_RESULT_BACKEND='amqp'
CELERY_ACCEPT_CONTENT = ['json','pickle','msgpack','yaml']
CELERY_IGNORE_RESULT = False

the celery debug console log for must by list/tuple:

[INFO/MainProcess] Received task: myapp.tasks.function[id]
[ERROR/MainProcess] Task myapp.tasks.function[id]
    raised unexpected: ValueError('task args must be a list or tuple',)
Traceback:
   File "/python2.7/site-packages/celery/app/trace.py", line 240, in trace_task
       R = retval = fun(*args, **kwargs)
   File "/python2.7/site-packages/celery/app/trace.py", line 437, in __protected_call__
       return self.run(*args, **kwargs)
   File "/myapp/tasks.py", line 28, in function
       p = Part.objects.get(id=part.id)
   File "/python2.7/site-packages/celery/app/task.py", line 555, in apply_async
       **dict(self._get_exec_options(), **options)
   File "/python2.7/site-packages/celery/app/base.py", line 351, in send_task
       reply_to=reply_to or self.oid, **options
   File "celery/app/amqp.py", line 252, in publish_task
       raise ValueError('task args must be a list or tuple')
ValueError: task args must be a list or tuple

the error I am getting is as above, AsyncResult(c.id).result: task args must be a list or tuple. This should be an easy solution but it is not. When I make it a list like so:

inline = [project.id]
c = function.delay(inline)

It then changes it mind and tells me that AsyncResult(c.id).result: int() argument must be a string or a number, not 'list'

As you can imagine I am very confused as to what might be the problem.


Edit

tasks.py

@shared_task
def function(app):
    @app.task(name='myapp.function', bind=True)
    def function(project_id):

tests.py

c = function.s(project.id).delay()

function.app prints

DoctorWizard
  • 303
  • 1
  • 3
  • 11
  • can you share some more of the code? – Rafael Barros Jun 17 '14 at 19:35
  • @RafaelBarros I can't share to much more of the task code but that shouldn't matter as I get the same results with the process doing nothing. I can add some settings and some more parts of the test function if that might help. – DoctorWizard Jun 17 '14 at 19:51
  • if you can send at least a few lines of "function", and maybe the full stack trace, that would help. – Rafael Barros Jun 17 '14 at 19:54
  • @RafaelBarros if you are confused about what any of the paths are let me know because I've made them condense for space and legibility sake. – DoctorWizard Jun 17 '14 at 20:10
  • can you try doing function.delay(*inline)? – Rafael Barros Jun 17 '14 at 20:13
  • @RafaelBarros unfortunately that gave me the same error as listed above. – DoctorWizard Jun 17 '14 at 20:24
  • Yeah, this will be impossible to debug without the code inside the function. – Rafael Barros Jun 17 '14 at 20:26
  • @RafaelBarros I can put in some code but I don't think the problem has to do with the code as it is the same error when the function is not doing anything. – DoctorWizard Jun 17 '14 at 20:38
  • but if you're calling the task like specified here: http://celery.readthedocs.org/en/latest/getting-started/first-steps-with-celery.html#calling-the-task the problem is not celery. – Rafael Barros Jun 17 '14 at 20:57
  • since you're using a shared_task, you might need to specify which app that task is bound to. what is within function.app? – Rafael Barros Jun 17 '14 at 21:03
  • @RafaelBarros I'm not sure what you mean about function.app, I've put in a little bit of code in function though and should I not be using shared_task and how do I make sure the task is bound to task in myapp? – DoctorWizard Jun 17 '14 at 21:15
  • the information you put there is enough for me to have a few ideas on what is going on. by printing function.app you might show which celery app is tied to that function. You can see the correct implementation of shared tasks here: https://github.com/celery/celery/issues/1937 – Rafael Barros Jun 17 '14 at 21:52
  • @RafaelBarros Sorry for the delayed response. I have changed to the way that is directed in your link as you can see above. The problem I am now having is and attribute error app.task where 'int' object has no attribute 'task'. – DoctorWizard Jun 18 '14 at 18:00
  • I believe the exception is raised when serializing the result of the task to send back to your test (exception text might be misleading, I encourage you to read the code instead). Perhaps you should simply have your task function return a result? – Robert Jørgensgaard Engdahl Jun 18 '14 at 18:59

1 Answers1

8

You are getting an error in your code inside the task, it shows in traceback:

File "/myapp/tasks.py", line 28, in function
   p = Part.objects.get(id=part.id)

Your code seems right, but from the traceback it looks like celery has an old version of task pickled. Very important that you restart celery whenever you change anything inside task.py (maybe even if you change other files, but I don't think so). It could be a cause of your problem, it bit me in a butt couple of times.

Also there is no reason to pull the part from database individually p = Part.objects.get(id=part.id) since you already getting current part instance when you are iterating over a queryset in for part in project.part_set.all():. This one is just a suggestion, you might have more code that needs that step.

As a side note, if it is a task in your project and not a part of some reusable app, just use a regular @task decorator, celery will find it, but make sure you configure Celery app right, here is another one of my posts that can guide you through: Celery / Django Single Tasks being run multiple times

So as long as you have everything configured correctly, just use it as you did before:

@task #or @shared_task
def function(project_id)
    #a bunch of calculations followed by a save of the project
    project = Project.objects.get(project=project_id)
    ....

Then call it:

result = function.delay(project.id)

or:

result = function.apply_async(args=(project.id,))

Obviously I'd also recommend testing the task directly by calling it without celery function(project.id), but I'm sure you knew that.

Community
  • 1
  • 1
lehins
  • 9,642
  • 2
  • 35
  • 49
  • Thanks, the problem was not the code I still had as that was right but celery thought I was using an older version of tasks.py – DoctorWizard Jun 20 '14 at 15:33