Coverage for  / home / runner / work / bijux-cli / bijux-cli / src / bijux_cli / cli / commands / memory / service.py: 100%

61 statements  

« prev     ^ index     » next       coverage.py v7.13.2, created at 2026-01-26 17:59 +0000

1# SPDX-License-Identifier: Apache-2.0 

2# Copyright © 2025 Bijan Mousavi 

3 

4"""Implements the root callback for the `bijux memory` command group. 

5 

6This module defines the default action for the `bijux memory` command. When 

7invoked without a subcommand, it provides a summary of the transient, 

8in-memory data store, including the number of keys currently set. 

9 

10Output Contract: 

11 * Success: `{"status": "ok", "count": int|None, "message": str}` 

12 * Error: `{"error": str, "code": int}` 

13 

14Exit Codes: 

15 * `0`: Success. 

16 * `1`: An unexpected error occurred (e.g., service unavailable). 

17 * `3`: An ASCII or encoding error was detected in the environment. 

18""" 

19 

20from __future__ import annotations 

21 

22import contextlib 

23import platform 

24import sys 

25 

26import typer 

27 

28from bijux_cli.cli.color import resolve_click_color 

29from bijux_cli.cli.commands.memory.resolve import resolve_memory_service 

30from bijux_cli.cli.core.command import ( 

31 ascii_safe, 

32 contains_non_ascii_env, 

33 normalize_format, 

34 raise_exit_intent, 

35 record_history, 

36 validate_common_flags, 

37) 

38from bijux_cli.cli.core.constants import ( 

39 OPT_FORMAT, 

40 OPT_LOG_LEVEL, 

41 OPT_PRETTY, 

42 OPT_QUIET, 

43) 

44from bijux_cli.cli.core.help_text import ( 

45 HELP_FORMAT, 

46 HELP_LOG_LEVEL, 

47 HELP_NO_PRETTY, 

48 HELP_QUIET, 

49) 

50from bijux_cli.core.enums import ErrorType, ExitCode, LogLevel, OutputFormat 

51from bijux_cli.core.precedence import current_execution_policy 

52 

53 

54def _build_payload(include_runtime: bool, keys_count: int | None) -> dict[str, object]: 

55 """Constructs the payload for the memory summary command. 

56 

57 Args: 

58 include_runtime (bool): If True, includes Python and platform info. 

59 keys_count (int | None): The number of keys in the memory store, or 

60 None if the count could not be determined. 

61 

62 Returns: 

63 Mapping[str, object]: A dictionary containing the status, key count, 

64 a confirmation message, and optional runtime metadata. 

65 """ 

66 payload: dict[str, object] = { 

67 "status": "ok", 

68 "count": keys_count, 

69 "message": "Memory command executed", 

70 } 

71 if include_runtime: 

72 return { 

73 "status": payload["status"], 

74 "count": payload["count"], 

75 "message": payload["message"], 

76 "python": ascii_safe(platform.python_version(), "python_version"), 

77 "platform": ascii_safe(platform.platform(), "platform"), 

78 } 

79 return payload 

80 

81 

82def _run_one_shot_mode( 

83 *, 

84 command: str, 

85 fmt: OutputFormat, 

86 output_format: OutputFormat, 

87 quiet: bool, 

88 log_level: LogLevel, 

89 effective_pretty: bool, 

90 include_runtime: bool, 

91 keys_count: int | None, 

92) -> None: 

93 """Orchestrates the execution for a single memory summary request. 

94 

95 This helper function handles environment validation, payload construction, 

96 and final emission for the memory summary. 

97 

98 Args: 

99 command (str): The command name for telemetry and error context. 

100 fmt (str): The output format string (e.g., "json"). 

101 output_format (OutputFormat): The output format enum for serialization. 

102 quiet (bool): If True, suppresses all output except for errors. 

103 log_level (LogLevel): Logging level for diagnostics. 

104 effective_pretty (bool): If True, pretty-prints the output. 

105 include_runtime (bool): If True, includes Python/platform info. 

106 keys_count (int | None): The number of keys in the memory store. 

107 

108 Returns: 

109 None: 

110 

111 Raises: 

112 SystemExit: Always exits with a contract-compliant status code and 

113 payload upon completion or error. 

114 """ 

115 if contains_non_ascii_env(): 

116 raise_exit_intent( 

117 "Non-ASCII characters in environment variables", 

118 code=3, 

119 failure="ascii_env", 

120 error_type=ErrorType.ASCII, 

121 command=command, 

122 fmt=fmt, 

123 quiet=quiet, 

124 include_runtime=include_runtime, 

125 log_level=log_level, 

126 ) 

127 try: 

128 payload = _build_payload(include_runtime, keys_count) 

129 except ValueError as exc: 

130 raise_exit_intent( 

131 str(exc), 

132 code=3, 

133 failure="ascii", 

134 error_type=ErrorType.ASCII, 

135 command=command, 

136 fmt=fmt, 

137 quiet=quiet, 

138 include_runtime=include_runtime, 

139 log_level=log_level, 

140 ) 

141 

142 emit_output = not quiet 

