Summary
openfeature.provider.in_memory_provider.InMemoryProvider has no way to update its flag set. Appendix A requires one:
The provider must support a means of updating the flag set, resulting in the emission of PROVIDER_CONFIGURATION_CHANGED events.
- The change event should consider all flags changed; a union of all previous and all new flag keys should be supplied in the
flags changed field.
The provider copies the mapping in its constructor and exposes nothing to change it afterwards:
class InMemoryProvider(AbstractProvider):
_flags: FlagStorage
def __init__(self, flags: FlagStorage) -> None:
self._flags = flags.copy()
Because it is a copy, a caller cannot even mutate the dict it passed in as a workaround.
What makes this a small fix
Unlike some SDKs, only half the machinery is missing. AbstractProvider already supplies emit_provider_configuration_changed, and the registry already calls attach() to wire the emitter up. All that is absent is a method that swaps the mapping and emits.
Comparison with the other SDKs
| SDK |
update method |
emits PROVIDER_CONFIGURATION_CHANGED |
| JavaScript |
putConfiguration() |
yes |
| Java |
updateFlag() |
yes |
| Python |
none |
no |
| Go |
none |
no — go-sdk#530 |
Why it matters beyond the missing method
Appendix A also says:
E2E tests must utilize in-memory provider defined within the SDK and must be self-contained.
So the gap propagates: the SDK's own Appendix B suite cannot cover configuration-change events, because the provider it is required to use cannot produce one. Any consumer wanting to test their own reaction to PROVIDER_CONFIGURATION_CHANGED has to write their own provider.
Suggested shape
def update_flags(self, flags: FlagStorage) -> None:
changed = sorted(set(self._flags) | set(flags))
self._flags = dict(flags)
self.emit_provider_configuration_changed(
ProviderEventDetails(flags_changed=changed)
)
A working implementation is ControllableInMemoryProvider in the linked PR. It subclasses this provider rather than replacing it — every resolution decision is still the SDK's — so it should port over as a method with no behaviour change. Happy to open a PR here instead if that shape looks right.
How it surfaced
Building the Python implementation of the cross-language provider conformance suite (spec#417). The suite's in-memory self-test has to leave the @configuration-change capability undeclared and report the scenario as skipped.
Summary
openfeature.provider.in_memory_provider.InMemoryProviderhas no way to update its flag set. Appendix A requires one:The provider copies the mapping in its constructor and exposes nothing to change it afterwards:
Because it is a copy, a caller cannot even mutate the dict it passed in as a workaround.
What makes this a small fix
Unlike some SDKs, only half the machinery is missing.
AbstractProvideralready suppliesemit_provider_configuration_changed, and the registry already callsattach()to wire the emitter up. All that is absent is a method that swaps the mapping and emits.Comparison with the other SDKs
PROVIDER_CONFIGURATION_CHANGEDputConfiguration()updateFlag()Why it matters beyond the missing method
Appendix A also says:
So the gap propagates: the SDK's own Appendix B suite cannot cover configuration-change events, because the provider it is required to use cannot produce one. Any consumer wanting to test their own reaction to
PROVIDER_CONFIGURATION_CHANGEDhas to write their own provider.Suggested shape
A working implementation is
ControllableInMemoryProviderin the linked PR. It subclasses this provider rather than replacing it — every resolution decision is still the SDK's — so it should port over as a method with no behaviour change. Happy to open a PR here instead if that shape looks right.How it surfaced
Building the Python implementation of the cross-language provider conformance suite (spec#417). The suite's in-memory self-test has to leave the
@configuration-changecapability undeclared and report the scenario as skipped.