Skip to content

Commit d5b3dde

Browse files
committed
fixup: remove lazy imports, shims, add check
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
1 parent 7ad838b commit d5b3dde

18 files changed

Lines changed: 272 additions & 287 deletions

File tree

openfeature/_api.py

Lines changed: 5 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,25 @@
11
from __future__ import annotations
22

3-
import typing
4-
53
from openfeature._event_support import EventSupport
4+
from openfeature.client import OpenFeatureClient
65
from openfeature.evaluation_context import EvaluationContext
76
from openfeature.event import EventHandler, ProviderEvent
87
from openfeature.exception import GeneralError
98
from openfeature.hook import Hook
109
from openfeature.provider import FeatureProvider, ProviderStatus
1110
from openfeature.provider._registry import ProviderRegistry
1211
from openfeature.provider.metadata import Metadata
13-
from openfeature.transaction_context import (
12+
from openfeature.transaction_context import TransactionContextPropagator
13+
from openfeature.transaction_context.no_op_transaction_context_propagator import (
1414
NoOpTransactionContextPropagator,
15-
TransactionContextPropagator,
1615
)
1716

18-
if typing.TYPE_CHECKING:
19-
from openfeature.client import OpenFeatureClient
20-
2117

2218
class OpenFeatureAPI:
2319
"""An independent OpenFeature API instance with its own isolated state.
2420
2521
Each instance maintains its own providers, evaluation context, hooks,
26-
event handlers, and transaction context propagator fully separate from
22+
event handlers, and transaction context propagator; fully separate from
2723
the global singleton and from other instances.
2824
"""
2925

@@ -44,8 +40,6 @@ def __init__(self) -> None:
4440
def get_client(
4541
self, domain: str | None = None, version: str | None = None
4642
) -> OpenFeatureClient:
47-
from openfeature.client import OpenFeatureClient # noqa: PLC0415
48-
4943
return OpenFeatureClient(domain=domain, version=version, api=self)
5044

5145
# --- Provider management ---
@@ -132,28 +126,4 @@ def remove_handler(self, event: ProviderEvent, handler: EventHandler) -> None:
132126
self._event_support.remove_global_handler(event, handler)
133127

134128

135-
def _create_default_api() -> OpenFeatureAPI:
136-
"""Create the default global API instance, wired to legacy module-level singletons.
137-
138-
The default API reuses the module-level ``_default_event_support`` and
139-
``provider_registry`` so that backward-compatible module-level functions
140-
continue to work against the same state.
141-
"""
142-
from openfeature._event_support import _default_event_support # noqa: PLC0415
143-
from openfeature.provider._registry import provider_registry # noqa: PLC0415
144-
145-
api = OpenFeatureAPI.__new__(OpenFeatureAPI)
146-
api._hooks = []
147-
api._evaluation_context = EvaluationContext()
148-
api._transaction_context_propagator = NoOpTransactionContextPropagator()
149-
api._event_support = _default_event_support
150-
api._provider_registry = provider_registry
151-
152-
# Wire the registry to this API's event support and context getter
153-
provider_registry._event_support = _default_event_support
154-
provider_registry._evaluation_context_getter = api.get_evaluation_context
155-
156-
return api
157-
158-
159-
_default_api = _create_default_api()
129+
_default_api = OpenFeatureAPI()

openfeature/_event_support.py

Lines changed: 0 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -145,52 +145,3 @@ def clear(self) -> None:
145145
self._global_handlers.clear()
146146
with self._client_lock:
147147
self._client_handlers.clear()
148-
149-
150-
# Default instance used by the global singleton API
151-
_default_event_support = EventSupport()
152-
153-
154-
# Backward-compatible module-level functions delegating to the default instance
155-
def run_client_handlers(
156-
client: OpenFeatureClient, event: ProviderEvent, details: EventDetails
157-
) -> None:
158-
_default_event_support.run_client_handlers(client, event, details)
159-
160-
161-
def run_global_handlers(event: ProviderEvent, details: EventDetails) -> None:
162-
_default_event_support.run_global_handlers(event, details)
163-
164-
165-
def add_client_handler(
166-
client: OpenFeatureClient, event: ProviderEvent, handler: EventHandler
167-
) -> None:
168-
_default_event_support.add_client_handler(client, event, handler)
169-
170-
171-
def remove_client_handler(
172-
client: OpenFeatureClient, event: ProviderEvent, handler: EventHandler
173-
) -> None:
174-
_default_event_support.remove_client_handler(client, event, handler)
175-
176-
177-
def add_global_handler(event: ProviderEvent, handler: EventHandler) -> None:
178-
from openfeature.api import get_client # noqa: PLC0415
179-
180-
_default_event_support.add_global_handler(event, handler, get_client)
181-
182-
183-
def remove_global_handler(event: ProviderEvent, handler: EventHandler) -> None:
184-
_default_event_support.remove_global_handler(event, handler)
185-
186-
187-
def run_handlers_for_provider(
188-
provider: FeatureProvider,
189-
event: ProviderEvent,
190-
provider_details: ProviderEventDetails,
191-
) -> None:
192-
_default_event_support.run_handlers_for_provider(provider, event, provider_details)
193-
194-
195-
def clear() -> None:
196-
_default_event_support.clear()

openfeature/api.py

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,25 @@
11
from openfeature._api import _default_api
22
from openfeature.client import OpenFeatureClient
3-
from openfeature.evaluation_context import (
4-
get_evaluation_context,
5-
set_evaluation_context,
6-
)
3+
from openfeature.evaluation_context import EvaluationContext
74
from openfeature.event import (
85
EventHandler,
96
ProviderEvent,
107
)
11-
from openfeature.hook import add_hooks, clear_hooks, get_hooks
8+
from openfeature.hook import Hook
129
from openfeature.provider import FeatureProvider
1310
from openfeature.provider.metadata import Metadata
14-
from openfeature.transaction_context import (
15-
get_transaction_context,
16-
set_transaction_context,
17-
set_transaction_context_propagator,
11+
from openfeature.transaction_context import TransactionContextPropagator
12+
from openfeature.transaction_context.no_op_transaction_context_propagator import (
13+
NoOpTransactionContextPropagator,
1814
)
1915

2016
__all__ = [
2117
"add_handler",
2218
"add_hooks",
19+
"clear_evaluation_context",
2320
"clear_hooks",
2421
"clear_providers",
22+
"clear_transaction_context_propagator",
2523
"get_client",
2624
"get_evaluation_context",
2725
"get_hooks",
@@ -69,3 +67,45 @@ def add_handler(event: ProviderEvent, handler: EventHandler) -> None:
6967

7068
def remove_handler(event: ProviderEvent, handler: EventHandler) -> None:
7169
_default_api.remove_handler(event, handler)
70+
71+
72+
def add_hooks(hooks: list[Hook]) -> None:
73+
_default_api.add_hooks(hooks)
74+
75+
76+
def clear_hooks() -> None:
77+
_default_api.clear_hooks()
78+
79+
80+
def get_hooks() -> list[Hook]:
81+
return _default_api.get_hooks()
82+
83+
84+
def get_evaluation_context() -> EvaluationContext:
85+
return _default_api.get_evaluation_context()
86+
87+
88+
def set_evaluation_context(evaluation_context: EvaluationContext) -> None:
89+
_default_api.set_evaluation_context(evaluation_context)
90+
91+
92+
def clear_evaluation_context() -> None:
93+
set_evaluation_context(EvaluationContext())
94+
95+
96+
def set_transaction_context_propagator(
97+
transaction_context_propagator: TransactionContextPropagator,
98+
) -> None:
99+
_default_api.set_transaction_context_propagator(transaction_context_propagator)
100+
101+
102+
def clear_transaction_context_propagator() -> None:
103+
set_transaction_context_propagator(NoOpTransactionContextPropagator())
104+
105+
106+
def get_transaction_context() -> EvaluationContext:
107+
return _default_api.get_transaction_context()
108+
109+
110+
def set_transaction_context(evaluation_context: EvaluationContext) -> None:
111+
_default_api.set_transaction_context(evaluation_context)

openfeature/client.py

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
from __future__ import annotations
2-
31
import logging
42
import typing
53
from collections.abc import Awaitable, Mapping, Sequence
@@ -77,24 +75,27 @@ class ClientMetadata:
7775

7876

7977
class OpenFeatureClient:
78+
"""Client for evaluating feature flags against a specific OpenFeatureAPI.
79+
80+
Clients should be obtained via ``OpenFeatureAPI.get_client()`` (or the
81+
module-level ``openfeature.api.get_client()`` for the default API);
82+
direct construction is supported only for advanced use cases and requires
83+
passing the owning ``OpenFeatureAPI`` instance.
84+
"""
85+
8086
def __init__(
8187
self,
8288
domain: str | None,
8389
version: str | None,
90+
api: "OpenFeatureAPI",
8491
context: EvaluationContext | None = None,
8592
hooks: list[Hook] | None = None,
86-
api: OpenFeatureAPI | None = None,
8793
) -> None:
8894
self.domain = domain
8995
self.version = version
9096
self.context = context or EvaluationContext()
9197
self.hooks = hooks or []
92-
if api is not None:
93-
self._api = api
94-
else:
95-
from openfeature._api import _default_api # noqa: PLC0415
96-
97-
self._api = _default_api
98+
self._api = api
9899

99100
@property
100101
def provider(self) -> FeatureProvider:
@@ -549,20 +550,20 @@ async def evaluate_flag_details_async(
549550
self,
550551
flag_type: FlagType,
551552
flag_key: str,
552-
default_value: Sequence[FlagValueType],
553+
default_value: Sequence["FlagValueType"],
553554
evaluation_context: EvaluationContext | None = None,
554555
flag_evaluation_options: FlagEvaluationOptions | None = None,
555-
) -> FlagEvaluationDetails[Sequence[FlagValueType]]: ...
556+
) -> FlagEvaluationDetails[Sequence["FlagValueType"]]: ...
556557

557558
@typing.overload
558559
async def evaluate_flag_details_async(
559560
self,
560561
flag_type: FlagType,
561562
flag_key: str,
562-
default_value: Mapping[str, FlagValueType],
563+
default_value: Mapping[str, "FlagValueType"],
563564
evaluation_context: EvaluationContext | None = None,
564565
flag_evaluation_options: FlagEvaluationOptions | None = None,
565-
) -> FlagEvaluationDetails[Mapping[str, FlagValueType]]: ...
566+
) -> FlagEvaluationDetails[Mapping[str, "FlagValueType"]]: ...
566567

567568
async def evaluate_flag_details_async(
568569
self,
@@ -725,20 +726,20 @@ def evaluate_flag_details(
725726
self,
726727
flag_type: FlagType,
727728
flag_key: str,
728-
default_value: Sequence[FlagValueType],
729+
default_value: Sequence["FlagValueType"],
729730
evaluation_context: EvaluationContext | None = None,
730731
flag_evaluation_options: FlagEvaluationOptions | None = None,
731-
) -> FlagEvaluationDetails[Sequence[FlagValueType]]: ...
732+
) -> FlagEvaluationDetails[Sequence["FlagValueType"]]: ...
732733

733734
@typing.overload
734735
def evaluate_flag_details(
735736
self,
736737
flag_type: FlagType,
737738
flag_key: str,
738-
default_value: Mapping[str, FlagValueType],
739+
default_value: Mapping[str, "FlagValueType"],
739740
evaluation_context: EvaluationContext | None = None,
740741
flag_evaluation_options: FlagEvaluationOptions | None = None,
741-
) -> FlagEvaluationDetails[Mapping[str, FlagValueType]]: ...
742+
) -> FlagEvaluationDetails[Mapping[str, "FlagValueType"]]: ...
742743

743744
def evaluate_flag_details(
744745
self,

openfeature/evaluation_context/__init__.py

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from dataclasses import dataclass, field
66
from datetime import datetime
77

8-
__all__ = ["EvaluationContext", "get_evaluation_context", "set_evaluation_context"]
8+
__all__ = ["EvaluationContext"]
99

1010
# https://openfeature.dev/specification/sections/evaluation-context#requirement-312
1111
EvaluationContextAttribute: typing.TypeAlias = (
@@ -32,19 +32,3 @@ def merge(self, ctx2: EvaluationContext) -> EvaluationContext:
3232
targeting_key = ctx2.targeting_key or self.targeting_key
3333

3434
return EvaluationContext(targeting_key=targeting_key, attributes=attributes)
35-
36-
37-
def get_evaluation_context() -> EvaluationContext:
38-
from openfeature._api import _default_api # noqa: PLC0415
39-
40-
return _default_api.get_evaluation_context()
41-
42-
43-
def set_evaluation_context(evaluation_context: EvaluationContext) -> None:
44-
from openfeature._api import _default_api # noqa: PLC0415
45-
46-
_default_api.set_evaluation_context(evaluation_context)
47-
48-
49-
# Kept for backward compatibility but no longer used; state lives in _default_api.
50-
_evaluation_context = EvaluationContext()

openfeature/hook/__init__.py

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@
1818
"HookData",
1919
"HookHints",
2020
"HookType",
21-
"add_hooks",
22-
"clear_hooks",
23-
"get_hooks",
2421
]
2522

2623

@@ -147,21 +144,3 @@ def supports_flag_value_type(self, flag_type: FlagType) -> bool:
147144
or not (False)
148145
"""
149146
return True
150-
151-
152-
def add_hooks(hooks: list[Hook]) -> None:
153-
from openfeature._api import _default_api # noqa: PLC0415
154-
155-
_default_api.add_hooks(hooks)
156-
157-
158-
def clear_hooks() -> None:
159-
from openfeature._api import _default_api # noqa: PLC0415
160-
161-
_default_api.clear_hooks()
162-
163-
164-
def get_hooks() -> list[Hook]:
165-
from openfeature._api import _default_api # noqa: PLC0415
166-
167-
return _default_api.get_hooks()

0 commit comments

Comments
 (0)