Skip to content

Commit eaa195b

Browse files
authored
fix: correctly reset api state on shutdown (#589)
correctly reset api state on shutdown Signed-off-by: gruebel <anton.gruebel@gmail.com>
1 parent 17d62c4 commit eaa195b

File tree

5 files changed

+63
-3
lines changed

5 files changed

+63
-3
lines changed

openfeature/api.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from openfeature import _event_support
22
from openfeature.client import OpenFeatureClient
33
from openfeature.evaluation_context import (
4+
clear_evaluation_context,
45
get_evaluation_context,
56
set_evaluation_context,
67
)
@@ -13,6 +14,7 @@
1314
from openfeature.provider._registry import provider_registry
1415
from openfeature.provider.metadata import Metadata
1516
from openfeature.transaction_context import (
17+
clear_transaction_context_propagator,
1618
get_transaction_context,
1719
set_transaction_context,
1820
set_transaction_context_propagator,
@@ -60,7 +62,14 @@ def get_provider_metadata(domain: str | None = None) -> Metadata:
6062

6163

6264
def shutdown() -> None:
63-
provider_registry.shutdown()
65+
# shutdown -> remove providers -> set default provider to NoOp -> remove event handlers
66+
clear_providers()
67+
# remove hooks
68+
clear_hooks()
69+
# set evaluation context to default
70+
clear_evaluation_context()
71+
# set propagator to NoOp
72+
clear_transaction_context_propagator()
6473

6574

6675
def add_handler(event: ProviderEvent, handler: EventHandler) -> None:

openfeature/evaluation_context/__init__.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@
77

88
from openfeature.exception import GeneralError
99

10-
__all__ = ["EvaluationContext", "get_evaluation_context", "set_evaluation_context"]
10+
__all__ = [
11+
"EvaluationContext",
12+
"clear_evaluation_context",
13+
"get_evaluation_context",
14+
"set_evaluation_context",
15+
]
1116

1217
# https://openfeature.dev/specification/sections/evaluation-context#requirement-312
1318
EvaluationContextAttribute: typing.TypeAlias = (
@@ -47,5 +52,9 @@ def set_evaluation_context(evaluation_context: EvaluationContext) -> None:
4752
_evaluation_context = evaluation_context
4853

4954

55+
def clear_evaluation_context() -> None:
56+
set_evaluation_context(EvaluationContext())
57+
58+
5059
# need to be at the bottom, because of the definition order
5160
_evaluation_context = EvaluationContext()

openfeature/transaction_context/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
__all__ = [
1313
"ContextVarsTransactionContextPropagator",
1414
"TransactionContextPropagator",
15+
"clear_transaction_context_propagator",
1516
"get_transaction_context",
1617
"set_transaction_context",
1718
"set_transaction_context_propagator",
@@ -29,6 +30,10 @@ def set_transaction_context_propagator(
2930
_evaluation_transaction_context_propagator = transaction_context_propagator
3031

3132

33+
def clear_transaction_context_propagator() -> None:
34+
set_transaction_context_propagator(NoOpTransactionContextPropagator())
35+
36+
3237
def get_transaction_context() -> EvaluationContext:
3338
return _evaluation_transaction_context_propagator.get_transaction_context()
3439

tests/test_api.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,13 @@
2121
from openfeature.exception import ErrorCode, GeneralError, ProviderFatalError
2222
from openfeature.hook import Hook
2323
from openfeature.provider import FeatureProvider, Metadata, ProviderStatus
24+
from openfeature.provider._registry import provider_registry
2425
from openfeature.provider.no_op_provider import NoOpProvider
26+
from openfeature.transaction_context import (
27+
ContextVarsTransactionContextPropagator,
28+
get_transaction_context,
29+
set_transaction_context_propagator,
30+
)
2531

2632

2733
def test_should_not_raise_exception_with_noop_client():
@@ -198,6 +204,7 @@ def test_should_not_shutdown_provider_bound_to_another_domain():
198204
provider.shutdown.assert_not_called()
199205

200206

207+
# Requirement 1.6.1
201208
def test_shutdown_should_shutdown_every_registered_provider_once():
202209
# Given
203210
provider_1 = MagicMock(spec=FeatureProvider)
@@ -215,6 +222,35 @@ def test_shutdown_should_shutdown_every_registered_provider_once():
215222
provider_2.shutdown.assert_called_once()
216223

217224

225+
# Requirement 1.6.2
226+
def test_shutdown_should_reset_api_state():
227+
# Given
228+
set_provider(MagicMock(spec=FeatureProvider))
229+
add_hooks([MagicMock(spec=Hook)])
230+
set_evaluation_context(EvaluationContext("targeting_key", {"attr1": "val1"}))
231+
set_transaction_context_propagator(ContextVarsTransactionContextPropagator())
232+
233+
# When
234+
shutdown()
235+
236+
# Then
237+
provider = provider_registry.get_default_provider()
238+
assert isinstance(provider, NoOpProvider)
239+
240+
hooks = get_hooks()
241+
assert not hooks
242+
243+
evaluation_context = get_evaluation_context()
244+
assert evaluation_context.targeting_key is None
245+
assert not evaluation_context.attributes
246+
247+
transaction_context = (
248+
get_transaction_context()
249+
) # NoOpTransactionContextPropagator returns a default context
250+
assert transaction_context.targeting_key is None
251+
assert not transaction_context.attributes
252+
253+
218254
def test_clear_providers_shutdowns_every_provider_and_resets_default_provider():
219255
# Given
220256
provider_1 = MagicMock(spec=FeatureProvider)

tests/test_client.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from openfeature.flag_evaluation import FlagResolutionDetails, FlagType, Reason
2424
from openfeature.hook import Hook
2525
from openfeature.provider import FeatureProvider, ProviderStatus
26+
from openfeature.provider._registry import provider_registry
2627
from openfeature.provider.in_memory_provider import InMemoryFlag, InMemoryProvider
2728
from openfeature.provider.no_op_provider import NoOpProvider
2829
from openfeature.transaction_context import ContextVarsTransactionContextPropagator
@@ -348,7 +349,7 @@ def _shutdown(self) -> None:
348349
monkeypatch.setattr(provider, "shutdown", types.MethodType(_shutdown, provider))
349350

350351
# When
351-
api.shutdown()
352+
provider_registry.shutdown()
352353

353354
status = client.get_provider_status()
354355

0 commit comments

Comments
 (0)