Skip to content

Commit 5ff5cd3

Browse files
authored
Merge branch 'main' into feature/gsk-2574-improve-release-step
2 parents 15386e3 + 1b724c6 commit 5ff5cd3

7 files changed

Lines changed: 135 additions & 7 deletions

File tree

docs/reference/suite/index.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ Test suite
66
.. automethod:: __init__
77
.. automethod:: run
88
.. automethod:: add_test
9+
.. automethod:: add_test
10+
.. automethod:: remove_test
11+
.. automethod:: upgrade_test
12+
.. automethod:: update_test_params
913
.. automethod:: upload
1014
.. automethod:: download
1115

giskard/core/suite.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any, Dict, List, Optional, Tuple, Union
1+
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
22

33
import inspect
44
import logging
@@ -612,6 +612,35 @@ def add_test(
612612

613613
return self
614614

615+
def upgrade_test(
616+
self, test: GiskardTest, migrate_params_fn: Optional[Callable[[Dict[str, Any]], Dict[str, Any]]] = None
617+
) -> "Suite":
618+
"""Upgrade a test with a new version, the test being upgraded are matched using display_name tests property.
619+
620+
Parameters
621+
----------
622+
test : GiskardTest
623+
The newest version of a test to be upgraded
624+
migrate_params_fn : Optional[Callable[[Dict[str, Any]], Dict[str, Any]]]
625+
An optional callback used to migrate the old test params into the new params
626+
627+
Returns
628+
-------
629+
Suite
630+
The current instance of the test suite to allow chained calls.
631+
632+
"""
633+
634+
for test_to_upgrade in self.tests:
635+
if test_to_upgrade.giskard_test.display_name != test.display_name:
636+
continue
637+
638+
test_to_upgrade.giskard_test = test
639+
if migrate_params_fn is not None:
640+
test_to_upgrade.provided_inputs = migrate_params_fn(test_to_upgrade.provided_inputs.copy())
641+
642+
return self
643+
615644
@singledispatchmethod
616645
def remove_test(self, arg):
617646
"""Remove a test from the suite.

giskard/core/test_result.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ class TestResult:
7777
messages: List[TestMessage] = field(default_factory=list, repr=False)
7878
props: Dict[str, str] = field(default_factory=dict, repr=False)
7979
metric_name: str = "Metric"
80-
metric: float = 0
80+
metric: Optional[float] = None
8181
missing_count: int = 0
8282
missing_percent: float = 0
8383
unexpected_count: int = 0

giskard/registry/giskard_test.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ def assert_(self):
112112

113113
assert result.passed, message
114114

115+
@property
116+
def display_name(self):
117+
return self.meta.display_name
118+
115119

116120
Function = Callable[..., Result]
117121

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,27 @@
11
{% for view in groups -%}
2+
23
<details>
34
<summary>👉{{ view.group.name }} issues ({{ view.issues|length }})</summary>
45

56
{% for issue in view.issues -%}
7+
{{ issue.description }}
68

7-
| Vulnerability | Level | Data slice | Metric | Transformation | Deviation |
8-
|---------------|-------|------------|--------|----------------|-----------|
9-
| {{ view.group.name }} | <span style="color:{% if issue.level.value == "major" %} red {% else %} orange {% endif %} "> {{ issue.level.value }} {% if issue.level.value == "major" %} 🔴 {% else %} 🟡 {% endif %} </span> | {{ issue.slicing_fn if issue.slicing_fn else "—" }} | {% if "metric" in issue.meta %}{{ issue.meta.metric }} = {{ issue.meta.metric_value|format_metric }}{% else %} "—" {% endif %} | {{ issue.transformation_fn if issue.transformation_fn else "—" }} | {{ issue.meta["deviation"] if "deviation" in issue.meta else "—" }} |
9+
| Level | Data slice | Metric | Deviation |
10+
|-------|------------|--------|-----------|
11+
| <span style="color:{% if issue.level.value == "major" %} red {% else %} orange {% endif %} "> {{ issue.level.value }} {% if issue.level.value == "major" %} 🔴 {% else %} 🟡 {% endif %} </span> | {{ issue.slicing_fn if issue.slicing_fn else "—" }} | {% if "metric" in issue.meta %}{{ issue.meta.metric }} = {{ issue.meta.metric_value|format_metric }}{% else %} "—" {% endif %} | {{ issue.meta["deviation"] if "deviation" in issue.meta else "—" }} |
12+
13+
{% if issue.taxonomy %}
14+
<h4 class="font-bold text-sm mt-4">Taxonomy</h4>
15+
{% for tag in issue.taxonomy %}
16+
<span class="inline-block bg-blue-300/25 text-zinc-100 px-2 py-0.5 rounded-sm text-sm mr-1 my-2">
17+
{{ tag }}
18+
</span>
19+
{% endfor %}
20+
<br />
21+
{% endif %}
1022

1123
<details>
1224
<summary> 🔍✨Examples</summary>
13-
{{ issue.description }}
1425

1526
{% if issue.examples(3)|length %}
1627
{{ issue.examples(issue.meta.num_examples if "num_examples" in issue.meta else 3).to_markdown(
@@ -22,5 +33,7 @@ else True)|replace("\\n", "<br>")|safe }}
2233
{% endfor %}
2334

2435
</details>
36+
<!-- line breaker -->
2537
{% endfor -%}
2638
<br />
39+

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ giskard = "giskard.integrations.mlflow.giskard_evaluator:GiskardEvaluator"
132132
name = "giskard"
133133
readme = "README.md"
134134
license = { text = "Apache Software License 2.0" }
135-
version = "2.6.0"
135+
version = "2.7.0"
136136
description = "The testing framework dedicated to ML models, from tabular to LLMs"
137137
authors = [{ name = "Giskard AI", email = "hello@giskard.ai" }]
138138
keywords = ["Artificial Intelligence", "Machine Learning", "Quality", "MLOps"]

tests/test_programmatic.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,3 +307,81 @@ def test_with_custom_types():
307307

308308
result = _test_with_custom_types(b=CustomClass(2), a=CustomClass(3)).execute()
309309
assert result
310+
311+
312+
def test_upgrade_test_with_migration():
313+
@test(name="Named test")
314+
def my_named_test(is_pass: bool):
315+
return is_pass
316+
317+
suite = Suite().add_test(my_named_test(True), "True").add_test(my_named_test(False), "False")
318+
319+
assert len(suite.tests) == 2
320+
assert suite.tests[0].test_id == "True"
321+
assert suite.tests[0].giskard_test.meta.uuid == my_named_test.meta.uuid
322+
assert suite.tests[0].provided_inputs == {"is_pass": True}
323+
assert suite.tests[1].test_id == "False"
324+
assert suite.tests[1].giskard_test.meta.uuid == my_named_test.meta.uuid
325+
assert suite.tests[1].provided_inputs == {"is_pass": False}
326+
327+
@test(name="Named test")
328+
def my_named_test_v2(passed: bool):
329+
return passed
330+
331+
suite.upgrade_test(my_named_test_v2, lambda params: {"passed": params["is_pass"]})
332+
333+
assert len(suite.tests) == 2
334+
assert suite.tests[0].test_id == "True"
335+
assert suite.tests[0].giskard_test.meta.uuid == my_named_test_v2.meta.uuid
336+
assert suite.tests[0].provided_inputs == {"passed": True}
337+
assert suite.tests[1].test_id == "False"
338+
assert suite.tests[1].giskard_test.meta.uuid == my_named_test_v2.meta.uuid
339+
assert suite.tests[1].provided_inputs == {"passed": False}
340+
341+
342+
def test_upgrade_test_without():
343+
@test(name="Named test")
344+
def my_named_test(is_pass: bool):
345+
return is_pass
346+
347+
suite = Suite().add_test(my_named_test(True), "True")
348+
349+
assert len(suite.tests) == 1
350+
assert suite.tests[0].test_id == "True"
351+
assert suite.tests[0].giskard_test.meta.uuid == my_named_test.meta.uuid
352+
assert suite.tests[0].provided_inputs == {"is_pass": True}
353+
354+
@test(name="Named test")
355+
def my_named_test_v2(passed: bool):
356+
return passed
357+
358+
suite.upgrade_test(my_named_test_v2)
359+
360+
assert len(suite.tests) == 1
361+
assert suite.tests[0].test_id == "True"
362+
assert suite.tests[0].giskard_test.meta.uuid == my_named_test_v2.meta.uuid
363+
assert suite.tests[0].provided_inputs == {"is_pass": True}
364+
365+
366+
def test_upgrade_test_not_matching():
367+
@test(name="Named test")
368+
def my_named_test(is_pass: bool):
369+
return is_pass
370+
371+
suite = Suite().add_test(my_named_test(True), "True")
372+
373+
assert len(suite.tests) == 1
374+
assert suite.tests[0].test_id == "True"
375+
assert suite.tests[0].giskard_test.meta.uuid == my_named_test.meta.uuid
376+
assert suite.tests[0].provided_inputs == {"is_pass": True}
377+
378+
@test(name="Named test V2")
379+
def my_named_test_v2(passed: bool):
380+
return passed
381+
382+
suite.upgrade_test(my_named_test_v2)
383+
384+
assert len(suite.tests) == 1
385+
assert suite.tests[0].test_id == "True"
386+
assert suite.tests[0].giskard_test.meta.uuid == my_named_test.meta.uuid
387+
assert suite.tests[0].provided_inputs == {"is_pass": True}

0 commit comments

Comments
 (0)