0

I have some base class like:

class Auto(object):
    _year = None
    _base_price = None

    @classmethod
    def calculate_price(cls):
        return cls._base_price + 5000 * (2016 - cls._year)

class BMW(Auto):
    _year = 2015
    _base_price = 40000

    @celery.task(filter=task_method,
             name='queue_name',
             base=basetask(), bind=True)
    def calculate_price(self):
        super(BMW, self).calculate_price()

So, my problem is with the last line of code, it raises: TypeError: super(type, obj): obj must be an instance or subtype of type

I was trying to remove bind=True and play a little with it, but with no results. Any ideas how to fix this issue?

UPDATE: where

celery = Celery(...)

so, I'm using decorator like app.task

smart
  • 1,975
  • 5
  • 26
  • 46
  • Typically one sees the task decorator used with functions, not methods. I'm not saying it can't be done, but it is rare. There are many resources discussing how to put your task behaviors in a class (eg https://blog.balthazar-rouberol.com/celery-best-practices), but the task decorator is still applied against a plain function. Perhaps you could simplify your design in that way. Also relevant: http://stackoverflow.com/questions/9250317/using-class-methods-as-celery-tasks – FMc Dec 31 '16 at 23:20
  • @FMc, I've read both of these topics. There is no such cases as mine there. I really need basic class and running some methods in child class as a task celery. So I need to resolve my issue somehow, without architecture changes – smart Jan 01 '17 at 19:24
  • Yes, there are no cases like your in those topics -- or in any of the Celery documentation -- because I don't think Celery supports the use of the task decorator against a method. You need to use a function. In particular, in the StackOverflow link I provided, note the comment from Hamy. It looks like your current design simply won't work. – FMc Jan 01 '17 at 19:35
  • @FMc, I've updated a little my question... I'm using my decorator as `app.task` as documentation for celery 4.X proposed. That's why I may be sure that it supports for now – smart Jan 02 '17 at 10:59

1 Answers1

0

You are mixing two styles of methods: Class methods (@classmethod), and instance methods (def calculate_price(self):).

I haven't really tried this in code, but what about:

class Auto(object):
    _year = None
    _base_price = None

    @classmethod
    def calculate_price(cls):
        return cls._base_price + 5000 * (2016 - cls._year)

class BMW(Auto):
    _year = 2015
    _base_price = 40000

    @celery.task(filter=task_method, name='queue_name', base=basetask(), bind=True)
    @classmethod
    def calculate_price(cls, task_self):
        super(BMW, cls).calculate_price()

So the @classmethod decorator is applied first to def calculate_price(...):, and then the @celery.task is applied to the class method.

If you do need calculate_price() to be an instance method, then the signature could be def calculate_price(self, task_self): but you need to apply the decorator when you already have an instance, like:

my_car = BMW()
celery.task(my_car.calculate_price)

When you access a method using instance<dot>method you don't get the function you wrote, you get a method descriptor that when called, it will take care of fill out the first argument (self) leaving to you filling the remaining arguments. In this case, is only the task_self argument, and the @celery.task decorator will take care of that one.

In any case, I think you should step back, and rethink your problem in a more generic way instead of figuring out how to tie class/instance methods to Celery. In general, Celery tasks should be just functions living in a module inside your application, which accept parameters that can be easily serialized using JSON.

Armando Pérez Marqués
  • 5,661
  • 4
  • 28
  • 45