microsoft/qlib · error · NotImplementedError
NotImplemented
Error message
NotImplemented
What it means
DelayedTask is a mixin in qlib.utils.paral for embedding joblib-delayed tasks inside nested data structures (dicts/lists) so Parallel can execute and replace them with results. The contract requires each subclass to return its (func, args, kwargs) delayed tuple from get_delayed_tuple(); the base class intentionally raises NotImplementedError. Hitting it means the base DelayedTask was used directly or a subclass omitted the method.
Source
Thrown at qlib/utils/paral.py:146
return decorator_func
# # Outlines: Joblib enhancement
# The code are for implementing following workflow
# - Construct complex data structure nested with delayed joblib tasks
# - For example, {"job": [<delayed_joblib_task>, {"1": <delayed_joblib_task>}]}
# - executing all the tasks and replace all the <delayed_joblib_task> with its return value
# This will make it easier to convert some existing code to a parallel one
class DelayedTask:
def get_delayed_tuple(self):
"""get_delayed_tuple.
Return the delayed_tuple created by joblib.delayed
"""
raise NotImplementedError("NotImplemented")
def set_res(self, res):
"""set_res.
Parameters
----------
res :
the executed result of the delayed tuple
"""
self.res = res
def get_replacement(self):
"""return the object to replace the delayed task"""
raise NotImplementedError("NotImplemented")
class DelayedTuple(DelayedTask):
def __init__(self, delayed_tpl):View on GitHub (pinned to 79633dd950)
Solutions
- Implement get_delayed_tuple() in your subclass, returning the tuple produced by joblib.delayed(func)(*args, **kwargs).
- Prefer using joblib.delayed directly and qlib.utils.paral.run_loop_delayed, which accepts raw delayed objects without subclassing.
- Never instantiate the DelayedTask base class itself; treat it as an interface.
Example fix
// before
class MyTask(DelayedTask):
pass # get_delayed_tuple inherited -> NotImplementedError
// after
from joblib import delayed
class MyTask(DelayedTask):
def get_delayed_tuple(self):
return delayed(self.func)(*self.args, **self.kwargs) Defensive patterns
Strategy: type-guard
Validate before calling
from qlib.utils.paral import DelayedTask assert type(task).get_delayed_tuple is not DelayedTask.get_delayed_tuple, 'implement get_delayed_tuple'
Type guard
def is_runnable_delayed(task) -> bool:
return type(task).get_delayed_tuple is not DelayedTask.get_delayed_tuple Prevention
- Use joblib.delayed directly with run_loop_delayed instead of subclassing DelayedTask when possible.
- If subclassing, implement get_delayed_tuple first and unit-test it returns a (func, args, kwargs) delayed tuple.
When it happens
Trigger: Instantiating DelayedTask() itself, or a custom subclass without get_delayed_tuple, and passing it into qlib's run_loop_delayed / parallel execution utilities.
Common situations: Writing custom delayed-execution wrappers around qlib.utils.paral.run_loop_delayed; upgrading qlib where DelayedTask subclasses changed shape; copy-pasting an example that only shows set_res/get_replacement.
Related errors
- please implement _align_indices func
- This type of input is not supported
- Please implement `save_obj`
- Please implement the `save_objs` method
- Please implement the `load_obj` method
AI-assisted analysis of microsoft/qlib@79633dd950 (2026-08-15).
Data as JSON: /api/errors/086629c32e97750c.
Report an issue: GitHub.