python-dependency-injector
python-dependency-injector copied to clipboard
Attempting to inject, getting: "AttributeError: 'Provide' object has no attribute X"
Trying to understand how to correctly inject dependencies into a class constructor, the following is a sandbox example where I've extracted the key parts from a broader project.
from dependency_injector import containers, providers
from dependency_injector.wiring import Provide, inject
class Config():
def __init__(self, config_file: str = None):
self.config_file = config_file
class Container(containers.DeclarativeContainer):
config = providers.Singleton(Config)
class Engine():
@inject
def __init__(
self,
config: Config = Provide[Container.config],
):
self.config = config
def init(self) -> None:
print(f'Reading from config: {self.config.config_file}')
def container_factory(config_file: str = None) -> Container:
container = containers.DynamicContainer()
config_provider = providers.Factory(Config, config_file=config_file) \
if config_file else providers.Factory(Config)
container.config = config_provider
return container
def main():
container = container_factory('/tmp/config.yml')
container.wire(modules=[__name__])
engine = Engine()
engine.init()
if __name__ == '__main__':
main()
I'm getting an error here from the init log:
AttributeError: 'Provide' object has no attribute 'config_file'
My expected behavior is that config is an instance of the Config class that has been passed the config_file value in its constructor. Instead I seem to be given an instance of the Provide class?
I have a few questions about this:
- The documentation is very vague about what wire actually does, and what it expects for its modules/packages arguments. What should these values be? The module and/or package that needs to have dependencies injected?
- I'm assuming the problem here is that the wire has failed so it's unable to inject correctly?
- I'm having a hard time finding examples that demonstrate where the
@injectdecorator needs to go for a class -- is it on the class declaration or on the actual__init__def, if I wish to inject into the constructor?
If it's relevant, I use virtualenv .venv to create a virtual env at the root of the project and am using poetry install and poetry shell to load the environment and run it.
As I'm looking back at this it's clear I'm actually not providing the dynamic container but instead referencing the unused Container class that's the declarative container in the marker. Leaving the answer for posterity.
Leads me to the next question: what is the right way to inject my dynamic container? I'm using the dynamic container because I need the Config instance to be initialized dynamically at runtime depending on whether or not config_file has been set at runtime, so I don't know at authorship time how to construct it. Is a dynamic container the right flavor to use for this use-case?