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

  1. Implement get_delayed_tuple() in your subclass, returning the tuple produced by joblib.delayed(func)(*args, **kwargs).
  2. Prefer using joblib.delayed directly and qlib.utils.paral.run_loop_delayed, which accepts raw delayed objects without subclassing.
  3. 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

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


AI-assisted analysis of microsoft/qlib@79633dd950 (2026-08-15). Data as JSON: /api/errors/086629c32e97750c. Report an issue: GitHub.