Coverage for src/polars_eval_metrics/evaluation_context.py: 95%

80 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2025-09-29 15:04 +0000

1from __future__ import annotations 

2 

3from dataclasses import dataclass, field 

4from types import MappingProxyType 

5from typing import Mapping, Sequence 

6 

7from .metric_define import MetricDefine 

8 

9 

10@dataclass(frozen=True) 

11class EstimateCatalog: 

12 """Read-only view over configured estimates with lookup helpers.""" 

13 

14 entries: tuple[tuple[str, str], ...] 

15 _key_to_label: Mapping[str, str] = field(init=False, repr=False) 

16 _label_to_key: Mapping[str, str] = field(init=False, repr=False) 

17 _label_order: Mapping[str, int] = field(init=False, repr=False) 

18 

19 def __post_init__(self) -> None: 

20 key_to_label = dict(self.entries) 

21 label_to_key = {label: key for key, label in self.entries} 

22 label_order = {label: idx for idx, label in enumerate(label_to_key)} 

23 object.__setattr__(self, "_key_to_label", MappingProxyType(key_to_label)) 

24 object.__setattr__(self, "_label_to_key", MappingProxyType(label_to_key)) 

25 object.__setattr__(self, "_label_order", MappingProxyType(label_order)) 

26 

27 @classmethod 

28 def build(cls, mapping: Mapping[str, str]) -> "EstimateCatalog": 

29 return cls(tuple(mapping.items())) 

30 

31 @property 

32 def key_to_label(self) -> Mapping[str, str]: 

33 return self._key_to_label 

34 

35 @property 

36 def label_to_key(self) -> Mapping[str, str]: 

37 return self._label_to_key 

38 

39 @property 

40 def label_order(self) -> Mapping[str, int]: 

41 return self._label_order 

42 

43 @property 

44 def keys(self) -> tuple[str, ...]: 

45 return tuple(key for key, _ in self.entries) 

46 

47 @property 

48 def labels(self) -> tuple[str, ...]: 

49 return tuple(label for _, label in self.entries) 

50 

51 def label_for(self, key: str) -> str: 

52 return self._key_to_label.get(key, key) 

53 

54 

55@dataclass(frozen=True) 

56class MetricCatalog: 

57 """Read-only view over configured metrics with ordering helpers.""" 

58 

59 metrics: tuple[MetricDefine, ...] 

60 _labels: tuple[str, ...] = field(init=False, repr=False) 

61 _names: tuple[str, ...] = field(init=False, repr=False) 

62 _name_order: Mapping[str, int] = field(init=False, repr=False) 

63 _label_order: Mapping[str, int] = field(init=False, repr=False) 

64 _name_set: frozenset[str] = field(init=False, repr=False) 

65 

66 def __post_init__(self) -> None: 

67 labels = tuple(metric.label or metric.name for metric in self.metrics) 

68 names = tuple(metric.name for metric in self.metrics) 

69 name_order = {name: idx for idx, name in enumerate(names)} 

70 label_order = {label: idx for idx, label in enumerate(labels)} 

71 object.__setattr__(self, "_labels", labels) 

72 object.__setattr__(self, "_names", names) 

73 object.__setattr__(self, "_name_order", MappingProxyType(name_order)) 

74 object.__setattr__(self, "_label_order", MappingProxyType(label_order)) 

75 object.__setattr__(self, "_name_set", frozenset(names)) 

76 

77 @property 

78 def entries(self) -> tuple[MetricDefine, ...]: 

79 return self.metrics 

80 

81 @property 

82 def labels(self) -> tuple[str, ...]: 

83 return self._labels 

84 

85 @property 

86 def names(self) -> tuple[str, ...]: 

87 return self._names 

88 

89 @property 

90 def name_order(self) -> Mapping[str, int]: 

91 return self._name_order 

92 

93 @property 

94 def label_order(self) -> Mapping[str, int]: 

95 return self._label_order 

96 

97 def contains(self, name: str) -> bool: 

98 return name in self._name_set 

99 

100 

101@dataclass(frozen=True) 

102class FormatterContext: 

103 """Lightweight bundle of formatting-related configuration.""" 

104 

105 group_by: Mapping[str, str] 

106 subgroup_by: Mapping[str, str] 

107 estimate_catalog: EstimateCatalog 

108 metric_catalog: MetricCatalog 

109 subgroup_categories: Sequence[str] = ()