Skip to content

Commit 095c5f5

Browse files
bennettandrewsrasendubileoromanovsky
authored
fix(python): Options that are None are also None in python (#212)
* Options that are None are also None in python * Update test to check attributes on event * chore: add changeset * fix(core): add custom Serialize implementation for AttributeValue This should fix `null` attributes serializing to unit instead of none. --------- Co-authored-by: Oleksii Shmalko <[email protected]> Co-authored-by: Oleksii Shmalko <[email protected]> Co-authored-by: Leo Romanovsky <[email protected]>
1 parent 9533b19 commit 095c5f5

File tree

4 files changed

+30
-3
lines changed

4 files changed

+30
-3
lines changed

.changeset/cyan-socks-fold.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"eppo_core": patch
3+
"python-sdk": patch
4+
"ruby-sdk": patch
5+
"rust-sdk": patch
6+
---
7+
8+
Fix `AttributeValue` serialization, so `Null` attributes are properly serialized as None instead of unit value.

eppo_core/src/attributes.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ pub type Attributes = HashMap<Str, AttributeValue>;
3535
#[derive(Debug, Clone, PartialEq, PartialOrd, derive_more::From, Serialize, Deserialize)]
3636
#[from(NumericAttribute, CategoricalAttribute, f64, bool, Str, String, &str, Arc<str>, Arc<String>, Cow<'_, str>)]
3737
pub struct AttributeValue(AttributeValueImpl);
38-
#[derive(Debug, Clone, PartialEq, PartialOrd, derive_more::From, Serialize, Deserialize)]
38+
#[derive(Debug, Clone, PartialEq, PartialOrd, derive_more::From, Deserialize)]
3939
#[serde(untagged)]
4040
enum AttributeValueImpl {
4141
#[from(NumericAttribute, f64)]
@@ -46,6 +46,23 @@ enum AttributeValueImpl {
4646
Null,
4747
}
4848

49+
impl serde::Serialize for AttributeValueImpl {
50+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
51+
where
52+
S: serde::Serializer,
53+
{
54+
match self {
55+
AttributeValueImpl::Numeric(numeric_attribute) => {
56+
numeric_attribute.serialize(serializer)
57+
}
58+
AttributeValueImpl::Categorical(categorical_attribute) => {
59+
categorical_attribute.serialize(serializer)
60+
}
61+
AttributeValueImpl::Null => serializer.serialize_none(),
62+
}
63+
}
64+
}
65+
4966
impl AttributeValue {
5067
/// Create a numeric attribute.
5168
#[inline]

eppo_core/src/pyo3.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ impl<T: TryToPyObject> TryToPyObject for Option<T> {
2525
fn try_to_pyobject(&self, py: Python) -> PyResult<PyObject> {
2626
match self {
2727
Some(it) => it.try_to_pyobject(py),
28-
None => Ok(().to_object(py)),
28+
None => Ok(py.None()),
2929
}
3030
}
3131
}

python-sdk/tests/test_assignment_logger.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,13 @@ def log_assignment(self, assignment_event: Dict):
4141

4242
client = init("ufc", assignment_logger=MyAssignmentLogger())
4343
result = client.get_string_assignment(
44-
"regex-flag", "alice", {"email": "[email protected]"}, "default"
44+
"regex-flag", "alice", {"email": "[email protected]", "empty": None}, "default"
4545
)
4646
assert result == "partial-example"
4747

4848
assert event["allocation"] == "partial-example"
4949
assert event["featureFlag"] == "regex-flag"
5050
assert event["experiment"] == "regex-flag-partial-example"
5151
assert event["metaData"]["sdkName"] == "python"
52+
assert event["subjectAttributes"]["email"] == "[email protected]"
53+
assert event["subjectAttributes"]["empty"] is None

0 commit comments

Comments
 (0)