Skip to content

Commit b22b02a

Browse files
Merge branch 'main' into fix/human-labeled-csv-text
2 parents 8267310 + 3f729ac commit b22b02a

2 files changed

Lines changed: 71 additions & 1 deletion

File tree

‎pyrit/output/score/pretty.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class PrettyScorePrinter(_PrettyPrinterMixin, PrinterBase):
2020
Provides ``_render_score`` for inline use by other printers (e.g.,
2121
conversation and attack-result printers) and ``render_async`` /
2222
``write_async`` for standalone rendering of a list of scores.
23+
Categories are comma-separated, with embedded newlines shown as escaped text.
2324
"""
2425

2526
def __init__(
@@ -54,7 +55,8 @@ def _render_score(self, score: Score, indent_level: int = 3) -> str:
5455
indent = self._indent * indent_level
5556
scorer_name = resolve_scorer_name(score, none_value="Unknown")
5657
lines.append(self._format_colored(f"{indent}Scorer: {scorer_name}"))
57-
lines.append(self._format_colored(f"{indent}• Category: {score.score_category or 'N/A'}", Fore.LIGHTMAGENTA_EX))
58+
category_str = ", ".join(score.score_category).replace("\n", r"\n") if score.score_category else "N/A"
59+
lines.append(self._format_colored(f"{indent}• Category: {category_str}", Fore.LIGHTMAGENTA_EX))
5860
lines.append(self._format_colored(f"{indent}• Type: {score.score_type}", Fore.CYAN))
5961

6062
if score.is_undetermined:

‎tests/unit/output/score/test_pretty.py‎

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# Copyright (c) Microsoft Corporation.
22
# Licensed under the MIT license.
33

4+
import pytest
5+
from colorama import Fore, Style
6+
47
from pyrit.models import Score, ScoreStatus
58
from pyrit.output.score.pretty import PrettyScorePrinter
69

@@ -22,3 +25,68 @@ async def test_render_async_supports_undetermined_score() -> None:
2225
output = await printer.render_async([score])
2326

2427
assert "Value: undetermined" in output
28+
29+
30+
@pytest.mark.parametrize(
31+
("categories", "expected"),
32+
[
33+
(["refusal"], "refusal"),
34+
(["refusal", "scams"], "refusal, scams"),
35+
(["scams", "refusal", "scams"], "scams, refusal, scams"),
36+
],
37+
)
38+
async def test_render_async_joins_score_categories(*, categories: list[str], expected: str) -> None:
39+
printer = PrettyScorePrinter(enable_colors=False)
40+
score = Score(score_value="true", score_type="true_false", score_category=categories)
41+
42+
output = await printer.render_async([score])
43+
44+
assert f"Category: {expected}" in output
45+
46+
47+
@pytest.mark.parametrize("enable_colors", [False, True])
48+
@pytest.mark.parametrize(
49+
("categories", "expected"),
50+
[
51+
(["refusal\nscams"], r"refusal\nscams"),
52+
(["refusal\r\nscams"], r"refusal\r\nscams"),
53+
(["refusal\rscams"], r"refusal\rscams"),
54+
(["\nrefusal\n\n"], r"\nrefusal\n\n"),
55+
(["refusal\nscams", "other\ncategory"], r"refusal\nscams, other\ncategory"),
56+
([r"refusal\nscams"], r"refusal\nscams"),
57+
],
58+
)
59+
async def test_write_async_keeps_category_line_breaks_on_one_line(
60+
*,
61+
categories: list[str],
62+
expected: str,
63+
enable_colors: bool,
64+
capsys: pytest.CaptureFixture[str],
65+
) -> None:
66+
printer = PrettyScorePrinter(enable_colors=enable_colors)
67+
score = Score(
68+
score_value="true",
69+
score_type="true_false",
70+
score_category=categories,
71+
score_metadata={"note": "unchanged"},
72+
)
73+
original = score.model_dump(mode="json")
74+
75+
await printer.write_async([score])
76+
77+
lines = capsys.readouterr().out.splitlines()
78+
category_line = f" \u2022 Category: {expected}"
79+
if enable_colors:
80+
category_line = f"{Fore.LIGHTMAGENTA_EX}{category_line}{Style.RESET_ALL}"
81+
assert lines[1] == category_line
82+
assert len(lines) == 4
83+
assert score.model_dump(mode="json") == original
84+
85+
86+
async def test_render_async_renders_missing_category_as_na() -> None:
87+
printer = PrettyScorePrinter(enable_colors=False)
88+
score = Score(score_value="true", score_type="true_false")
89+
90+
output = await printer.render_async([score])
91+
92+
assert "Category: N/A" in output

0 commit comments

Comments
 (0)