Skip to content

Commit 3a9e0c4

Browse files
committed
refactor: Drop lazy_identity_evaluation rollback kwarg
Lazy is now the only path through ``_get_identity_flags_from_document``. Per-flag resolution still goes via the engine's public ``get_evaluation_result`` (against a trimmed context), so callers get the perf win without the eager fallback or the kwarg surface. Tests pinned to the old eager path are reworked to mock ``flagsmith.models.engine.get_evaluation_result`` instead — that's where the call lands now — and trigger evaluation via ``.all_flags()`` when they need the bulk-context call shape. beep boop
1 parent 5a7d7dc commit 3a9e0c4

2 files changed

Lines changed: 26 additions & 67 deletions

File tree

flagsmith/flagsmith.py

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ def __init__(
8383
offline_handler: typing.Optional[OfflineHandler] = None,
8484
enable_realtime_updates: bool = False,
8585
application_metadata: typing.Optional[ApplicationMetadata] = None,
86-
lazy_identity_evaluation: bool = True,
8786
):
8887
"""
8988
:param environment_key: The environment key obtained from Flagsmith interface.
@@ -112,11 +111,6 @@ def __init__(
112111
default_flag_handler if offline_mode is not set and using remote evaluation.
113112
:param enable_realtime_updates: Use real-time functionality via SSE as opposed to polling the API
114113
:param application_metadata: Optional metadata about the client application.
115-
:param lazy_identity_evaluation: When True (default), ``get_identity_flags``
116-
returns a lazy ``Flags`` that resolves flags on first access using a
117-
precomputed segment-overrides index, rather than evaluating every
118-
feature in the environment up-front. Set to False to opt back into
119-
the legacy eager path if you hit a regression.
120114
"""
121115

