Skip to content

Commit b08d7f3

Browse files
committed
python(feat): Support specifying units on test results from client library.
1 parent f02cc6f commit b08d7f3

4 files changed

Lines changed: 32 additions & 9 deletions

File tree

python/lib/sift_client/_tests/resources/test_test_results.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ def test_create_test_measurements(self, sift_client):
201201
"string_value": "1.10.3",
202202
"passed": True,
203203
"timestamp": step2.start_time,
204+
"unit": "K",
204205
},
205206
update_step=True,
206207
)
@@ -232,6 +233,7 @@ def test_create_test_measurements(self, sift_client):
232233
assert measurement2.id_ is not None
233234
assert measurement3.id_ is not None
234235
assert measurement4.id_ is not None
236+
assert measurement2.unit == "K"
235237
self.test_measurements["measurement1"] = measurement1
236238
self.test_measurements["measurement2"] = measurement2
237239
self.test_measurements["measurement3"] = measurement3
@@ -248,12 +250,13 @@ def test_update_test_measurements(self, sift_client):
248250
update={
249251
"passed": False,
250252
"string_expected_value": "1.10.4",
253+
"unit": "C",
251254
},
252255
update_step=True,
253256
)
254257
assert measurement2.passed == False
255258
assert measurement2.string_expected_value == "1.10.4"
256-
259+
assert measurement2.unit == "C"
257260
# Update the measurement using class function.
258261
measurement4 = measurement4.update(
259262
{

python/lib/sift_client/sift_types/_base.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ class ModelCreateUpdateBase(BaseModel, ABC):
9494

9595
model_config = ConfigDict(frozen=False)
9696
_to_proto_helpers: ClassVar[dict[str, MappingHelper]] = {}
97+
_field_helpers_called: set[str] = set()
9798

9899
def __init__(self, **data: Any):
99100
super().__init__(**data)
@@ -146,13 +147,19 @@ def _build_proto_and_paths(
146147
if mapping_helper.update_field:
147148
paths.append(mapping_helper.update_field)
148149
elif isinstance(value, dict):
149-
if field_name in self.__class__._to_proto_helpers:
150-
assert self.__class__._to_proto_helpers[field_name].converter, (
151-
f"Expecting to run a coverter given a helper was defined for: {field_name}"
152-
)
150+
if (
151+
field_name in self.__class__._to_proto_helpers
152+
and not field_name in self._field_helpers_called
153+
):
154+
self._field_helpers_called.add(field_name)
155+
if (
156+
self.__class__._to_proto_helpers.get(field_name)
157+
and self.__class__._to_proto_helpers[field_name].converter
158+
):
159+
value = self.__class__._to_proto_helpers[field_name].converter(value)
153160
sub_paths = self._build_proto_and_paths(
154161
proto_msg,
155-
{field_name: self.__class__._to_proto_helpers[field_name].converter(value)}, # type: ignore[misc]
162+
{field_name: value}, # type: ignore[misc]
156163
"",
157164
already_setting_path_override=True,
158165
)
@@ -178,6 +185,7 @@ def _build_proto_and_paths(
178185
assert self.__class__._to_proto_helpers[field_name].converter, (
179186
f"Expecting to run a coverter given a helper was defined for: {field_name}"
180187
)
188+
self._field_helpers_called.add(field_name)
181189
for item in value:
182190
repeated_field.append(
183191
self.__class__._to_proto_helpers[field_name].converter(**item) # type: ignore

python/lib/sift_client/sift_types/test_report.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ class TestMeasurementUpdate(TestMeasurementBase, ModelUpdate[TestMeasurementProt
271271
"string_expected_value": MappingHelper(
272272
proto_attr_path="string_bounds.expected_value", update_field="string_bounds"
273273
),
274+
"unit": MappingHelper(proto_attr_path="unit.abbreviated_name", update_field="unit"),
274275
}
275276

276277
def _add_resource_id_to_proto(self, proto_msg: TestMeasurementProto):
@@ -286,7 +287,6 @@ class TestMeasurementCreate(TestMeasurementBase, ModelCreate[TestMeasurementProt
286287
test_step_id: str
287288
passed: bool
288289
timestamp: datetime
289-
unit: str | None = None
290290

291291
def to_proto(self) -> TestMeasurementProto:
292292
"""Convert to protobuf message with custom logic."""
@@ -301,6 +301,8 @@ def to_proto(self) -> TestMeasurementProto:
301301

302302
proto.timestamp.FromDatetime(self.timestamp)
303303

304+
if self.unit:
305+
proto.unit.abbreviated_name = self.unit
304306
if self.numeric_value is not None:
305307
proto.numeric_value = self.numeric_value
306308
elif self.string_value is not None:

python/lib/sift_client/util/test_results/context_manager.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,17 +268,27 @@ def measure(
268268
name: str,
269269
value: float | str | bool,
270270
bounds: dict[str, float] | NumericBounds | str | None = None,
271+
timestamp: datetime | None = None,
272+
unit: str | None = None,
271273
) -> bool:
272274
"""Measure a value and return the result.
273275
274-
returns: The measurement object.
276+
Args:
277+
name: The name of the measurement.
278+
value: The value of the measurement.
279+
bounds: [Optional] The bounds to compare the value to.
280+
timestamp: [Optional] The timestamp of the measurement. Defaults to the current time.
281+
unit: [Optional] The unit of the measurement.
282+
283+
returns: The result of the measurement.
275284
"""
276285
assert self.current_step is not None
277286
create = TestMeasurementCreate(
278287
test_step_id=str(self.current_step.id_),
279288
name=name,
280289
passed=True,
281-
timestamp=datetime.now(timezone.utc),
290+
timestamp=timestamp if timestamp else datetime.now(timezone.utc),
291+
unit=unit,
282292
)
283293
evaluate_measurement_bounds(create, value, bounds)
284294
measurement = self.client.test_results.create_measurement(create)

0 commit comments

Comments
 (0)