celery-types icon indicating copy to clipboard operation
celery-types copied to clipboard

TypeError: 'type' object is not subscriptable

Open mdantonio opened this issue 2 years ago • 8 comments

Hello!

I have a problem with the new Task annotations. I installed version 0.13.0 and started receiving typing errors on Task annotations (from https://github.com/sbdchd/celery-types/pull/75)

Missing type parameters for generic type "Task"

So added the expected annotations, like:

from celery.app.task import Task
from typing import Any

@app.task(bind=True)
def my_task(self: Task[Any, Any]):

But now celery (version 5.2.7) fails at runtime with:

    self: Task[Any, Any],
          │    │    └ typing.Any
          │    └ typing.Any
          └ <class 'celery.app.task.Task'>

TypeError: 'type' object is not subscriptable

What I'm missing? :cry:

mdantonio avatar Jun 05 '22 06:06 mdantonio

Oh at runtime, yeah I need to update the docs, you'll need to monkey patch Task to accept a generic argument:

 Task.__class_getitem__ = classmethod(lambda cls, *args, **kwargs: cls)  # type: ignore [attr-defined]

sbdchd avatar Jun 06 '22 01:06 sbdchd

Another option is to do something like:

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    class AnyTask[Any, Any]: ...
else:
    AnyTask = Task

from celery.app.task import Task
from typing import Any

@app.task(bind=True)
def my_task(self: AnyTask): ...

sbdchd avatar Jun 06 '22 01:06 sbdchd

Would it be possible for this library to provide something like django_stubs_ext to do this for us?

craiga avatar Jun 06 '22 11:06 craiga

Yeah that’s possible but then we’d need to have a second package and it’s only a couple lines of code to copy

sbdchd avatar Jun 06 '22 12:06 sbdchd

In most cases you can get away with just sticking from __future__ import annotations at the top of your file. As long as the subscripting is in a "type annotation position" that will stop it from being evaluated at runtime. It won't help if it's in a "runtime position" though, like if you inherit from Task or use it in typing.cast or something.

henribru avatar Jun 08 '22 16:06 henribru

Oh at runtime, yeah I need to update the docs, you'll need to monkey patch Task to accept a generic argument:

 Task.__class_getitem__ = classmethod(lambda cls, *args, **kwargs: cls)  # type: ignore [attr-defined]

Ok, thank you for the clarification :+1:

mdantonio avatar Jun 11 '22 07:06 mdantonio

Is it the same if I want to use from celery.result import AsyncResult? Is it then safe to do the following in order to allow for generics?

 AsyncResult.__class_getitem__ = classmethod(lambda cls, *args, **kwargs: cls)  # type: ignore [attr-defined]

NixBiks avatar Jan 01 '23 21:01 NixBiks

Yeah monkey patching __class_getitem__ should always be safe in Celery as they don't use that method at all

sbdchd avatar Jan 01 '23 22:01 sbdchd