Coverage for src/polars_eval_metrics/metric_helpers.py: 96%
28 statements
« prev ^ index » next coverage.py v7.10.7, created at 2025-09-29 15:04 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2025-09-29 15:04 +0000
1from __future__ import annotations
3"""
4Helper functions for creating metrics from various sources.
6Simple, functional approach to metric creation without factory classes.
7"""
9# pyre-strict
11from typing import TYPE_CHECKING, Any
13from .utils import parse_enum_value
16if TYPE_CHECKING:
17 from .metric_define import MetricDefine, MetricScope, MetricType
20def create_metric_from_dict(config: dict[str, Any]) -> MetricDefine:
21 """Create a MetricDefine from dictionary configuration."""
23 _validate_metric_config(config)
25 from .metric_define import MetricDefine
27 return MetricDefine(
28 name=config["name"],
29 label=config.get("label", config["name"]),
30 type=config.get("type", "across_sample"),
31 scope=config.get("scope"),
32 within_expr=config.get("within_expr"),
33 across_expr=config.get("across_expr"),
34 )
37def create_metrics(configs: list[dict[str, Any]] | list[str]) -> list[MetricDefine]:
38 """Create metrics from configurations or simple names."""
39 if not configs:
40 return []
42 from .metric_define import MetricDefine
44 if isinstance(configs[0], str):
45 str_configs: list[str] = configs # pyre-ignore[9]
46 return [MetricDefine(name=name) for name in str_configs]
48 dict_configs: list[dict[str, Any]] = configs # pyre-ignore[9]
49 return [create_metric_from_dict(config) for config in dict_configs]
52# ========================================
53# VALIDATION FUNCTIONS - Centralized Logic
54# ========================================
57def _validate_metric_config(config: dict[str, Any]) -> None:
58 """Validate metric configuration dictionary."""
59 if "name" not in config:
60 raise ValueError("Metric configuration must include 'name'")
62 if not isinstance(config["name"], str) or not config["name"].strip():
63 raise ValueError("Metric name must be a non-empty string")
65 if "type" in config and config["type"] is not None:
66 from .metric_define import MetricType
68 parse_enum_value(
69 config["type"],
70 MetricType,
71 field="metric type",
72 )
74 if "scope" in config and config["scope"] is not None:
75 from .metric_define import MetricScope
77 parse_enum_value(
78 config["scope"],
79 MetricScope,
80 field="metric scope",
81 allow_none=True,
82 )