Skip to content

Custom instantiator does not run when using nested dependency injection #606

@vsimkus

Description

@vsimkus

First of all, thanks for the package, been using it for 5 years already as it simplifies my code bases quite a bit.

🐛 Bug report

A custom class instantiator, added with parser.add_instantiator(...), does not run when the class objects are added via nested dependency injection. See the provided code and below explanations. I am using version 4.32.1, but from the change logs it doesn't seem that this has been fixed.

Case 1 (instantiator runs)

The instantiator runs if we do not use nesting. For example see Case 1 in the below, which executes the custom instantiator correctly.

Case 2 (instantiator does not run)

In case 2, the objects of Dependency class are provided via nested dependency injection. In this case, the custom instantiator is not run.

To reproduce

from typing import Iterable, Callable, Type, Any, Dict

import jsonargparse
from jsonargparse._util import ClassType


class Dependency():
    def __init__(self, val: int, val2: float = 0.2):
        self.val = val
        self.val2 = val2

class Runner():
    def __init__(self, dependency: Callable[[Iterable], Dependency]):
        self.dependency = dependency(self)

class Wrapper():
    def __init__(self, runner: Runner):
        self.runner = runner

class CustomDependencyInstantiator():
        """
        A custom instantiator to be used in jsonargparse.

        Allows settings some default values based on the dynamically provided arguments.
        """
        def __call__(self, class_type: Type[ClassType], *args, **scheduler_kwargs) -> ClassType:
            runner = args[0]

            # TODO: Do something with the scheduler_kwargs using the arguments
            # This does not run when using dependency injection
            print('Running instantiator')

            return class_type(1, **scheduler_kwargs)

if __name__ == '__main__':
    #
    # Case 1: Using dependency injection without nesting, runs custom instantiator
    #
    config = {
        "dependency": {
            "val2": 0.3,
        }
    }

    parser = jsonargparse.ArgumentParser()
    parser.add_class_arguments(Runner)
    parser.add_instantiator(CustomDependencyInstantiator(), Dependency, subclasses=True)

    config = parser.parse_object(config)
    config = parser.instantiate_classes(config)

    wrapper = Runner(dependency=config.dependency)

    #
    # Case 2: Using dependency injection with nesting, does not run custom instantiator
    #
    config = {
        "runner": {
            "dependency": {
                "val2": 0.3,
            }
        }
    }

    parser = jsonargparse.ArgumentParser()
    parser.add_class_arguments(Wrapper)
    parser.add_instantiator(CustomDependencyInstantiator(), Dependency, subclasses=True)

    config = parser.parse_object(config)
    config = parser.instantiate_classes(config)

    wrapper = Wrapper(runner=config.runner)

Expected behavior

The custom instantiator runs even when using nested dependency injection.

Environment

  • jsonargparse version: 4.32.1
  • Python version: 3.10
  • How jsonargparse was installed: pip install jsonargparse[signatures]==4.32.1 in a conda environment
  • OS: linux pop-os 6.9.3

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions