Skip to content

Commit 36af47e

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

19 files changed

Lines changed: 305 additions & 275 deletions

MIGRATION.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Migration Guide
2+
3+
## Unreleased
4+
5+
### Import changes
6+
7+
Several module-level convenience functions moved to `openfeature.api`. Function signatures are unchanged; update imports only.
8+
9+
- Instead of `from openfeature.evaluation_context import get_evaluation_context`, use `from openfeature.api import get_evaluation_context`.
10+
- Instead of `from openfeature.hook import add_hooks`, use `from openfeature.api import add_hooks`.
11+
- Instead of `from openfeature.transaction_context import set_transaction_context_propagator`, use `from openfeature.api import set_transaction_context_propagator`.
12+
- Instead of `from openfeature.transaction_context import NoOpTransactionContextPropagator`, use `from openfeature.transaction_context.no_op_transaction_context_propagator import NoOpTransactionContextPropagator`.
13+
14+
Type imports (`EvaluationContext`, `Hook`, `HookContext`, `HookData`, `HookHints`, `HookType`, `ContextVarsTransactionContextPropagator`, `TransactionContextPropagator`) are unchanged.
15+
16+
### Constructor change: `OpenFeatureClient`
17+
18+
`OpenFeatureClient.__init__` now requires the owning `OpenFeatureAPI` (3rd positional or `api=` keyword).
19+
20+
Prefer the factory:
21+
22+
```python
23+
from openfeature import api
24+
25+
client = api.get_client()
26+
client = api.get_client(domain="my-domain")
27+
```
28+
29+
For direct construction, pass `api`:
30+
31+
```python
32+
# Before
33+
client = OpenFeatureClient("my-domain", None)
34+
client = OpenFeatureClient("my-domain", None, context=ctx)
35+
36+
# After
37+
client = OpenFeatureClient("my-domain", None, api=my_api)
38+
client = OpenFeatureClient("my-domain", None, my_api, context=ctx)
39+
```

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: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,24 +77,27 @@ class ClientMetadata:
7777

7878

7979
class OpenFeatureClient:
80+
"""Client for evaluating feature flags against a specific OpenFeatureAPI.
81+
82+
Clients should be obtained via ``OpenFeatureAPI.get_client()`` (or the
83+
module-level ``openfeature.api.get_client()`` for the default API);
84+
direct construction is supported only for advanced use cases and requires
85+
passing the owning ``OpenFeatureAPI`` instance.
86+
"""
87+
8088
def __init__(
8189
self,
8290
domain: str | None,
8391
version: str | None,
92+
api: OpenFeatureAPI,
8493
context: EvaluationContext | None = None,
8594
hooks: list[Hook] | None = None,
86-
api: OpenFeatureAPI | None = None,
8795
) -> None:
8896
self.domain = domain
8997
self.version = version
9098
self.context = context or EvaluationContext()
9199
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
100+
self._api = api
98101

99102
@property
100103
def provider(self) -> FeatureProvider:

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)