New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Configuration raises AttributeError when provider is called #358
Comments
Hey @StefanoFrazzetto , what version of Dependency Injector do you use? There were a couple fixes recently. I tried that code sample on latest version |
Btw, containers in initial example look the same way like in workaround. I have tried next example and it also seems to be working correctly: class ServiceContainer(containers.DeclarativeContainer):
config = providers.Configuration()
myservice = providers.Singleton(MyService, key=config.bar)
class Container(containers.DeclarativeContainer):
config = providers.Configuration()
services = providers.Container(ServiceContainer, config=config.foo)
mydevice = providers.Factory(MyDevice) |
Hey @rmk135, thanks for the quick reply! Sorry, I made a mistake when I copy-pasted the first part (I pasted the workaround instead of the issue); I've corrected the initial comment with the issue. I found the error because I was expecting to load something like services:
myservice:
key: baz but my config file did not contain the 'services' key, e.g. something:
foo: baz By the way, I'm using version 4.8.2. |
Hey @StefanoFrazzetto , got it, thanks for extra info. I see a bug now, fixing. |
Fixed in |
Hey @rmk135, thank you very much for the effort with this! I've updated to v4.9.0, but unfortunately I'm still experincing the issue. I created a gist with the sample code showing two examples: working and broken config. 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. |
Got it, will take a look later today.
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 |
I have applied one more fix and now sample code provides: ### WORKING
> Device: triggering service
> Service: success
### BUG
> Device: triggering service
Traceback (most recent call last):
File "/Users/rmoh/projects/ets-labs/python-dependency-injector/.workspace/issue358/bug.py", line 64, in <module>
execute(broken)
File "/Users/rmoh/projects/ets-labs/python-dependency-injector/.workspace/issue358/bug.py", line 52, in execute
mydevice.do_something()
File "/Users/rmoh/projects/ets-labs/python-dependency-injector/.workspace/issue358/bug.py", line 23, 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 2250, 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 "/Users/rmoh/projects/ets-labs/python-dependency-injector/.workspace/issue358/bug.py", line 8, in __init__
self.key = config.pop('key')
AttributeError: 'NoneType' object has no attribute 'pop' While that fixes the internal bug, it does not lead to the fail-fast behaviour. That happens because undefined config value provides none: PS: I published a fix that leads to |
@StefanoFrazzetto I have released version 4.10.0 with strict mode and required modifier features for the Configuration provider. Here is a doc: https://python-dependency-injector.ets-labs.org/providers/configuration.html#strict-mode-and-required-options |
Hi @rmk135 - that's great, thank you! Just to be sure, could you tell me if the behaviour for the following is the expected one? I updated the code to use class ServiceContainer(containers.DeclarativeContainer):
config = providers.Configuration(strict=True)
myservice = providers.Singleton(MyService, config=config.myservice.required())
class Container(containers.DeclarativeContainer):
config = providers.Configuration()
services = providers.Container(ServiceContainer, config=config.services)
mydevice = providers.Factory(MyDevice) but I still get the attribute error in
Per documentation, I would expect the error to be raised earlier
Of course, it works fine if I do the following instead 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.required())
mydevice = providers.Factory(MyDevice)
For convenience, I updated the gist. |
Re: I would usually expect to have an exception raised when trying to do something with non-existent resources. If you believe it's better to keep that behaviour, then it might be useful to have a try:
with open(filepath) as opened_file:
config = yaml.load(opened_file, yaml.Loader)
except OSError as e:
if self.__strict and e.errno in (errno.ENOENT, errno.EISDIR):
e.strerror = f"Unable to load yaml file ({e.strerror})"
raise I understand that, to be consistent, this would require making changes in the other methods as well, e.g. PS: The above was inspired by Flask. |
Update dependency-injector to v4.10.0 ets-labs/python-dependency-injector#358
Hey @StefanoFrazzetto ,
It was a bug. Fixed in Also you can give a specific name for the config provider in class ServiceContainer(containers.DeclarativeContainer):
config = providers.Configuration('config.services', strict=True)
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) produces:
PS: I will check if I could make config provider name definition |
I agree. This is a design error. It didn't look like that when I designed it initially. Sort of hindsight bias :) I can not change To fix the issue I could introduce a new API. Something like:
What do you think? |
Hi, I have been experiencing problems with the Configuration Provider as well. The weird thing is that it runs perfectly fine locally on my machine, but when deployed to AWS Lambda, it seems to fail.
I am initializing the configuration provider using Locally I was running on Python 3.6, using Anaconda on Windows OS. |
Hi @Minitour , can you provide a code sample? |
@rmk135 Sure class Container(containers.DeclarativeContainer):
config = providers.Configuration()
config.from_dict({
'region': Const.REGION, # <--- This is a string
'ssm': Const.PARAMETERS # <--- This is a dict
})
ssm_client = providers.Singleton(boto3.client, 'ssm', region_name=config.region)
s3_client = providers.Singleton(boto3.resource, 's3')
parameter_kit = providers.Singleton(ParameterKit, ssm=ssm_client, parameters_config=config.ssm)
database = providers.Singleton(DynamoDatabase)
ingestion_config = providers.Singleton(IngestionConfig,
s3_client=s3_client,
parameter_kit=parameter_kit,
db=database) In lambda handler: def handle(event, context):
config = Container().ingestion_config() # <------ This line crashes in AWS Lambda
... |
@rmk135 As the proverb says, "hindsight is 20/20" Using a required flag seems a great option, though I think this should be passed with kwargs, as this would allow passing things like a custom yaml loader (I will open a new issue for a potential problem I spotted). By the way, I still believe an exception should be raised when using a strict config. PS: Sorry I can't actively help with this (e.g. by opening a PR), so thank you again for your work! |
Thanks @Minitour. I'll try to run a couple of experiments with it. |
I've created an issue for the
I think I can add this in version 4 since In version 5, |
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. |
Fixed in def handle(event, context):
config = Container().ingestion_config() # <------ This line does not crash in AWS Lambda anymore
... Thanks for reporting the problem. |
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
If I run
app.py
with
config.yaml
it raises the following error
Workaround
To avoid the issue, I have to pass the whole
config
toServiceContainer
Running the application now, raises the following (as expected)
The text was updated successfully, but these errors were encountered: