Skip to content

Commit 0bfb7a2

Browse files
authored
FF-3392 feat(python): add Configuration.get_bandits_configuration (#50)
* FF-3392 feat(python): add Configuration.get_bandits_configuration * chore(python): add `--no-index` do disable installing python-sdk from pypi For CI, we should always install our SDK from dist. Without `--no-index`, I found that pip randomly decides between local build and pypi (unless we bumped the version locally), so CI would randomly fail. * chore(python): bump version
1 parent 4100723 commit 0bfb7a2

File tree

5 files changed

+42
-8
lines changed

5 files changed

+42
-8
lines changed

.github/workflows/python.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ jobs:
8282
set -ex
8383
python3 -m venv .venv
8484
source .venv/bin/activate
85-
pip install eppo-server-sdk --find-links dist --force-reinstall
85+
pip install eppo-server-sdk --no-index --find-links dist --force-reinstall
8686
pip install pytest cachetools
8787
npm ci
8888
npm run with-server test:python
@@ -99,7 +99,7 @@ jobs:
9999
pip3 install -U pip pytest cachetools
100100
run: |
101101
set -ex
102-
pip3 install eppo-server-sdk --find-links dist --force-reinstall
102+
pip3 install eppo-server-sdk --no-index --find-links dist --force-reinstall
103103
npm ci
104104
npm run with-server test:python
105105
@@ -167,7 +167,7 @@ jobs:
167167
python3 -m virtualenv .venv
168168
source .venv/bin/activate
169169
pip install pytest cachetools
170-
pip install eppo-server-sdk --find-links dist --force-reinstall
170+
pip install eppo-server-sdk --no-index --find-links dist --force-reinstall
171171
npm ci
172172
npm run with-server test:python
173173
@@ -206,7 +206,7 @@ jobs:
206206
set -ex
207207
python3 -m venv .venv
208208
source .venv/Scripts/activate
209-
pip install eppo-server-sdk --find-links dist --force-reinstall
209+
pip install eppo-server-sdk --no-index --find-links dist --force-reinstall
210210
pip install pytest cachetools
211211
npm ci
212212
npm run with-server test:python
@@ -243,7 +243,7 @@ jobs:
243243
set -ex
244244
python3 -m venv .venv
245245
source .venv/bin/activate
246-
pip install eppo-server-sdk --find-links dist --force-reinstall
246+
pip install eppo-server-sdk --no-index --find-links dist --force-reinstall
247247
pip install pytest cachetools
248248
npm ci
249249
npm run with-server test:python

python-sdk/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "eppo_py"
3-
version = "4.0.3"
3+
version = "4.1.0"
44
edition = "2021"
55
publish = false
66

python-sdk/python/eppo_client/_eppo_client.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class Configuration:
1010
self, *, flags_configuration: bytes, bandits_configuration: bytes | None = None
1111
) -> None: ...
1212
def get_flags_configuration(self) -> bytes: ...
13+
def get_bandits_configuration(self) -> bytes | None: ...
1314
def get_flag_keys(self) -> Set[str]: ...
1415
def get_bandit_keys(self) -> Set[str]: ...
1516

python-sdk/src/configuration.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
// backend to pass on configuration when initializing the frontend.
33
use std::{borrow::Cow, sync::Arc};
44

5-
use pyo3::{exceptions::PyValueError, prelude::*, types::PySet};
5+
use pyo3::{
6+
exceptions::{PyRuntimeError, PyValueError},
7+
prelude::*,
8+
types::PySet,
9+
};
610

711
use eppo_core::{ufc::UniversalFlagConfig, Configuration as CoreConfiguration};
812

@@ -68,6 +72,23 @@ impl Configuration {
6872
fn get_flags_configuration(&self) -> Cow<[u8]> {
6973
Cow::Borrowed(self.configuration.flags.to_json())
7074
}
75+
76+
/// Return bytes representing bandits configuration.
77+
///
78+
/// It should be treated as opaque and passed on to another Eppo client for initialization.
79+
fn get_bandits_configuration(&self) -> PyResult<Option<Cow<[u8]>>> {
80+
self.configuration
81+
.bandits
82+
.as_ref()
83+
.map(|it| serde_json::to_vec(it).map(Cow::Owned))
84+
.transpose()
85+
.map_err(|err| {
86+
// This should normally never happen.
87+
PyRuntimeError::new_err(format!(
88+
"failed to serialize bandits configuration: {err:?}"
89+
))
90+
})
91+
}
7192
}
7293

7394
impl Configuration {

python-sdk/tests/test_configuration.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@
4848
BANDITS_MODEL_CONFIG = json.dumps(
4949
{
5050
"updatedAt": "2023-09-13T04:52:06.462Z",
51-
"environment": {"name": "Test"},
5251
"bandits": {
5352
"car_bandit": {
5453
"banditKey": "car_bandit",
@@ -116,6 +115,19 @@ def test_init_invalid_format(self):
116115
flags_configuration=b'{"createdAt":"2024-09-09T10:18:15.988Z","environment":{"name":"test"},"flags":[]}'
117116
)
118117

118+
def test_get_bandits_configuration(self):
119+
configuration = Configuration(
120+
flags_configuration=FLAGS_CONFIG_WITH_BANDITS,
121+
bandits_configuration=BANDITS_MODEL_CONFIG,
122+
)
123+
124+
bandits_configuration = configuration.get_bandits_configuration()
125+
126+
# JSON parsing is an internal detail and is not a public
127+
# guarantee. We're using it here because serialization order
128+
# of bandits is not guaranteed.
129+
assert json.loads(bandits_configuration) == json.loads(BANDITS_MODEL_CONFIG)
130+
119131

120132
@pytest.mark.rust_only
121133
def test_configuration_none():

0 commit comments

Comments
 (0)