143 if not emit_output: 

144 from bijux_cli.core.exit_policy import ExitIntent, ExitIntentError 

145 

146 raise ExitIntentError( 

147 ExitIntent( 

148 code=ExitCode.SUCCESS, 

149 stream=None, 

150 payload=None, 

151 fmt=output_format, 

152 pretty=effective_pretty, 

153 show_traceback=False, 

154 ) 

155 ) 

156 record_history(command, 0) 

157 from bijux_cli.core.exit_policy import ExitIntent, ExitIntentError 

158 

159 raise ExitIntentError( 

160 ExitIntent( 

161 code=ExitCode.SUCCESS, 

162 stream="stdout", 

163 payload=payload, 

164 fmt=output_format, 

165 pretty=effective_pretty, 

166 show_traceback=False, 

167 ) 

168 ) 

169 

170 

171def memory_summary( 

172 ctx: typer.Context, 

173 quiet: bool, 

174 fmt: str, 

175 pretty: bool, 

176 log_level: str, 

177) -> None: 

178 """Handles the logic for the default `bijux memory` command action. 

179 

180 This function is called by the main Typer callback when no subcommand is 

181 specified. It resolves the memory service, gets the key count, and then 

182 executes the one-shot summary. 

183 

184 Args: 

185 ctx (typer.Context): The Typer context for the CLI. 

186 quiet (bool): If True, suppresses all output except for errors. 

187 fmt (str): The output format, "json" or "yaml". 

188 pretty (bool): If True, pretty-prints the output. 

189 log_level (str): The requested logging level. 

190 

191 Returns: 

192 None: 

193 

194 Raises: 

195 SystemExit: Always exits with a contract-compliant status code and 

196 payload upon completion or error. 

197 """ 

198 command = "memory" 

199 policy = current_execution_policy() 

200 quiet = policy.quiet 

201 include_runtime = policy.include_runtime 

202 log_level_value = policy.log_level 

203 include_runtime = policy.include_runtime 

204 effective_pretty = policy.pretty 

205 fmt_lower = normalize_format(fmt) or OutputFormat.JSON 

206 

207 validate_common_flags( 

208 fmt_lower, 

209 command, 

210 quiet, 

211 include_runtime=include_runtime, 

212 log_level=log_level_value, 

213 ) 

214 

215 output_format = fmt_lower 

216 

217 svc = resolve_memory_service( 

218 command, fmt_lower, quiet, include_runtime, log_level_value 

219 ) 

220 

221 keys_count = None 

222 with contextlib.suppress(Exception): 

223 keys_count = len(svc.keys()) 

224 

225 _run_one_shot_mode( 

226 command=command, 

227 fmt=fmt_lower, 

228 output_format=output_format, 

229 quiet=quiet, 

230 log_level=log_level_value, 

231 effective_pretty=effective_pretty, 

232 include_runtime=include_runtime, 

233 keys_count=keys_count, 

234 ) 

235 

236 

237def memory( 

238 ctx: typer.Context, 

239 quiet: bool = typer.Option(False, *OPT_QUIET, help=HELP_QUIET), 

240 fmt: str = typer.Option("json", *OPT_FORMAT, help=HELP_FORMAT), 

241 pretty: bool = typer.Option(True, OPT_PRETTY, help=HELP_NO_PRETTY), 

242 log_level: str = typer.Option("info", *OPT_LOG_LEVEL, help=HELP_LOG_LEVEL), 

243) -> None: 

244 """Defines the entrypoint for the `bijux memory` command group. 

245 

246 This function serves as the main callback. It handles `--help` requests and, 

247 if no subcommand is invoked, delegates to the `memory_summary` function to 

248 display the default summary view. 

249 

250 Args: 

251 ctx (typer.Context): The Typer context for the CLI. 

252 quiet (bool): If True, suppresses all output except for errors. 

253 fmt (str): The output format, "json" or "yaml". 

254 pretty (bool): If True, pretty-prints the output. 

255 log_level (str): The resolved log level name. 

256 

257 Returns: 

258 None: 

259 

260 Raises: 

261 typer.Exit: Exits after displaying help text. 

262 """ 

263 if any(arg in ("-h", "--help") for arg in sys.argv): 

264 policy = current_execution_policy() 

265 color = resolve_click_color(quiet=policy.quiet, fmt=None) 

266 if ctx.invoked_subcommand: 

267 cmd = getattr(ctx.command, "get_command", None) 

268 sub_cmd = cmd(ctx, ctx.invoked_subcommand) if callable(cmd) else None 

269 if sub_cmd and hasattr(sub_cmd, "get_help"): 

270 typer.echo(sub_cmd.get_help(ctx), color=color) 

271 else: 

272 typer.echo(ctx.get_help(), color=color) 

273 else: 

274 typer.echo(ctx.get_help(), color=color) 

275 raise typer.Exit() 

276 if ctx.invoked_subcommand is None: 

277 memory_summary( 

278 ctx=ctx, 

279 quiet=quiet, 

280 fmt=fmt, 

281 pretty=pretty, 

282 log_level=log_level, 

283 )