sbdchd / celery-types

:seedling: Type stubs for Celery and its related packages
Apache License 2.0
85 stars 39 forks source link

TypeError: 'type' object is not subscriptable #80

Open mdantonio opened 2 years ago

mdantonio commented 2 years ago

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:

sbdchd commented 2 years ago

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 commented 2 years ago

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): ...
craiga commented 2 years ago

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

sbdchd commented 2 years ago

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

henribru commented 2 years ago

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.

mdantonio commented 2 years ago

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:

NixBiks commented 1 year ago

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]
sbdchd commented 1 year ago

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