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

47 statements  

« prev     ^ index     » next       coverage.py v7.10.4, created at 2025-08-19 23:36 +0000

1# SPDX-License-Identifier: MIT 

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 * Verbose: Adds `{"python": str, "platform": str}` to the payload. 

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

14 

15Exit Codes: 

16 * `0`: Success. 

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

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

19""" 

20 

21from __future__ import annotations 

22 

23from collections.abc import Mapping 

24import contextlib 

25import platform 

26import sys 

27 

28import typer 

29 

30from bijux_cli.commands.memory.utils import resolve_memory_service 

31from bijux_cli.commands.utilities import ( 

32 ascii_safe, 

33 contains_non_ascii_env, 

34 emit_and_exit, 

35 emit_error_and_exit, 

36 validate_common_flags, 

37) 

38from bijux_cli.core.constants import ( 

39 HELP_DEBUG, 

40 HELP_FORMAT, 

41 HELP_NO_PRETTY, 

42 HELP_QUIET, 

43 HELP_VERBOSE, 

44) 

45from bijux_cli.core.enums import OutputFormat 

46 

47 

48def _build_payload( 

49 include_runtime: bool, keys_count: int | None 

50) -> Mapping[str, object]: 

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

52 

53 Args: 

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

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

56 None if the count could not be determined. 

57 

58 Returns: 

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

60 a confirmation message, and optional runtime metadata. 

61 """ 

62 payload: dict[str, object] = { 

63 "status": "ok", 

64 "count": keys_count, 

65 "message": "Memory command executed", 

66 } 

67 if include_runtime: 

68 payload["python"] = ascii_safe(platform.python_version(), "python_version") 

69 payload["platform"] = ascii_safe(platform.platform(), "platform") 

70 return payload 

71 

72 

73def _run_one_shot_mode( 

74 *, 

75 command: str, 

76 fmt: str, 

77 output_format: OutputFormat, 

78 quiet: bool, 

79 verbose: bool, 

80 debug: bool, 

81 effective_pretty: bool, 

82 include_runtime: bool, 

83 keys_count: int | None, 

84) -> None: 

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

86 

87 This helper function handles environment validation, payload construction, 

88 and final emission for the memory summary. 

89 

90 Args: 

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

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

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

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

95 verbose (bool): If True, includes runtime metadata in the payload. 

96 debug (bool): If True, enables debug diagnostics. 

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

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

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

100 

101 Returns: 

102 None: 

103 

104 Raises: 

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

106 payload upon completion or error. 

107 """ 

108 if contains_non_ascii_env(): 

109 emit_error_and_exit( 

110 "Non-ASCII characters in environment variables", 

111 code=3, 

112 failure="ascii_env", 

113 command=command, 

114 fmt=fmt, 

115 quiet=quiet, 

116 include_runtime=include_runtime, 

117 ) 

118 

119 try: 

120 payload = _build_payload(include_runtime, keys_count) 

121 except ValueError as exc: 

122 emit_error_and_exit( 

123 str(exc), 

124 code=3, 

125 failure="ascii", 

126 command=command, 

127 fmt=fmt, 

128 quiet=quiet, 

129 include_runtime=include_runtime, 

130 ) 

131 

132 emit_and_exit( 

133 payload=payload, 

134 fmt=output_format, 

135 effective_pretty=effective_pretty, 

136 verbose=verbose, 

137 debug=debug, 

138 quiet=quiet, 

139 command=command, 

140 exit_code=0, 

141 ) 

142 

143 

144def memory_summary( 

145 ctx: typer.Context, 

146 quiet: bool, 

147 verbose: bool, 

148 fmt: str, 

149 pretty: bool, 

150 debug: bool, 

151) -> None: 

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

153 

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

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

156 executes the one-shot summary. 

157 

158 Args: 

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

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

161 verbose (bool): If True, includes Python/platform details in the output. 

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

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

164 debug (bool): If True, enables debug diagnostics. 

165 

166 Returns: 

167 None: 

168 

169 Raises: 

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

171 payload upon completion or error. 

172 """ 

173 command = "memory" 

174 include_runtime = verbose or debug 

175 

176 fmt_lower = validate_common_flags(fmt, command, quiet) 

177 

178 output_format = OutputFormat.YAML if fmt_lower == "yaml" else OutputFormat.JSON 

179 effective_pretty = debug or pretty 

180 

181 svc = resolve_memory_service(command, fmt_lower, quiet, include_runtime, debug) 

182 

183 keys_count = None 

184 with contextlib.suppress(Exception): 

185 keys_count = len(svc.keys()) 

186 

187 _run_one_shot_mode( 

188 command=command, 

189 fmt=fmt_lower, 

190 output_format=output_format, 

191 quiet=quiet, 

192 verbose=verbose, 

193 debug=debug, 

194 effective_pretty=effective_pretty, 

195 include_runtime=include_runtime, 

196 keys_count=keys_count, 

197 ) 

198 

199 

200def memory( 

201 ctx: typer.Context, 

202 quiet: bool = typer.Option(False, "-q", "--quiet", help=HELP_QUIET), 

203 verbose: bool = typer.Option(False, "-v", "--verbose", help=HELP_VERBOSE), 

204 fmt: str = typer.Option("json", "-f", "--format", help=HELP_FORMAT), 

205 pretty: bool = typer.Option(True, "--pretty/--no-pretty", help=HELP_NO_PRETTY), 

206 debug: bool = typer.Option(False, "-d", "--debug", help=HELP_DEBUG), 

207) -> None: 

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

209 

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

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

212 display the default summary view. 

213 

214 Args: 

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

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

217 verbose (bool): If True, includes runtime metadata in the output. 

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

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

220 debug (bool): If True, enables debug diagnostics. 

221 

222 Returns: 

223 None: 

224 

225 Raises: 

226 typer.Exit: Exits after displaying help text. 

227 """ 

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

229 if ctx.invoked_subcommand: 

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

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

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

233 typer.echo( 

234 sub_cmd.get_help(ctx) # pyright: ignore[reportAttributeAccessIssue] 

235 ) 

236 else: 

237 typer.echo(ctx.get_help()) 

238 else: 

239 typer.echo(ctx.get_help()) 

240 raise typer.Exit() 

241 if ctx.invoked_subcommand is None: 

242 memory_summary(ctx, quiet, verbose, fmt, pretty, debug)