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
« 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
4"""Implements the root callback for the `bijux memory` command group.
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.
10Output Contract:
11 * Success: `{"status": "ok", "count": int|None, "message": str}`
12 * Error: `{"error": str, "code": int}`
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"""
20from __future__ import annotations
22import contextlib
23import platform
24import sys
26import typer
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
54def _build_payload(include_runtime: bool, keys_count: int | None) -> dict[str, object]:
55 """Constructs the payload for the memory summary command.
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.
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
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.
95 This helper function handles environment validation, payload construction,
96 and final emission for the memory summary.
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.
108 Returns:
109 None:
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 )
142 emit_output = not quiet
143 if not emit_output:
144 from bijux_cli.core.exit_policy import ExitIntent, ExitIntentError
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
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 )
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.
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.
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.
191 Returns:
192 None:
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
207 validate_common_flags(
208 fmt_lower,
209 command,
210 quiet,
211 include_runtime=include_runtime,
212 log_level=log_level_value,
213 )
215 output_format = fmt_lower
217 svc = resolve_memory_service(
218 command, fmt_lower, quiet, include_runtime, log_level_value
219 )
221 keys_count = None
222 with contextlib.suppress(Exception):
223 keys_count = len(svc.keys())
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 )
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.
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.
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.
257 Returns:
258 None:
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 )