Skip to content

Commit 3636a0d

Browse files
gruebelbeeme1mr
andauthored
fix: fix cycle dependency between api and client (#480)
* fix cycle dependency between api and client Signed-off-by: gruebel <[email protected]> * remove comment Signed-off-by: gruebel <[email protected]> --------- Signed-off-by: gruebel <[email protected]> Co-authored-by: Michael Beemer <[email protected]>
1 parent e61b69b commit 3636a0d

File tree

7 files changed

+109
-89
lines changed

7 files changed

+109
-89
lines changed

openfeature/api.py

+9-56
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,22 @@
22

33
from openfeature import _event_support
44
from openfeature.client import OpenFeatureClient
5-
from openfeature.evaluation_context import EvaluationContext
5+
from openfeature.evaluation_context import (
6+
get_evaluation_context,
7+
set_evaluation_context,
8+
)
69
from openfeature.event import (
710
EventHandler,
811
ProviderEvent,
912
)
10-
from openfeature.exception import GeneralError
11-
from openfeature.hook import Hook
13+
from openfeature.hook import add_hooks, clear_hooks, get_hooks
1214
from openfeature.provider import FeatureProvider
1315
from openfeature.provider._registry import provider_registry
1416
from openfeature.provider.metadata import Metadata
15-
from openfeature.transaction_context import TransactionContextPropagator
16-
from openfeature.transaction_context.no_op_transaction_context_propagator import (
17-
NoOpTransactionContextPropagator,
17+
from openfeature.transaction_context import (
18+
get_transaction_context,
19+
set_transaction_context,
20+
set_transaction_context_propagator,
1821
)
1922

2023
__all__ = [
@@ -35,13 +38,6 @@
3538
"shutdown",
3639
]
3740

38-
_evaluation_context = EvaluationContext()
39-
_evaluation_transaction_context_propagator: TransactionContextPropagator = (
40-
NoOpTransactionContextPropagator()
41-
)
42-
43-
_hooks: list[Hook] = []
44-
4541

4642
def get_client(
4743
domain: typing.Optional[str] = None, version: typing.Optional[str] = None
@@ -67,49 +63,6 @@ def get_provider_metadata(domain: typing.Optional[str] = None) -> Metadata:
6763
return provider_registry.get_provider(domain).get_metadata()
6864

6965

70-
def get_evaluation_context() -> EvaluationContext:
71-
return _evaluation_context
72-
73-
74-
def set_evaluation_context(evaluation_context: EvaluationContext) -> None:
75-
global _evaluation_context
76-
if evaluation_context is None:
77-
raise GeneralError(error_message="No api level evaluation context")
78-
_evaluation_context = evaluation_context
79-
80-
81-
def set_transaction_context_propagator(
82-
transaction_context_propagator: TransactionContextPropagator,
83-
) -> None:
84-
global _evaluation_transaction_context_propagator
85-
_evaluation_transaction_context_propagator = transaction_context_propagator
86-
87-
88-
def get_transaction_context() -> EvaluationContext:
89-
return _evaluation_transaction_context_propagator.get_transaction_context()
90-
91-
92-
def set_transaction_context(evaluation_context: EvaluationContext) -> None:
93-
global _evaluation_transaction_context_propagator
94-
_evaluation_transaction_context_propagator.set_transaction_context(
95-
evaluation_context
96-
)
97-
98-
99-
def add_hooks(hooks: list[Hook]) -> None:
100-
global _hooks
101-
_hooks = _hooks + hooks
102-
103-
104-
def clear_hooks() -> None:
105-
global _hooks
106-
_hooks = []
107-
108-
109-
def get_hooks() -> list[Hook]:
110-
return _hooks
111-
112-
11366
def shutdown() -> None:
11467
provider_registry.shutdown()
11568

openfeature/client.py

+7-9
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
from collections.abc import Awaitable
44
from dataclasses import dataclass
55

6-
from openfeature import _event_support, api
7-
from openfeature.evaluation_context import EvaluationContext
6+
from openfeature import _event_support
7+
from openfeature.evaluation_context import EvaluationContext, get_evaluation_context
88
from openfeature.event import EventHandler, ProviderEvent
99
from openfeature.exception import (
1010
ErrorCode,
@@ -21,7 +21,7 @@
2121
FlagType,
2222
Reason,
2323
)
24-
from openfeature.hook import Hook, HookContext, HookHints
24+
from openfeature.hook import Hook, HookContext, HookHints, get_hooks
2525
from openfeature.hook._hook_support import (
2626
after_all_hooks,
2727
after_hooks,
@@ -30,6 +30,7 @@
3030
)
3131
from openfeature.provider import FeatureProvider, ProviderStatus
3232
from openfeature.provider._registry import provider_registry
33+
from openfeature.transaction_context import get_transaction_context
3334

3435
__all__ = [
3536
"ClientMetadata",
@@ -433,10 +434,7 @@ def _establish_hooks_and_provider(
433434
# in the flag evaluation
434435
# before: API, Client, Invocation, Provider
435436
merged_hooks = (
436-
api.get_hooks()
437-
+ self.hooks
438-
+ evaluation_hooks
439-
+ provider.get_provider_hooks()
437+
get_hooks() + self.hooks + evaluation_hooks + provider.get_provider_hooks()
440438
)
441439
# after, error, finally: Provider, Invocation, Client, API
442440
reversed_merged_hooks = merged_hooks[:]
@@ -474,8 +472,8 @@ def _before_hooks_and_merge_context(
474472

475473
# Requirement 3.2.2 merge: API.context->transaction.context->client.context->invocation.context
476474
merged_context = (
477-
api.get_evaluation_context()
478-
.merge(api.get_transaction_context())
475+
get_evaluation_context()
476+
.merge(get_transaction_context())
479477
.merge(self.context)
480478
.merge(invocation_context)
481479
)

openfeature/evaluation_context.py

-19
This file was deleted.
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from __future__ import annotations
2+
3+
import typing
4+
from dataclasses import dataclass, field
5+
6+
from openfeature.exception import GeneralError
7+
8+
__all__ = ["EvaluationContext", "get_evaluation_context", "set_evaluation_context"]
9+
10+
11+
@dataclass
12+
class EvaluationContext:
13+
targeting_key: typing.Optional[str] = None
14+
attributes: dict = field(default_factory=dict)
15+
16+
def merge(self, ctx2: EvaluationContext) -> EvaluationContext:
17+
if not (self and ctx2):
18+
return self or ctx2
19+
20+
attributes = {**self.attributes, **ctx2.attributes}
21+
targeting_key = ctx2.targeting_key or self.targeting_key
22+
23+
return EvaluationContext(targeting_key=targeting_key, attributes=attributes)
24+
25+
26+
def get_evaluation_context() -> EvaluationContext:
27+
return _evaluation_context
28+
29+
30+
def set_evaluation_context(evaluation_context: EvaluationContext) -> None:
31+
global _evaluation_context
32+
if evaluation_context is None:
33+
raise GeneralError(error_message="No api level evaluation context")
34+
_evaluation_context = evaluation_context
35+
36+
37+
# need to be at the bottom, because of the definition order
38+
_evaluation_context = EvaluationContext()

openfeature/hook/__init__.py

+25-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,17 @@
1212
from openfeature.client import ClientMetadata
1313
from openfeature.provider.metadata import Metadata
1414

15-
__all__ = ["Hook", "HookContext", "HookHints", "HookType"]
15+
__all__ = [
16+
"Hook",
17+
"HookContext",
18+
"HookHints",
19+
"HookType",
20+
"add_hooks",
21+
"clear_hooks",
22+
"get_hooks",
23+
]
24+
25+
_hooks: list[Hook] = []
1626

1727

1828
class HookType(Enum):
@@ -133,3 +143,17 @@ def supports_flag_value_type(self, flag_type: FlagType) -> bool:
133143
or not (False)
134144
"""
135145
return True
146+
147+
148+
def add_hooks(hooks: list[Hook]) -> None:
149+
global _hooks
150+
_hooks = _hooks + hooks
151+
152+
153+
def clear_hooks() -> None:
154+
global _hooks
155+
_hooks = []
156+
157+
158+
def get_hooks() -> list[Hook]:
159+
return _hooks

openfeature/provider/_registry.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import typing
22

33
from openfeature._event_support import run_handlers_for_provider
4-
from openfeature.evaluation_context import EvaluationContext
4+
from openfeature.evaluation_context import EvaluationContext, get_evaluation_context
55
from openfeature.event import (
66
ProviderEvent,
77
ProviderEventDetails,
@@ -65,9 +65,6 @@ def shutdown(self) -> None:
6565
self._shutdown_provider(provider)
6666

6767
def _get_evaluation_context(self) -> EvaluationContext:
68-
# imported here to avoid circular imports
69-
from openfeature.api import get_evaluation_context
70-
7168
return get_evaluation_context()
7269

7370
def _initialize_provider(self, provider: FeatureProvider) -> None:
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,40 @@
1+
from openfeature.evaluation_context import EvaluationContext
12
from openfeature.transaction_context.context_var_transaction_context_propagator import (
23
ContextVarsTransactionContextPropagator,
34
)
5+
from openfeature.transaction_context.no_op_transaction_context_propagator import (
6+
NoOpTransactionContextPropagator,
7+
)
48
from openfeature.transaction_context.transaction_context_propagator import (
59
TransactionContextPropagator,
610
)
711

812
__all__ = [
913
"ContextVarsTransactionContextPropagator",
1014
"TransactionContextPropagator",
15+
"get_transaction_context",
16+
"set_transaction_context",
17+
"set_transaction_context_propagator",
1118
]
19+
20+
_evaluation_transaction_context_propagator: TransactionContextPropagator = (
21+
NoOpTransactionContextPropagator()
22+
)
23+
24+
25+
def set_transaction_context_propagator(
26+
transaction_context_propagator: TransactionContextPropagator,
27+
) -> None:
28+
global _evaluation_transaction_context_propagator
29+
_evaluation_transaction_context_propagator = transaction_context_propagator
30+
31+
32+
def get_transaction_context() -> EvaluationContext:
33+
return _evaluation_transaction_context_propagator.get_transaction_context()
34+
35+
36+
def set_transaction_context(evaluation_context: EvaluationContext) -> None:
37+
global _evaluation_transaction_context_propagator
38+
_evaluation_transaction_context_propagator.set_transaction_context(
39+
evaluation_context
40+
)

0 commit comments

Comments
 (0)