Skip to content

Commit 0ba9546

Browse files
committed
fixup: pr feedback
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
1 parent 556afed commit 0ba9546

4 files changed

Lines changed: 50 additions & 6 deletions

File tree

openfeature/_api.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,13 +104,19 @@ def set_evaluation_context(self, evaluation_context: EvaluationContext) -> None:
104104
raise GeneralError(error_message="No api level evaluation context")
105105
self._evaluation_context = evaluation_context
106106

107+
def clear_evaluation_context(self) -> None:
108+
self.set_evaluation_context(EvaluationContext())
109+
107110
# --- Transaction context ---
108111

109112
def set_transaction_context_propagator(
110113
self, transaction_context_propagator: TransactionContextPropagator
111114
) -> None:
112115
self._transaction_context_propagator = transaction_context_propagator
113116

117+
def clear_transaction_context_propagator(self) -> None:
118+
self.set_transaction_context_propagator(NoOpTransactionContextPropagator())
119+
114120
def get_transaction_context(self) -> EvaluationContext:
115121
return self._transaction_context_propagator.get_transaction_context()
116122

openfeature/api.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@
99
from openfeature.provider import FeatureProvider
1010
from openfeature.provider.metadata import Metadata
1111
from openfeature.transaction_context import TransactionContextPropagator
12-
from openfeature.transaction_context.no_op_transaction_context_propagator import (
13-
NoOpTransactionContextPropagator,
14-
)
1512

1613
__all__ = [
1714
"add_handler",
@@ -90,7 +87,7 @@ def set_evaluation_context(evaluation_context: EvaluationContext) -> None:
9087

9188

9289
def clear_evaluation_context() -> None:
93-
set_evaluation_context(EvaluationContext())
90+
_default_api.clear_evaluation_context()
9491

9592

9693
def set_transaction_context_propagator(
@@ -100,7 +97,7 @@ def set_transaction_context_propagator(
10097

10198

10299
def clear_transaction_context_propagator() -> None:
103-
set_transaction_context_propagator(NoOpTransactionContextPropagator())
100+
_default_api.clear_transaction_context_propagator()
104101

105102

106103
def get_transaction_context() -> EvaluationContext:

openfeature/provider/_registry.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@
2727

2828

2929
def _register_binding(provider: FeatureProvider, owner: ProviderRegistry) -> None:
30+
try:
31+
weakref.ref(provider)
32+
except TypeError as exc:
33+
raise TypeError(
34+
f"Provider {type(provider).__name__!r} cannot be tracked because "
35+
"it is not weak-referenceable. If your provider class uses "
36+
"__slots__, add '__weakref__' to the slots list."
37+
) from exc
3038
with _binding_lock:
3139
existing = _provider_bindings.get(provider)
3240
if existing is not None and existing is not owner:

tests/test_isolated_api.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from openfeature.event import ProviderEvent, ProviderEventDetails
1313
from openfeature.hook import Hook
1414
from openfeature.isolated import OpenFeatureAPI, create_api
15-
from openfeature.provider import FeatureProvider, ProviderStatus
15+
from openfeature.provider import FeatureProvider, Metadata, ProviderStatus
1616
from openfeature.provider.no_op_provider import NoOpProvider
1717
from openfeature.transaction_context import ContextVarsTransactionContextPropagator
1818

@@ -46,8 +46,10 @@ def test_isolated_instance_is_openfeature_api():
4646
_ISOLATED_API_PUBLIC_METHODS = (
4747
"add_handler",
4848
"add_hooks",
49+
"clear_evaluation_context",
4950
"clear_hooks",
5051
"clear_providers",
52+
"clear_transaction_context_propagator",
5153
"get_client",
5254
"get_evaluation_context",
5355
"get_hooks",
@@ -189,6 +191,37 @@ def test_provider_can_be_rebound_after_being_released():
189191
assert api2.get_provider() is provider
190192

191193

194+
def test_set_provider_rejects_non_weak_referenceable_provider():
195+
"""Providers must be weak-referenceable so the SDK can track bindings
196+
without leaking memory; surfacing this requirement up front (rather than
197+
silently skipping the spec 1.8.4 check) avoids hard-to-diagnose bugs."""
198+
199+
# A direct ``object`` subclass with ``__slots__`` and no ``__weakref__``
200+
# entry; instances are not weak-referenceable. Implements the
201+
# ``FeatureProvider`` protocol structurally rather than via inheritance
202+
# (which would inherit ``__weakref__`` from the parent class).
203+
class NotWeakReferenceable:
204+
__slots__ = ()
205+
206+
def attach(self, on_emit):
207+
pass
208+
209+
def detach(self):
210+
pass
211+
212+
def get_metadata(self):
213+
return Metadata(name="not-weak-referenceable")
214+
215+
def get_provider_hooks(self):
216+
return []
217+
218+
provider = NotWeakReferenceable()
219+
api_instance = create_api()
220+
221+
with pytest.raises(TypeError, match="weak-referenceable"):
222+
api_instance.set_provider(provider) # type: ignore[arg-type]
223+
224+
192225
# --- Isolated state: hooks ---
193226

194227

0 commit comments

Comments
 (0)