From 840ac00c668eafe8803bfa6f72c805862d37cb06 Mon Sep 17 00:00:00 2001 From: Kryptonzy <108212748+Kryptonzy@users.noreply.github.com> Date: Mon, 10 Aug 2026 22:58:23 +0800 Subject: [PATCH] Fix required configuration option cache invalidation --- src/dependency_injector/providers.pxd | 1 + src/dependency_injector/providers.pyx | 11 ++++++++++- .../configuration/test_config_py2_py3.py | 18 ++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/dependency_injector/providers.pxd b/src/dependency_injector/providers.pxd index 4332118d..81140f15 100644 --- a/src/dependency_injector/providers.pxd +++ b/src/dependency_injector/providers.pxd @@ -117,6 +117,7 @@ cdef class ConfigurationOption(Provider): cdef Configuration _root cdef dict _children cdef bint _required + cdef object _required_option cdef object _cache diff --git a/src/dependency_injector/providers.pyx b/src/dependency_injector/providers.pyx index 92ea4f37..1935c012 100644 --- a/src/dependency_injector/providers.pyx +++ b/src/dependency_injector/providers.pyx @@ -1522,6 +1522,7 @@ cdef class ConfigurationOption(Provider): self._root = root self._children = {} self._required = required + self._required_option = None self._cache = UNDEFINED super().__init__() @@ -1537,6 +1538,7 @@ cdef class ConfigurationOption(Provider): copied._root = deepcopy(self._root, memo) copied._children = deepcopy(self._children, memo) copied._required = self._required + copied._required_option = deepcopy(self._required_option, memo) self._copy_overridings(copied, memo) return copied @@ -1604,7 +1606,11 @@ cdef class ConfigurationOption(Provider): return TypedConfigurationOption(callback, self, *args, **kwargs) def required(self): - return self.__class__(self._name, self._root, required=True) + if self._required: + return self + if self._required_option is None: + self._required_option = self.__class__(self._name, self._root, required=True) + return self._required_option def is_required(self): return self._required @@ -1626,6 +1632,9 @@ cdef class ConfigurationOption(Provider): for provider in self._children.values(): provider.reset_cache() + if self._required_option is not None: + self._required_option.reset_cache() + for provider in self.overrides: if isinstance(provider, (Configuration, ConfigurationOption)): provider.reset_cache() diff --git a/tests/unit/providers/configuration/test_config_py2_py3.py b/tests/unit/providers/configuration/test_config_py2_py3.py index 34f1b409..41648b67 100644 --- a/tests/unit/providers/configuration/test_config_py2_py3.py +++ b/tests/unit/providers/configuration/test_config_py2_py3.py @@ -121,6 +121,24 @@ def test_required(config): provider() +def test_required_cache_is_reset_after_option_override(): + class Container(containers.DeclarativeContainer): + config = providers.Configuration() + singleton = providers.Singleton(dict, value=config.a.required()) + + container = Container() + container.config.a.from_value("initial") + + assert container.singleton() == {"value": "initial"} + + with container.config.a.override("overridden"): + container.singleton.reset() + assert container.singleton() == {"value": "overridden"} + + container.singleton.reset() + assert container.singleton() == {"value": "initial"} + + def test_required_defined_none(config): provider = providers.Callable( lambda value: value,