Coverage for little_loops / config / cli.py: 99%

70 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2026-04-11 23:20 -0500

1"""CLI presentation configuration dataclasses. 

2 

3Covers ANSI color overrides for log levels, priority labels, type labels, 

4and general CLI display options. 

5""" 

6 

7from __future__ import annotations 

8 

9from dataclasses import dataclass, field 

10from typing import Any 

11 

12 

13@dataclass 

14class CliColorsLoggerConfig: 

15 """ANSI color overrides for Logger log-level output.""" 

16 

17 info: str = "36" 

18 success: str = "32" 

19 warning: str = "33" 

20 error: str = "38;5;208" 

21 

22 @classmethod 

23 def from_dict(cls, data: dict[str, Any]) -> CliColorsLoggerConfig: 

24 """Create CliColorsLoggerConfig from dictionary.""" 

25 return cls( 

26 info=data.get("info", "36"), 

27 success=data.get("success", "32"), 

28 warning=data.get("warning", "33"), 

29 error=data.get("error", "38;5;208"), 

30 ) 

31 

32 

33@dataclass 

34class CliColorsPriorityConfig: 

35 """ANSI color overrides for issue priority labels (P0–P5).""" 

36 

37 P0: str = "38;5;208;1" 

38 P1: str = "38;5;208" 

39 P2: str = "33" 

40 P3: str = "0" 

41 P4: str = "2" 

42 P5: str = "2" 

43 

44 @classmethod 

45 def from_dict(cls, data: dict[str, Any]) -> CliColorsPriorityConfig: 

46 """Create CliColorsPriorityConfig from dictionary.""" 

47 return cls( 

48 P0=data.get("P0", "38;5;208;1"), 

49 P1=data.get("P1", "38;5;208"), 

50 P2=data.get("P2", "33"), 

51 P3=data.get("P3", "0"), 

52 P4=data.get("P4", "2"), 

53 P5=data.get("P5", "2"), 

54 ) 

55 

56 

57@dataclass 

58class CliColorsTypeConfig: 

59 """ANSI color overrides for issue type labels (BUG, FEAT, ENH).""" 

60 

61 BUG: str = "38;5;208" 

62 FEAT: str = "32" 

63 ENH: str = "34" 

64 

65 @classmethod 

66 def from_dict(cls, data: dict[str, Any]) -> CliColorsTypeConfig: 

67 """Create CliColorsTypeConfig from dictionary.""" 

68 return cls( 

69 BUG=data.get("BUG", "38;5;208"), 

70 FEAT=data.get("FEAT", "32"), 

71 ENH=data.get("ENH", "34"), 

72 ) 

73 

74 

75@dataclass 

76class CliColorsEdgeLabelsConfig: 

77 """ANSI color overrides for FSM transition edge labels in loop diagrams.""" 

78 

79 yes: str = "32" 

80 no: str = "38;5;208" 

81 error: str = "31" 

82 partial: str = "33" 

83 next: str = "2" 

84 default: str = "2" 

85 blocked: str = "31" 

86 retry_exhausted: str = "38;5;208" 

87 

88 @classmethod 

89 def from_dict(cls, data: dict[str, Any]) -> CliColorsEdgeLabelsConfig: 

90 """Create CliColorsEdgeLabelsConfig from dictionary.""" 

91 return cls( 

92 yes=data.get("yes", "32"), 

93 no=data.get("no", "38;5;208"), 

94 error=data.get("error", "31"), 

95 partial=data.get("partial", "33"), 

96 next=data.get("next", "2"), 

97 default=data.get("default", "2"), 

98 blocked=data.get("blocked", "31"), 

99 retry_exhausted=data.get("retry_exhausted", "38;5;208"), 

100 ) 

101 

102 def to_dict(self) -> dict[str, str]: 

103 """Convert to a label→SGR-code dict for use by _colorize_diagram_labels. 

104 

105 Maps 'default' back to '_' to match the key used in _EDGE_LABEL_COLORS. 

106 """ 

107 return { 

108 "yes": self.yes, 

109 "no": self.no, 

110 "error": self.error, 

111 "partial": self.partial, 

112 "next": self.next, 

113 "_": self.default, 

114 "blocked": self.blocked, 

115 "retry_exhausted": self.retry_exhausted, 

116 } 

117 

118 

119@dataclass 

120class CliColorsConfig: 

121 """ANSI color overrides for logger levels, priority labels, and type labels.""" 

122 

123 logger: CliColorsLoggerConfig = field(default_factory=CliColorsLoggerConfig) 

124 priority: CliColorsPriorityConfig = field(default_factory=CliColorsPriorityConfig) 

125 type: CliColorsTypeConfig = field(default_factory=CliColorsTypeConfig) 

126 fsm_active_state: str = "32" 

127 fsm_edge_labels: CliColorsEdgeLabelsConfig = field(default_factory=CliColorsEdgeLabelsConfig) 

128 

129 @classmethod 

130 def from_dict(cls, data: dict[str, Any]) -> CliColorsConfig: 

131 """Create CliColorsConfig from dictionary.""" 

132 return cls( 

133 logger=CliColorsLoggerConfig.from_dict(data.get("logger", {})), 

134 priority=CliColorsPriorityConfig.from_dict(data.get("priority", {})), 

135 type=CliColorsTypeConfig.from_dict(data.get("type", {})), 

136 fsm_active_state=data.get("fsm_active_state", "32"), 

137 fsm_edge_labels=CliColorsEdgeLabelsConfig.from_dict(data.get("fsm_edge_labels", {})), 

138 ) 

139 

140 

141@dataclass 

142class RefineStatusConfig: 

143 """refine-status display configuration.""" 

144 

145 columns: list[str] = field(default_factory=list) 

146 elide_order: list[str] = field(default_factory=list) 

147 

148 @classmethod 

149 def from_dict(cls, data: dict[str, Any]) -> RefineStatusConfig: 

150 """Create RefineStatusConfig from dictionary.""" 

151 return cls( 

152 columns=data.get("columns", []), 

153 elide_order=data.get("elide_order", []), 

154 ) 

155 

156 

157@dataclass 

158class CliConfig: 

159 """CLI output configuration.""" 

160 

161 color: bool = True 

162 colors: CliColorsConfig = field(default_factory=CliColorsConfig) 

163 

164 @classmethod 

165 def from_dict(cls, data: dict[str, Any]) -> CliConfig: 

166 """Create CliConfig from dictionary.""" 

167 return cls( 

168 color=data.get("color", True), 

169 colors=CliColorsConfig.from_dict(data.get("colors", {})), 

170 )