@@ -55,17 +55,39 @@ def __init__(
5555 self .broker = broker
5656 self .task_name = task_name
5757 self .original_func = original_func
58+ self .labels = labels
59+
60+ # This is a hack to make ProcessPoolExecutor work
61+ # with decorated functions.
62+ #
63+ # The problem is that when we decorate a function
64+ # it becomes a new class. This class has the same
65+ # name as the original function.
66+ #
67+ # When receiver sends original function to another
68+ # process, it will have the same name as the decorated
69+ # class. This will cause an error, because ProcessPoolExecutor
70+ # uses `__name__` and `__qualname__` attributes to
71+ # import functions from other processes and then it verifies
72+ # that the function is the same as the original one.
73+ #
74+ # This hack renames the original function and injects
75+ # it back to the module where it was defined.
76+ # This way ProcessPoolExecutor will be able to import
77+ # the function by it's name and verify its correctness.
5878 new_name = f"{ original_func .__name__ } __taskiq_original"
5979 self .original_func .__name__ = new_name
60- self .original_func .__qualname__ = new_name
80+ if hasattr (self .original_func , "__qualname__" ):
81+ original_qualname = self .original_func .__qualname__ .rsplit ("." )
82+ original_qualname [- 1 ] = new_name
83+ new_qualname = "." .join (original_qualname )
84+ self .original_func .__qualname__ = new_qualname
6185 setattr (
6286 sys .modules [original_func .__module__ ],
6387 new_name ,
6488 original_func ,
6589 )
6690
67- self .labels = labels
68-
6991 # Docs for this method are omitted in order to help
7092 # your IDE resolve correct docs for it.
7193 def __call__ ( # noqa: D102
0 commit comments