How to react to configuration changes and selectively reinitialize providers?
Closed this issue · 4 comments
I'm working on a robotics application using Dependency Injector, and I want to support live configuration changes (e.g. coming from a user interface).
Is there an idiomatic way to respond to configuration changes and selectively:
- Reconfigure an existing instance in-place (e.g. camera resolution)
- Reset or recreate a provider (e.g. if the value for a
Selectorchanges) - Cascade updates (e.g., from a changed config section down to the affected components)
Is there a recommended pattern for that?
Minimal Example
from dependency_injector import containers, providers
class PiCamera:
def __init__(self, resolution):
self.resolution = resolution
def capture(self):
return f"PiCamera at {self.resolution}"
class OpenCVCamera:
def __init__(self, device_index):
self.device_index = device_index
def capture(self):
return f"OpenCVCamera device {self.device_index}"
class Container(containers.DeclarativeContainer):
config = providers.Configuration()
camera = providers.Selector(
config.camera.type,
PiCamera=providers.Resource(PiCamera, resolution=config.camera.PiCamera.resolution),
OpenCVCamera=providers.Resource(OpenCVCamera, device_index=config.camera.OpenCVCamera.device_index)
)
# Initial config
container = Container()
container.config.from_dict({
"camera": {
"type": "PiCamera",
"PiCamera": {"resolution": "720p"},
"OpenCVCamera": {"device_index": 0}
}
})
cam = container.camera()
print(cam.capture()) # PiCamera at 720p
# UI now changes config to use OpenCVCamera
new_config = {
"camera": {
"type": "OpenCVCamera",
"PiCamera": {"resolution": "1080p"},
"OpenCVCamera": {"device_index": 1}
}
}
container.config.from_dict(new_config)
# Magic
...
cam = container.camera()
print(cam.capture()) # Expected: OpenCVCamera device 1Is there an idiomatic way to respond to configuration changes and selectively:
Using DI - no, not really.
Reconfigure an existing instance in-place (e.g. camera resolution)
Nope, there are no mechanisms to notify about config changes as of right now.
Reset or recreate a provider (e.g. if the value for a Selector changes)
You can selectively shutdown resources via e.g. container.camera.PiCamera.shutdown() (or in case of singletons - using .reset() method).
Cascade updates (e.g., from a changed config section down to the affected components)
If you shutdown/reset providers using methods mentioned above, providers would be re-initialized using fresh config values, recursively. Provided you re-request new instance from container: explicitly (e.g. container.camera()) or via @inject. But given your use case, I do not think this is good solution long-term.
Is there a recommended pattern for that?
I'd recommend implementing in-memory event bus in your code and use configs only for a cold starts/permanent settings storage.
Thanks for your detailed answer!
I'd recommend implementing in-memory event bus in your code and use configs only for a cold starts/permanent settings storage.
What would that look like? Use a config provider to setup the initial configuration and then have components that subscribe to the event bus to update themselves? That sounds a lot like building the same structure twice (or like I shouldn't be using Dependency Injector at all)...
Use a config provider to setup the initial configuration and then have components that subscribe to the event bus to update themselves?
Pretty much, yes.
That sounds a lot like building the same structure twice
If we're talking only about config updates, then yes. But event bus as a pattern is much more versatile. My robotics knowledge is limited, but I'd imagine the event that might've caused e.g. camera resolution change is caused by specific user action (pushing buttons, remote command, etc...). If you look at events like this, then config update is more like a side effect, not the cause. So in this particular example you'll have at least two event handlers: one for changing camera params, and one for updating actual config file.
or like I shouldn't be using Dependency Injector at all
Well, it depends. DI has web backend DNA in it, which (unsurprisingly) works well for ever-changing web apps. Robotics is much more rigid in this regard, not to mention that performance/memory limitations might be too strict to have extra layers of indirection. But nevertheless it is a still decent "glue" if you have a lot moving parts (pun intended).
Thank you for your thoughts, this is an interesting discussion.
But nevertheless it is a still decent "glue" if you have a lot moving parts (pun intended).
😆
I think we can close this issue, because my initial question was answered.