Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,26 @@ The SDK for Python applications for [https://www.flagsmith.com/](https://www.fla
For full documentation visit
[https://docs.flagsmith.com/clients/server-side?language=python](https://docs.flagsmith.com/clients/server-side?language=python).

## Checking client readiness

`flagsmith.ready()` returns whether the client has an environment document available for local or offline evaluation. It
makes no network requests and does not block. Remote-evaluation clients return `True` immediately; readiness does not
indicate API connectivity or document freshness.

If the initial local-evaluation fetch fails and no document is available, clients with realtime updates disabled are not
ready until the background poller loads a document. With realtime updates enabled, construction raises `ValueError`
instead. A later refresh failure keeps the last successful document available, so readiness remains `True`.

Check readiness before evaluating flags to avoid the existing remote API fallback while the first local document is
unavailable:

```python
if flagsmith.ready():
enabled = flagsmith.get_environment_flags().is_feature_enabled("feature")
else:
enabled = False # Application-defined fallback while waiting for a document.
```

## Contributing

Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull
Expand Down
12 changes: 12 additions & 0 deletions flagsmith/flagsmith.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,18 @@ def handle_stream_event(self, event: StreamEvent) -> None:
if event["updated_at"] > environment_updated_at:
self.update_environment()

def ready(self) -> bool:
"""Return whether the client can evaluate in its configured mode.

Local and offline evaluation require an environment document. Remote
evaluation is immediately ready; this does not check API connectivity.
This method does not make requests or wait for a document to load.
"""
return (
not (self.enable_local_evaluation or self.offline_mode)
or self._local_evaluation is not None
)

def get_environment_flags(self) -> Flags:
"""
Get all the default for flags for the current environment.
Expand Down
35 changes: 35 additions & 0 deletions tests/test_flagsmith.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,41 @@
from flagsmith.types import SDKEvaluationContext


@responses.activate()
def test_ready_tracks_local_document_availability(
mocker: MockerFixture, environment_json: str
) -> None:
mocker.patch("flagsmith.flagsmith.EnvironmentDataPollingManager")
url = "https://edge.api.flagsmith.com/api/v1/environment-document/"
responses.get(url, status=500)
responses.get(url, body=environment_json)
responses.get(url, status=500)
client = Flagsmith(environment_key="ser.test", enable_local_evaluation=True)

assert client.ready() is False
assert len(responses.calls) == 1
client.update_environment()
assert client.ready() is True
client.update_environment()
assert client.ready() is True
assert len(responses.calls) == 3


def test_ready_remote_evaluation(flagsmith: Flagsmith) -> None:
assert flagsmith.ready() is True


def test_ready_offline_document(
mocker: MockerFixture, environment: EnvironmentModel
) -> None:
handler = mocker.Mock(spec=OfflineHandler)
handler.get_environment.return_value = environment
client = Flagsmith(offline_mode=True, offline_handler=handler)

assert client.ready() is True
assert client.get_environment_flags().is_feature_enabled("some_feature")


def test_flagsmith_starts_polling_manager_on_init_if_enabled(
mocker: MockerFixture, server_api_key: str, requests_session_response_ok: None
) -> None:
Expand Down