flagsmith 5.3.0__tar.gz → 5.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {flagsmith-5.3.0 → flagsmith-5.4.0}/PKG-INFO +1 -1
- flagsmith-5.4.0/flagsmith/__init__.py +6 -0
- {flagsmith-5.3.0 → flagsmith-5.4.0}/flagsmith/analytics.py +54 -56
- {flagsmith-5.3.0 → flagsmith-5.4.0}/flagsmith/flagsmith.py +91 -30
- {flagsmith-5.3.0 → flagsmith-5.4.0}/flagsmith/models.py +1 -37
- {flagsmith-5.3.0 → flagsmith-5.4.0}/pyproject.toml +1 -1
- flagsmith-5.3.0/flagsmith/__init__.py +0 -6
- {flagsmith-5.3.0 → flagsmith-5.4.0}/LICENSE +0 -0
- {flagsmith-5.3.0 → flagsmith-5.4.0}/README.md +0 -0
- {flagsmith-5.3.0 → flagsmith-5.4.0}/flagsmith/api/__init__.py +0 -0
- {flagsmith-5.3.0 → flagsmith-5.4.0}/flagsmith/api/types.py +0 -0
- {flagsmith-5.3.0 → flagsmith-5.4.0}/flagsmith/exceptions.py +0 -0
- {flagsmith-5.3.0 → flagsmith-5.4.0}/flagsmith/mappers.py +0 -0
- {flagsmith-5.3.0 → flagsmith-5.4.0}/flagsmith/offline_handlers.py +0 -0
- {flagsmith-5.3.0 → flagsmith-5.4.0}/flagsmith/polling_manager.py +0 -0
- {flagsmith-5.3.0 → flagsmith-5.4.0}/flagsmith/py.typed +0 -0
- {flagsmith-5.3.0 → flagsmith-5.4.0}/flagsmith/streaming_manager.py +0 -0
- {flagsmith-5.3.0 → flagsmith-5.4.0}/flagsmith/types.py +0 -0
- {flagsmith-5.3.0 → flagsmith-5.4.0}/flagsmith/utils/__init__.py +0 -0
- {flagsmith-5.3.0 → flagsmith-5.4.0}/flagsmith/utils/datetime.py +0 -0
- {flagsmith-5.3.0 → flagsmith-5.4.0}/flagsmith/utils/identities.py +0 -0
- {flagsmith-5.3.0 → flagsmith-5.4.0}/flagsmith/version.py +0 -0
- {flagsmith-5.3.0 → flagsmith-5.4.0}/flagsmith/webhooks.py +0 -0
|
@@ -2,7 +2,6 @@ import atexit
|
|
|
2
2
|
import json
|
|
3
3
|
import logging
|
|
4
4
|
import threading
|
|
5
|
-
import time
|
|
6
5
|
import typing
|
|
7
6
|
from dataclasses import dataclass
|
|
8
7
|
from datetime import datetime
|
|
@@ -18,6 +17,10 @@ ANALYTICS_ENDPOINT: typing.Final[str] = "analytics/flags/"
|
|
|
18
17
|
# Used to control how often we send data(in seconds)
|
|
19
18
|
ANALYTICS_TIMER: typing.Final[int] = 10
|
|
20
19
|
|
|
20
|
+
FLAG_EXPOSURE_EVENT: typing.Final[str] = "$flag_exposure"
|
|
21
|
+
|
|
22
|
+
DEFAULT_EVENT_API_URL: typing.Final[str] = "https://events.api.flagsmith.com/"
|
|
23
|
+
|
|
21
24
|
session = FuturesSession(max_workers=4)
|
|
22
25
|
|
|
23
26
|
|
|
@@ -72,92 +75,90 @@ class AnalyticsProcessor:
|
|
|
72
75
|
|
|
73
76
|
|
|
74
77
|
@dataclass
|
|
75
|
-
class
|
|
76
|
-
|
|
78
|
+
class EventProcessorConfig:
|
|
79
|
+
events_api_url: str = DEFAULT_EVENT_API_URL
|
|
77
80
|
max_buffer_items: int = 1000
|
|
78
81
|
flush_interval_seconds: float = 10.0
|
|
79
82
|
|
|
80
83
|
|
|
81
|
-
class
|
|
84
|
+
class EventProcessor:
|
|
82
85
|
"""
|
|
83
|
-
Buffered
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
Evaluation events are deduplicated within each flush window. Events are
|
|
87
|
-
flushed periodically via a background timer or when the buffer is full.
|
|
86
|
+
Buffered event processor that batches custom events and POSTs them to the
|
|
87
|
+
Flagsmith event endpoint. Flushes on a background timer or when the buffer
|
|
88
|
+
fills.
|
|
88
89
|
"""
|
|
89
90
|
|
|
90
91
|
def __init__(
|
|
91
92
|
self,
|
|
92
|
-
config:
|
|
93
|
+
config: EventProcessorConfig,
|
|
93
94
|
environment_key: str,
|
|
94
95
|
) -> None:
|
|
95
|
-
url = config.
|
|
96
|
+
url = config.events_api_url
|
|
96
97
|
if not url.endswith("/"):
|
|
97
98
|
url = f"{url}/"
|
|
98
|
-
self._batch_endpoint = f"{url}v1/
|
|
99
|
+
self._batch_endpoint = f"{url}v1/events"
|
|
99
100
|
self._environment_key = environment_key
|
|
100
101
|
self._max_buffer = config.max_buffer_items
|
|
101
102
|
self._flush_interval_seconds = config.flush_interval_seconds
|
|
102
103
|
|
|
103
104
|
self._buffer: typing.List[typing.Dict[str, typing.Any]] = []
|
|
104
|
-
self._dedup_keys: typing.Dict[str, str] = {}
|
|
105
105
|
self._lock = threading.Lock()
|
|
106
106
|
self._timer: typing.Optional[threading.Timer] = None
|
|
107
107
|
|
|
108
|
-
def
|
|
108
|
+
def track_event(
|
|
109
109
|
self,
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
value: typing.
|
|
113
|
-
identity_identifier: typing.Optional[str] = None,
|
|
110
|
+
event: str,
|
|
111
|
+
identifier: typing.Optional[str] = None,
|
|
112
|
+
value: typing.Optional[typing.Union[str, int, float, bool]] = None,
|
|
114
113
|
traits: typing.Optional[typing.Dict[str, typing.Any]] = None,
|
|
114
|
+
metadata: typing.Optional[typing.Dict[str, typing.Any]] = None,
|
|
115
115
|
) -> None:
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
{
|
|
125
|
-
"event_id": flag_key,
|
|
126
|
-
"event_type": "flag_evaluation",
|
|
127
|
-
"evaluated_at": int(time.time() * 1000),
|
|
128
|
-
"identity_identifier": identity_identifier,
|
|
129
|
-
"enabled": enabled,
|
|
130
|
-
"value": value,
|
|
131
|
-
"traits": dict(traits) if traits else None,
|
|
132
|
-
"metadata": {"sdk_version": __version__},
|
|
133
|
-
}
|
|
134
|
-
)
|
|
135
|
-
if len(self._buffer) >= self._max_buffer:
|
|
136
|
-
should_flush = True
|
|
137
|
-
|
|
138
|
-
if should_flush:
|
|
139
|
-
self.flush()
|
|
116
|
+
self._buffer_event(
|
|
117
|
+
event=event,
|
|
118
|
+
feature_name=None,
|
|
119
|
+
identifier=identifier,
|
|
120
|
+
value=value,
|
|
121
|
+
traits=traits,
|
|
122
|
+
metadata=metadata,
|
|
123
|
+
)
|
|
140
124
|
|
|
141
|
-
def
|
|
125
|
+
def track_exposure_event(
|
|
142
126
|
self,
|
|
143
|
-
|
|
144
|
-
|
|
127
|
+
feature_name: str,
|
|
128
|
+
identifier: typing.Optional[str] = None,
|
|
129
|
+
value: typing.Optional[typing.Union[str, int, float, bool]] = None,
|
|
145
130
|
traits: typing.Optional[typing.Dict[str, typing.Any]] = None,
|
|
146
131
|
metadata: typing.Optional[typing.Dict[str, typing.Any]] = None,
|
|
147
132
|
) -> None:
|
|
148
|
-
|
|
133
|
+
self._buffer_event(
|
|
134
|
+
event=FLAG_EXPOSURE_EVENT,
|
|
135
|
+
feature_name=feature_name,
|
|
136
|
+
identifier=identifier,
|
|
137
|
+
value=value,
|
|
138
|
+
traits=traits,
|
|
139
|
+
metadata=metadata,
|
|
140
|
+
)
|
|
149
141
|
|
|
142
|
+
def _buffer_event(
|
|
143
|
+
self,
|
|
144
|
+
event: str,
|
|
145
|
+
feature_name: typing.Optional[str],
|
|
146
|
+
identifier: typing.Optional[str],
|
|
147
|
+
value: typing.Optional[typing.Union[str, int, float, bool]],
|
|
148
|
+
traits: typing.Optional[typing.Dict[str, typing.Any]],
|
|
149
|
+
metadata: typing.Optional[typing.Dict[str, typing.Any]],
|
|
150
|
+
) -> None:
|
|
151
|
+
should_flush = False
|
|
150
152
|
with self._lock:
|
|
151
153
|
self._buffer.append(
|
|
152
154
|
{
|
|
153
|
-
"
|
|
154
|
-
"
|
|
155
|
-
"
|
|
156
|
-
"
|
|
157
|
-
"enabled": None,
|
|
158
|
-
"value": None,
|
|
155
|
+
"event": event,
|
|
156
|
+
"feature_name": feature_name,
|
|
157
|
+
"identifier": identifier,
|
|
158
|
+
"value": str(value) if value is not None else None,
|
|
159
159
|
"traits": dict(traits) if traits else None,
|
|
160
160
|
"metadata": {**(metadata or {}), "sdk_version": __version__},
|
|
161
|
+
"timestamp": int(datetime.now().timestamp() * 1000),
|
|
161
162
|
}
|
|
162
163
|
)
|
|
163
164
|
if len(self._buffer) >= self._max_buffer:
|
|
@@ -172,11 +173,8 @@ class PipelineAnalyticsProcessor:
|
|
|
172
173
|
return
|
|
173
174
|
events = self._buffer
|
|
174
175
|
self._buffer = []
|
|
175
|
-
self._dedup_keys.clear()
|
|
176
176
|
|
|
177
|
-
payload = json.dumps(
|
|
178
|
-
{"events": events, "environment_key": self._environment_key}
|
|
179
|
-
)
|
|
177
|
+
payload = json.dumps({"events": events})
|
|
180
178
|
try:
|
|
181
179
|
future = session.post(
|
|
182
180
|
self._batch_endpoint,
|
|
@@ -9,9 +9,10 @@ from requests.adapters import HTTPAdapter
|
|
|
9
9
|
from urllib3 import Retry
|
|
10
10
|
|
|
11
11
|
from flagsmith.analytics import (
|
|
12
|
+
FLAG_EXPOSURE_EVENT,
|
|
12
13
|
AnalyticsProcessor,
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
EventProcessor,
|
|
15
|
+
EventProcessorConfig,
|
|
15
16
|
)
|
|
16
17
|
from flagsmith.exceptions import FlagsmithAPIError, FlagsmithClientError
|
|
17
18
|
from flagsmith.mappers import (
|
|
@@ -23,6 +24,7 @@ from flagsmith.mappers import (
|
|
|
23
24
|
)
|
|
24
25
|
from flagsmith.models import (
|
|
25
26
|
DefaultFlag,
|
|
27
|
+
Flag,
|
|
26
28
|
Flags,
|
|
27
29
|
Segment,
|
|
28
30
|
SegmentOverridesIndex,
|
|
@@ -74,7 +76,8 @@ class Flagsmith:
|
|
|
74
76
|
environment_refresh_interval_seconds: typing.Union[int, float] = 60,
|
|
75
77
|
retries: typing.Optional[Retry] = None,
|
|
76
78
|
enable_analytics: bool = False,
|
|
77
|
-
|
|
79
|
+
enable_events: bool = False,
|
|
80
|
+
event_processor_config: typing.Optional[EventProcessorConfig] = None,
|
|
78
81
|
default_flag_handler: typing.Optional[
|
|
79
82
|
typing.Callable[[str], DefaultFlag]
|
|
80
83
|
] = None,
|
|
@@ -100,6 +103,12 @@ class Flagsmith:
|
|
|
100
103
|
Flagsmith API
|
|
101
104
|
:param enable_analytics: if enabled, sends additional requests to the Flagsmith
|
|
102
105
|
API to power flag analytics charts
|
|
106
|
+
:param enable_events: if enabled, starts an event processor that buffers
|
|
107
|
+
and sends events (custom and flag-exposure) to the Flagsmith events
|
|
108
|
+
API, powering experimentation analytics.
|
|
109
|
+
:param event_processor_config: optional configuration for the event
|
|
110
|
+
processor (URL override for self-hosted, buffer/flush tuning). Only
|
|
111
|
+
valid when ``enable_events=True``.
|
|
103
112
|
:param default_flag_handler: callable which will be used in the case where
|
|
104
113
|
flags cannot be retrieved from the API or a non-existent feature is
|
|
105
114
|
requested
|
|
@@ -120,9 +129,7 @@ class Flagsmith:
|
|
|
120
129
|
self.default_flag_handler = default_flag_handler
|
|
121
130
|
self.enable_realtime_updates = enable_realtime_updates
|
|
122
131
|
self._analytics_processor: typing.Optional[AnalyticsProcessor] = None
|
|
123
|
-
self.
|
|
124
|
-
PipelineAnalyticsProcessor
|
|
125
|
-
] = None
|
|
132
|
+
self._event_processor: typing.Optional[EventProcessor] = None
|
|
126
133
|
self.__evaluation_context: typing.Optional[SDKEvaluationContext] = None
|
|
127
134
|
self._segment_overrides_index: SegmentOverridesIndex = {}
|
|
128
135
|
self._environment_updated_at: typing.Optional[datetime] = None
|
|
@@ -140,6 +147,11 @@ class Flagsmith:
|
|
|
140
147
|
"Can only use realtime updates when running in local evaluation mode."
|
|
141
148
|
)
|
|
142
149
|
|
|
150
|
+
if event_processor_config is not None and not enable_events:
|
|
151
|
+
raise ValueError(
|
|
152
|
+
"event_processor_config can only be set when enable_events=True."
|
|
153
|
+
)
|
|
154
|
+
|
|
143
155
|
if self.offline_handler:
|
|
144
156
|
self._evaluation_context = map_environment_document_to_context(
|
|
145
157
|
self.offline_handler.get_environment()
|
|
@@ -189,25 +201,35 @@ class Flagsmith:
|
|
|
189
201
|
self._initialise_analytics(
|
|
190
202
|
environment_key=environment_key,
|
|
191
203
|
enable_analytics=enable_analytics,
|
|
192
|
-
|
|
204
|
+
)
|
|
205
|
+
self._initialise_events(
|
|
206
|
+
environment_key=environment_key,
|
|
207
|
+
enable_events=enable_events,
|
|
208
|
+
event_processor_config=event_processor_config,
|
|
193
209
|
)
|
|
194
210
|
|
|
195
211
|
def _initialise_analytics(
|
|
196
212
|
self,
|
|
197
213
|
environment_key: str,
|
|
198
214
|
enable_analytics: bool,
|
|
199
|
-
pipeline_analytics_config: typing.Optional[PipelineAnalyticsConfig],
|
|
200
215
|
) -> None:
|
|
201
216
|
if enable_analytics:
|
|
202
217
|
self._analytics_processor = AnalyticsProcessor(
|
|
203
218
|
environment_key, self.api_url, timeout=self.request_timeout_seconds
|
|
204
219
|
)
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
220
|
+
|
|
221
|
+
def _initialise_events(
|
|
222
|
+
self,
|
|
223
|
+
environment_key: str,
|
|
224
|
+
enable_events: bool,
|
|
225
|
+
event_processor_config: typing.Optional[EventProcessorConfig],
|
|
226
|
+
) -> None:
|
|
227
|
+
if enable_events:
|
|
228
|
+
self._event_processor = EventProcessor(
|
|
229
|
+
config=event_processor_config or EventProcessorConfig(),
|
|
208
230
|
environment_key=environment_key,
|
|
209
231
|
)
|
|
210
|
-
self.
|
|
232
|
+
self._event_processor.start()
|
|
211
233
|
|
|
212
234
|
def _initialise_local_evaluation(self) -> None:
|
|
213
235
|
# To ensure that the environment is set before allowing subsequent
|
|
@@ -324,21 +346,68 @@ class Flagsmith:
|
|
|
324
346
|
|
|
325
347
|
return map_segment_results_to_identity_segments(evaluation_result["segments"])
|
|
326
348
|
|
|
349
|
+
def get_experiment_flag(
|
|
350
|
+
self,
|
|
351
|
+
feature_name: str,
|
|
352
|
+
identifier: str,
|
|
353
|
+
traits: typing.Optional[TraitMapping] = None,
|
|
354
|
+
) -> typing.Union[DefaultFlag, Flag]:
|
|
355
|
+
"""
|
|
356
|
+
Resolve a flag for an identity and record an exposure event.
|
|
357
|
+
|
|
358
|
+
Skips the exposure event when the resolved flag is a `DefaultFlag`
|
|
359
|
+
(i.e. the feature was not present and was served via the
|
|
360
|
+
`default_flag_handler`), to keep experimentation data clean.
|
|
361
|
+
"""
|
|
362
|
+
if not self._event_processor:
|
|
363
|
+
raise ValueError("Events must be enabled to use experiment flags.")
|
|
364
|
+
flag = self.get_identity_flags(identifier, traits).get_flag(feature_name)
|
|
365
|
+
if not flag.is_default:
|
|
366
|
+
self.track_exposure_event(
|
|
367
|
+
feature_name=feature_name,
|
|
368
|
+
identifier=identifier,
|
|
369
|
+
value=flag.value,
|
|
370
|
+
traits=traits,
|
|
371
|
+
)
|
|
372
|
+
return flag
|
|
373
|
+
|
|
327
374
|
def track_event(
|
|
328
375
|
self,
|
|
329
|
-
|
|
330
|
-
|
|
376
|
+
event: str,
|
|
377
|
+
identifier: typing.Optional[str] = None,
|
|
378
|
+
value: typing.Optional[typing.Union[str, int, float, bool]] = None,
|
|
331
379
|
traits: typing.Optional[TraitMapping] = None,
|
|
332
380
|
metadata: typing.Optional[typing.Dict[str, typing.Any]] = None,
|
|
333
381
|
) -> None:
|
|
334
|
-
if not self.
|
|
382
|
+
if not self._event_processor:
|
|
383
|
+
raise ValueError("Events must be enabled to track events.")
|
|
384
|
+
if event.startswith("$") and event != FLAG_EXPOSURE_EVENT:
|
|
335
385
|
raise ValueError(
|
|
336
|
-
"
|
|
337
|
-
"
|
|
386
|
+
f'Event name "{event}" uses the reserved "$" prefix '
|
|
387
|
+
"but is not a known system event."
|
|
338
388
|
)
|
|
339
|
-
self.
|
|
340
|
-
|
|
341
|
-
|
|
389
|
+
self._event_processor.track_event(
|
|
390
|
+
event=event,
|
|
391
|
+
identifier=identifier,
|
|
392
|
+
value=value,
|
|
393
|
+
traits=resolve_trait_values(traits),
|
|
394
|
+
metadata=metadata,
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
def track_exposure_event(
|
|
398
|
+
self,
|
|
399
|
+
feature_name: str,
|
|
400
|
+
identifier: typing.Optional[str] = None,
|
|
401
|
+
value: typing.Optional[typing.Union[str, int, float, bool]] = None,
|
|
402
|
+
traits: typing.Optional[TraitMapping] = None,
|
|
403
|
+
metadata: typing.Optional[typing.Dict[str, typing.Any]] = None,
|
|
404
|
+
) -> None:
|
|
405
|
+
if not self._event_processor:
|
|
406
|
+
raise ValueError("Events must be enabled to track exposure events.")
|
|
407
|
+
self._event_processor.track_exposure_event(
|
|
408
|
+
feature_name=feature_name,
|
|
409
|
+
identifier=identifier,
|
|
410
|
+
value=value,
|
|
342
411
|
traits=resolve_trait_values(traits),
|
|
343
412
|
metadata=metadata,
|
|
344
413
|
)
|
|
@@ -418,7 +487,6 @@ class Flagsmith:
|
|
|
418
487
|
evaluation_result=evaluation_result,
|
|
419
488
|
analytics_processor=self._analytics_processor,
|
|
420
489
|
default_flag_handler=self.default_flag_handler,
|
|
421
|
-
pipeline_analytics_processor=self._pipeline_analytics_processor,
|
|
422
490
|
)
|
|
423
491
|
|
|
424
492
|
def _get_identity_flags_from_document(
|
|
@@ -442,9 +510,6 @@ class Flagsmith:
|
|
|
442
510
|
overrides_index=self._segment_overrides_index,
|
|
443
511
|
analytics_processor=self._analytics_processor,
|
|
444
512
|
default_flag_handler=self.default_flag_handler,
|
|
445
|
-
pipeline_analytics_processor=self._pipeline_analytics_processor,
|
|
446
|
-
identity_identifier=identifier,
|
|
447
|
-
traits=resolve_trait_values(traits),
|
|
448
513
|
)
|
|
449
514
|
|
|
450
515
|
def _get_environment_flags_from_api(self) -> Flags:
|
|
@@ -456,7 +521,6 @@ class Flagsmith:
|
|
|
456
521
|
api_flags=json_response,
|
|
457
522
|
analytics_processor=self._analytics_processor,
|
|
458
523
|
default_flag_handler=self.default_flag_handler,
|
|
459
|
-
pipeline_analytics_processor=self._pipeline_analytics_processor,
|
|
460
524
|
)
|
|
461
525
|
except FlagsmithAPIError:
|
|
462
526
|
if self.offline_handler:
|
|
@@ -489,9 +553,6 @@ class Flagsmith:
|
|
|
489
553
|
api_flags=json_response["flags"],
|
|
490
554
|
analytics_processor=self._analytics_processor,
|
|
491
555
|
default_flag_handler=self.default_flag_handler,
|
|
492
|
-
pipeline_analytics_processor=self._pipeline_analytics_processor,
|
|
493
|
-
identity_identifier=identifier,
|
|
494
|
-
traits=resolve_trait_values(traits),
|
|
495
556
|
)
|
|
496
557
|
except FlagsmithAPIError:
|
|
497
558
|
if self.offline_handler:
|
|
@@ -525,5 +586,5 @@ class Flagsmith:
|
|
|
525
586
|
if hasattr(self, "event_stream_thread"):
|
|
526
587
|
self.event_stream_thread.stop()
|
|
527
588
|
|
|
528
|
-
if self.
|
|
529
|
-
self.
|
|
589
|
+
if self._event_processor:
|
|
590
|
+
self._event_processor.stop()
|
|
@@ -6,7 +6,7 @@ from dataclasses import dataclass, field
|
|
|
6
6
|
from flag_engine import engine
|
|
7
7
|
from flag_engine.context.types import SegmentContext
|
|
8
8
|
|
|
9
|
-
from flagsmith.analytics import AnalyticsProcessor
|
|
9
|
+
from flagsmith.analytics import AnalyticsProcessor
|
|
10
10
|
from flagsmith.exceptions import FlagsmithFeatureDoesNotExistError
|
|
11
11
|
from flagsmith.types import (
|
|
12
12
|
FeatureMetadata,
|
|
@@ -85,9 +85,6 @@ class Flags:
|
|
|
85
85
|
flags: typing.Dict[str, Flag] = field(default_factory=dict)
|
|
86
86
|
default_flag_handler: typing.Optional[typing.Callable[[str], DefaultFlag]] = None
|
|
87
87
|
_analytics_processor: typing.Optional[AnalyticsProcessor] = None
|
|
88
|
-
_pipeline_analytics_processor: typing.Optional[PipelineAnalyticsProcessor] = None
|
|
89
|
-
_identity_identifier: typing.Optional[str] = None
|
|
90
|
-
_traits: typing.Optional[typing.Dict[str, typing.Any]] = None
|
|
91
88
|
# Lazy-evaluation state. When `_context` is set, `flags` is a
|
|
92
89
|
# per-feature memo rather than a fully-materialised snapshot; unseen
|
|
93
90
|
# features are resolved on demand via the engine primitives and
|
|
@@ -103,11 +100,6 @@ class Flags:
|
|
|
103
100
|
evaluation_result: SDKEvaluationResult,
|
|
104
101
|
analytics_processor: typing.Optional[AnalyticsProcessor],
|
|
105
102
|
default_flag_handler: typing.Optional[typing.Callable[[str], DefaultFlag]],
|
|
106
|
-
pipeline_analytics_processor: typing.Optional[
|
|
107
|
-
PipelineAnalyticsProcessor
|
|
108
|
-
] = None,
|
|
109
|
-
identity_identifier: typing.Optional[str] = None,
|
|
110
|
-
traits: typing.Optional[typing.Dict[str, typing.Any]] = None,
|
|
111
103
|
) -> Flags:
|
|
112
104
|
return cls(
|
|
113
105
|
flags={
|
|
@@ -117,9 +109,6 @@ class Flags:
|
|
|
117
109
|
},
|
|
118
110
|
default_flag_handler=default_flag_handler,
|
|
119
111
|
_analytics_processor=analytics_processor,
|
|
120
|
-
_pipeline_analytics_processor=pipeline_analytics_processor,
|
|
121
|
-
_identity_identifier=identity_identifier,
|
|
122
|
-
_traits=traits,
|
|
123
112
|
)
|
|
124
113
|
|
|
125
114
|
@classmethod
|
|
@@ -129,11 +118,6 @@ class Flags:
|
|
|
129
118
|
overrides_index: SegmentOverridesIndex,
|
|
130
119
|
analytics_processor: typing.Optional[AnalyticsProcessor],
|
|
131
120
|
default_flag_handler: typing.Optional[typing.Callable[[str], DefaultFlag]],
|
|
132
|
-
pipeline_analytics_processor: typing.Optional[
|
|
133
|
-
PipelineAnalyticsProcessor
|
|
134
|
-
] = None,
|
|
135
|
-
identity_identifier: typing.Optional[str] = None,
|
|
136
|
-
traits: typing.Optional[typing.Dict[str, typing.Any]] = None,
|
|
137
121
|
) -> Flags:
|
|
138
122
|
"""Build a lazy `Flags` backed by an evaluation context.
|
|
139
123
|
|
|
@@ -146,9 +130,6 @@ class Flags:
|
|
|
146
130
|
flags={},
|
|
147
131
|
default_flag_handler=default_flag_handler,
|
|
148
132
|
_analytics_processor=analytics_processor,
|
|
149
|
-
_pipeline_analytics_processor=pipeline_analytics_processor,
|
|
150
|
-
_identity_identifier=identity_identifier,
|
|
151
|
-
_traits=traits,
|
|
152
133
|
_context=context,
|
|
153
134
|
_overrides_index=overrides_index,
|
|
154
135
|
)
|
|
@@ -159,11 +140,6 @@ class Flags:
|
|
|
159
140
|
api_flags: typing.Sequence[typing.Mapping[str, typing.Any]],
|
|
160
141
|
analytics_processor: typing.Optional[AnalyticsProcessor],
|
|
161
142
|
default_flag_handler: typing.Optional[typing.Callable[[str], DefaultFlag]],
|
|
162
|
-
pipeline_analytics_processor: typing.Optional[
|
|
163
|
-
PipelineAnalyticsProcessor
|
|
164
|
-
] = None,
|
|
165
|
-
identity_identifier: typing.Optional[str] = None,
|
|
166
|
-
traits: typing.Optional[typing.Dict[str, typing.Any]] = None,
|
|
167
143
|
) -> Flags:
|
|
168
144
|
flags = {
|
|
169
145
|
flag_data["feature"]["name"]: Flag.from_api_flag(flag_data)
|
|
@@ -174,9 +150,6 @@ class Flags:
|
|
|
174
150
|
flags=flags,
|
|
175
151
|
default_flag_handler=default_flag_handler,
|
|
176
152
|
_analytics_processor=analytics_processor,
|
|
177
|
-
_pipeline_analytics_processor=pipeline_analytics_processor,
|
|
178
|
-
_identity_identifier=identity_identifier,
|
|
179
|
-
_traits=traits,
|
|
180
153
|
)
|
|
181
154
|
|
|
182
155
|
def all_flags(self) -> typing.List[Flag]:
|
|
@@ -252,15 +225,6 @@ class Flags:
|
|
|
252
225
|
if self._analytics_processor and hasattr(flag, "feature_name"):
|
|
253
226
|
self._analytics_processor.track_feature(flag.feature_name)
|
|
254
227
|
|
|
255
|
-
if self._pipeline_analytics_processor and hasattr(flag, "feature_name"):
|
|
256
|
-
self._pipeline_analytics_processor.record_evaluation_event(
|
|
257
|
-
flag_key=flag.feature_name,
|
|
258
|
-
enabled=flag.enabled,
|
|
259
|
-
value=flag.value,
|
|
260
|
-
identity_identifier=self._identity_identifier,
|
|
261
|
-
traits=self._traits,
|
|
262
|
-
)
|
|
263
|
-
|
|
264
228
|
return flag
|
|
265
229
|
|
|
266
230
|
def _resolve_flag(self, feature_name: str) -> Flag:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|