122116
self.offline_mode = offline_mode
@@ -125,7 +119,6 @@ def __init__(
125119
self.offline_handler = offline_handler
126120
self.default_flag_handler = default_flag_handler
127121
self.enable_realtime_updates = enable_realtime_updates
128-
self.lazy_identity_evaluation = lazy_identity_evaluation
129122
self._analytics_processor: typing.Optional[AnalyticsProcessor] = None
130123
self._pipeline_analytics_processor: typing.Optional[
131124
PipelineAnalyticsProcessor
@@ -441,25 +434,12 @@ def _get_identity_flags_from_document(
441434
identifier=identifier,
442435
traits=traits,
443436
)
444-
if self.lazy_identity_evaluation:
445-
# Lazy path: defer per-feature evaluation until the caller
446-
# actually reads a flag. Hot for callers that only read one
447-
# or a few flags out of a large environment.
448-
return Flags.from_evaluation_context(
449-
context=context,
450-
overrides_index=self._segment_overrides_index,
451-
analytics_processor=self._analytics_processor,
452-
default_flag_handler=self.default_flag_handler,
453-
pipeline_analytics_processor=self._pipeline_analytics_processor,
454-
identity_identifier=identifier,
455-
traits=resolve_trait_values(traits),
456-
)
457-
evaluation_result = engine.get_evaluation_result(
437+
# Lazy: defer per-feature evaluation until the caller actually reads
438+
# a flag. Hot for callers that only read one or a few flags out of a
439+
# large environment.
440+
return Flags.from_evaluation_context(
458441
context=context,
459-
)
460-
461-
return Flags.from_evaluation_result(
462-
evaluation_result=evaluation_result,
442+
overrides_index=self._segment_overrides_index,
463443
analytics_processor=self._analytics_processor,
464444
default_flag_handler=self.default_flag_handler,
465445
pipeline_analytics_processor=self._pipeline_analytics_processor,

tests/test_flagsmith.py

Lines changed: 21 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -191,12 +191,15 @@ def test_get_identity_flags_uses_local_environment_when_available(
191191
evaluation_context: SDKEvaluationContext,
192192
mocker: MockerFixture,
193193
) -> None:
194-
# Given: the eager rollback path; this test pins the engine call
195-
# shape, so bypass the lazy default to exercise it.
194+
# Given
196195
flagsmith._evaluation_context = evaluation_context
197196
flagsmith.enable_local_evaluation = True
198-
flagsmith.lazy_identity_evaluation = False
199-
mock_engine = mocker.patch("flagsmith.flagsmith.engine")
197+
# ``Flags`` materialises identity flags via ``engine.get_evaluation_result``
198+
# imported from ``flagsmith.models``, so patch it where it's actually used.
199+
mock_get_evaluation_result = mocker.patch(
200+
"flagsmith.models.engine.get_evaluation_result",
201+
autospec=True,
202+
)
200203

201204
expected_evaluation_result = {
202205
"flags": {
@@ -213,15 +216,15 @@ def test_get_identity_flags_uses_local_environment_when_available(
213216
identifier = "identifier"
214217
traits = {"some_trait": "some_value"}
215218

216-
mock_engine.get_evaluation_result.return_value = expected_evaluation_result
219+
mock_get_evaluation_result.return_value = expected_evaluation_result
217220

218221
# When
219222
identity_flags = flagsmith.get_identity_flags(identifier, traits).all_flags()
220223

221224
# Then
222-
mock_engine.get_evaluation_result.assert_called_once()
223-
call_args = mock_engine.get_evaluation_result.call_args
224-
context = call_args[1]["context"]
225+
mock_get_evaluation_result.assert_called_once()
226+
call_args = mock_get_evaluation_result.call_args
227+
context = call_args[0][0] if call_args.args else call_args[1]["context"]
225228
assert context["identity"]["identifier"] == identifier
226229
assert context["identity"]["traits"]["some_trait"] == "some_value"
227230
assert "some_trait" in context["identity"]["traits"]
@@ -234,11 +237,9 @@ def test_get_identity_flags_includes_segments_in_evaluation_context(
234237
mocker: MockerFixture,
235238
local_eval_flagsmith: Flagsmith,
236239
) -> None:
237-
# Given: eager rollback path — this test asserts what goes into the
238-
# engine call directly.
239-
local_eval_flagsmith.lazy_identity_evaluation = False
240+
# Given
240241
mock_get_evaluation_result = mocker.patch(
241-
"flagsmith.flagsmith.engine.get_evaluation_result",
242+
"flagsmith.models.engine.get_evaluation_result",
242243
autospec=True,
243244
)
244245

@@ -259,22 +260,22 @@ def test_get_identity_flags_includes_segments_in_evaluation_context(
259260

260261
mock_get_evaluation_result.return_value = expected_evaluation_result
261262

262-
# When
263-
local_eval_flagsmith.get_identity_flags(identifier, traits)
263+
# When: ``all_flags`` triggers the bulk evaluation path on the lazy
264+
# ``Flags`` object, which is where the full identity context — segments
265+
# included — is passed to the engine.
266+
local_eval_flagsmith.get_identity_flags(identifier, traits).all_flags()
264267

265-
# Then
266-
# Verify segments are present in the context passed to the engine for identity flags
268+
# Then: segments are present in the context passed to the engine for
269+
# identity flags (in contrast to the env-flags path, which strips them).
267270
call_args = mock_get_evaluation_result.call_args
268-
context = call_args[1]["context"]
271+
context = call_args[0][0] if call_args.args else call_args[1]["context"]
269272
assert "segments" in context
270273

271274

272-
def test_get_identity_flags__lazy_by_default__resolves_one_flag_at_a_time(
275+
def test_get_identity_flags__resolves_one_flag_at_a_time(
273276
local_eval_flagsmith: Flagsmith,
274277
mocker: MockerFixture,
275278
) -> None:
276-
# Given: the lazy path is on by default.
277-
assert local_eval_flagsmith.lazy_identity_evaluation is True
278279
spy = mocker.spy(engine, "get_evaluation_result")
279280

280281
# When: we ask for identity flags but never touch a specific flag...
@@ -296,28 +297,6 @@ def test_get_identity_flags__lazy_by_default__resolves_one_flag_at_a_time(
296297
assert set(trimmed_context["features"]) == {"some_feature"}
297298

298299

299-
def test_get_identity_flags__lazy_disabled__falls_back_to_eager_path(
300-
requests_session_response_ok: None,
301-
server_api_key: str,
302-
mocker: MockerFixture,
303-
) -> None:
304-
# Given: lazy evaluation is explicitly turned off (rollback kwarg).
305-
flagsmith = Flagsmith(
306-
environment_key=server_api_key,
307-
enable_local_evaluation=True,
308-
environment_refresh_interval_seconds=0.1,
309-
lazy_identity_evaluation=False,
310-
)
311-
spy = mocker.spy(engine, "get_evaluation_result")
312-
313-
# When
314-
flags = flagsmith.get_identity_flags("someone")
315-
316-
# Then: the bulk evaluation ran up-front and every flag is resolved.
317-
assert spy.call_count == 1
318-
assert set(flags.flags.keys()) == {"some_feature"}
319-
320-
321300
@responses.activate()
322301
def test_get_identity_flags__transient_identity__calls_expected(
323302
flagsmith: Flagsmith,

0 commit comments

Comments
 (0)