python-dependency-injector: Configuration raises AttributeError when provider is called

Hi, I just run into this issue with the Configuration provider. After scratching my head for a bit, I managed to find a workaround, but I was wondering if this is actually a bug or just something wrong I am doing. Any help would be appreciated!

Steps to reproduce

containers.py

from dependency_injector import providers, containers

class MyService(object):
    def __init__(self, **kwargs):
        self.key = kwargs.pop('key')

    def trigger(self):
        pass

class MyDevice(object):
    def __init__(self, **kwargs):
        # doesn't raise an error because it's an instance of
        # dependency_injector.providers.Singleton
        self.service = kwargs.pop('service')

    def do_something(self):
        # raises "AttributeError: 'NoneType' object has no attribute 'get'"
        self.service().trigger()

class ServiceContainer(containers.DeclarativeContainer):
    config = providers.Configuration()

    myservice = providers.Singleton(MyService, config=config.myservice)


class Container(containers.DeclarativeContainer):
    config = providers.Configuration()

    services = providers.Container(ServiceContainer, config=config.services)

    mydevice = providers.Factory(MyDevice)

If I run app.py

import sys

from containers import Container

container = Container()
container.config.from_yaml('config.yaml')
container.init_resources()
container.wire(modules=[sys.modules[__name__]])

mydevice = container.mydevice(service=container.services.myservice)
mydevice.do_something()

with config.yaml

foo:
  bar: 42

it raises the following error

File “/home/stefano/personal/test-error/containers.py”, line 15, in do_something self.service().trigger() File “src/dependency_injector/providers.pyx”, line 168, in dependency_injector.providers.Provider.call File “src/dependency_injector/providers.pyx”, line 2245, in dependency_injector.providers.Singleton._provide File “src/dependency_injector/providers.pxd”, line 550, in dependency_injector.providers.__factory_call File “src/dependency_injector/providers.pxd”, line 536, in dependency_injector.providers.__callable_call File “src/dependency_injector/providers.pxd”, line 495, in dependency_injector.providers.__call File “src/dependency_injector/providers.pxd”, line 387, in dependency_injector.providers.__provide_keyword_args File “src/dependency_injector/providers.pxd”, line 310, in dependency_injector.providers.__get_value File “src/dependency_injector/providers.pyx”, line 168, in dependency_injector.providers.Provider.call File “src/dependency_injector/providers.pyx”, line 1232, in dependency_injector.providers.ConfigurationOption._provide File “src/dependency_injector/providers.pyx”, line 1467, in dependency_injector.providers.Configuration.get AttributeError: ‘NoneType’ object has no attribute ‘get’

Workaround To avoid the issue, I have to pass the whole config to ServiceContainer

class ServiceContainer(containers.DeclarativeContainer):
    config = providers.Configuration()

    myservice = providers.Singleton(MyService, config=config.services.myservice)


class Container(containers.DeclarativeContainer):
    config = providers.Configuration()

    services = providers.Container(ServiceContainer, config=config)

    mydevice = providers.Factory(MyDevice)

Running the application now, raises the following (as expected)

File “/home/stefano/personal/test-error/containers.py”, line 18, in do_something self.service().trigger() File “src/dependency_injector/providers.pyx”, line 168, in dependency_injector.providers.Provider.call File “src/dependency_injector/providers.pyx”, line 2245, in dependency_injector.providers.Singleton._provide File “src/dependency_injector/providers.pxd”, line 550, in dependency_injector.providers.__factory_call File “src/dependency_injector/providers.pxd”, line 536, in dependency_injector.providers.__callable_call File “src/dependency_injector/providers.pxd”, line 526, in dependency_injector.providers.__call File “/home/stefano/personal/test-error/containers.py”, line 5, in init self.key = kwargs.pop(‘key’) KeyError: ‘key’

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 21 (14 by maintainers)

Commits related to this issue

Most upvoted comments

@Minitour ,

Here is a workaround for your problem:

def handle(event, context):
    container = Container()
    config = container.ingestion_config()  # <------ This line crashes in AWS Lambda
    ...

The root cause of that issue is kind of tricky. I’ll check later if there is a good way to fix it.

@StefanoFrazzetto

I’ve created an issue for the required option: #369

By the way, I still believe an exception should be raised when using a strict config. Maybe that’s something you could consider with 5.x.x?

I think I can add this in version 4 since strict is a new feature. I’ll use your code sample from https://github.com/ets-labs/python-dependency-injector/issues/358#issuecomment-761611148. Here is an issue for this: #370

In version 5, strict=True should become a default.

Got it, will take a look later today.

I’ve also found another bug where the config provider doesn’t raise an error if you try to load a non-existing file, e.g. container.config.from_yaml(‘i_dont_exist.yaml’). I can open a separate issue for that one, if you prefer.

This is not a bug, but a feature. It’s done that way to be able to put in a code multiple locations like:

container.config.from_yaml('config.yaml')
container.config.from_yaml('config.local.yaml')

In that case settings from config.local.yaml will override the settings from config.yaml. If config.local.yaml does not exist, no error is expected.

Hey @StefanoFrazzetto , got it, thanks for extra info. I see a bug now, fixing.