Repository navigation
No longer able to register methods in TaskIQ 0.11.16 #436
Copy link
Copy link
Open
Description
After updating to 0.11.16, registering a method as a task produces an AttributeError:
bbot_server/applets/_base.py:234: in register_watchdog_tasks
task = broker.register_task(method, **kwargs)
../../../.cache/pypoetry/virtualenvs/bbot-server-DmHQwTaK-py3.13/lib/python3.13/site-packages/taskiq/abc/broker.py:377: in register_task
return self.task(task_name=task_name, **labels)(func)
../../../.cache/pypoetry/virtualenvs/bbot-server-DmHQwTaK-py3.13/lib/python3.13/site-packages/taskiq/abc/broker.py:331: in inner
self.decorator_class(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = AsyncTaskiqDecoratedTask(bbot_server.applets.events:archive_events_task), broker = <taskiq_nats.broker.NatsBroker object at 0x70a16466cec0>
task_name = 'bbot_server.applets.events:archive_events_task'
original_func = <bound method EventsApplet.archive_events_task of <bbot_server.applets.events.EventsApplet object at 0x70a16609c050>>, labels = {'schedule': [{'cron': '0 0 * * *'}]}
def __init__(
self,
broker: "AsyncBroker",
task_name: str,
original_func: Callable[_FuncParams, _ReturnType],
labels: Dict[str, Any],
) -> None:
self.broker = broker
self.task_name = task_name
self.original_func = original_func
self.labels = labels
# This is a hack to make ProcessPoolExecutor work
# with decorated functions.
#
# The problem is that when we decorate a function
# it becomes a new class. This class has the same
# name as the original function.
#
# When receiver sends original function to another
# process, it will have the same name as the decorated
# class. This will cause an error, because ProcessPoolExecutor
# uses `__name__` and `__qualname__` attributes to
# import functions from other processes and then it verifies
# that the function is the same as the original one.
#
# This hack renames the original function and injects
# it back to the module where it was defined.
# This way ProcessPoolExecutor will be able to import
# the function by it's name and verify its correctness.
new_name = f"{original_func.__name__}__taskiq_original"
> self.original_func.__name__ = new_name
E AttributeError: 'method' object has no attribute '__name__' and no __dict__ for setting new attributes. Did you mean: '__ne__'?
../../../.cache/pypoetry/virtualenvs/bbot-server-DmHQwTaK-py3.13/lib/python3.13/site-packages/taskiq/decor.py:79: AttributeError
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
s3rius commentedon Mar 31, 2025
Can you pelase show how you declare tasks?
[-]No longer able to register methods in TaskIQ 0.11.10[/-][+]No longer able to register methods in TaskIQ 0.11.16[/+]s3rius commentedon Mar 31, 2025
It seems like you mark your class methods as tasks. I tried this and it worked.
I wonder how do you declare tasks?
TheTechromancer commentedon Mar 31, 2025
Sure, here is the relevant code:
Basically, we are iterating over our class, and registering certain methods with the TaskIQ broker.
s3rius commentedon Mar 31, 2025
Can you please show me how you use it? Because it seems like something is purging out
__name__attribute of methods.s3rius commentedon Mar 31, 2025
I tried this snippet and seems like all methods have
__name__by default.Which led me to an idea that something is removing
__name__attribute of a function. Also, what got my attention is message that somemethoddoesn't have__name__. But by default methods are typed as functions.TheTechromancer commentedon Mar 31, 2025
That is really strange. I'm not sure how they're losing their
__name__attribute. Yeah wtf. I'll look into this some more tomorrow.TheTechromancer commentedon Apr 1, 2025
Okay, I tried printing
method.__name__before it's passed intobroker.register_task(), and at least at that point, it's still accessible. It seems to lose it somewhere betweenbroker.register_task()andself.decorator_class().lindycoder commentedon Apr 1, 2025
Seems related, introduced in
0.11.15: I'm not able to add a callable object as task anymoreThis works with 0.11.14
starting 0.11.15, produces the same error
AttributeError: 'MyTask' object has no attribute '__name__'. Did you mean: '__ne__'?s3rius commentedon Apr 1, 2025
I don't think it should have worked in the first place. The tasks were intended to be functions, and currently, classes and objects are not officially supported.
We can add support for them, but it requires some considerations, such as how to handle
self. Should we be able to mark methods as tasks?Also, it's unclear to me how this code works because, on the worker side, the task isn't declared.
s3rius commentedon Apr 1, 2025
@lindycoder, can you please share with me MRE of the codebase that works on previous version and doesn't work since the 0.11.16.
Because the code you sent doesn't work on verison 0.11.15.
Here's what I got.
scott-boost commentedon Apr 1, 2025
@s3rius maybe you misread
he wrote his code snippet should work upto and including version 0.11.14
s3rius commentedon Apr 1, 2025
@scott-boost, yeah, thanks for noticing. My bad. I was running it on
0.11.14. It doesn't work on0.11.15because of__name__and doesn't work on0.11.14because tasks are not available to the worker.lindycoder commentedon Apr 1, 2025
@s3rius This is all part of a bigger framework we have, I just wanted to share that we currently have a use case where we pass in a "callable", even though it's not a function. I didn't expect my example to work at all, just to not raise.
s3rius commentedon Apr 1, 2025
@lindycoder, okay, clear point. Can you describe the framework you have a little bit so I can re-create something similar to test out my solutions?
lindycoder commentedon Apr 1, 2025
Here's high level what happens. That script will print hello on 0.11.14 and fail to just create the task on 0.11.15
Hope this can help
josiah-lunit commentedon Jul 4, 2025
Also experiencing this, it is blocking us from being able to upgrade TaskIQ.
kai-nashi commentedon Aug 13, 2025
@TheTechromancer i have wrote class-based task. It require custom receiver with little change.
Wirker with custom receiver starts with
--receiver. Example:$ worker main:broker --receiver receiver:TaskReceivertask.py
receiver.py
main.py
kai-nashi commentedon Aug 13, 2025
@s3rius, class-based tasks are awesome — but before making a PR, I think we should discuss two problems.
Class-based model for all tasks
What if all tasks were classes, with some backward compatibility to support the
@broker.taskdecorator?Labels handling
If all tasks are classes, and labels are set via a
Meta(orLabels, as it’s currently called), we might want an interface likeself.meta.retry_on_error.That’s fine as long as
retry_on_erroris explicitly configured.However, labels are just a dictionary — keys might not be valid Python identifiers, and any middleware can set arbitrary labels.
It would be great to have a mechanism for middleware to read class
Metaand update instanceMetabefore the target method is called.I think this could be achieved with a small update to side libraries, for example:
If the meta is not updated by middleware due method
set_task_metanot exists, that middleware labels simply wouldn’t be available via the.metainterface. (check method exists before callset_task_meta)casper-71 commentedon Nov 6, 2025
I have the same problem with register_task() method.
Packages:
Example:
Error:
But in taskiq 0.11.14 everything works fine.
astoff commentedon Jan 14, 2026
@s3rius:
For the record, (bound) methods don't have a
__name__attribute. When accessed, what you get is the__name__of the underlying function, but you can't set it; and setting the name of the underlying function is not a great idea. Ideally one would get rid of the "hack" explained in the comment inside the OP.Regarding your other comment starting with:
It doesn't cause the reported error because you're decorating good old functions that happen to be declared inside a class, not bound methods. However, I doubt that the said hack would do whatever it needs to do in order to the process pool executor to work.
If you try to adapt the code in your comment so that
brokeris not a global variable but rather an argument to createTaskClass(e.g. usingbroker.register_taskinTaskClass.__init__), then you will arrive at the problem.casper-71 commentedon Jan 22, 2026
@s3rius Hi!
Do you have any updates? The same issue is occurring in version 0.12.1.