Coverage for  / home / runner / work / bijux-cli / bijux-cli / src / bijux_cli / cli / commands / status.py: 97%

93 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 `status` command for the Bijux CLI. 

5 

6This module provides a lightweight "liveness probe" for the CLI, designed for 

7health checks and monitoring. In its default mode, it performs a quick check 

8and returns a simple "ok" status. It also supports a continuous "watch" mode 

9that emits status updates at a regular interval. 

10 

11Output Contract: 

12 * Success: `{"status": "ok"}` 

13 * Watch Mode Tick: `{"status": "ok", "ts": float, ...}` 

14 * Watch Mode Stop: `{"status": "watch-stopped", ...}` 

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

16 

17Exit Codes: 

18 * `0`: Success. 

19 * `1`: Internal or fatal error during execution. 

20 * `2`: Invalid argument (e.g., bad watch interval or format). 

21 * `3`: ASCII encoding error. 

22""" 

23 

24from __future__ import annotations 

25 

26import platform 

27import signal 

28import threading 

29import time 

30from types import FrameType 

31from typing import Any 

32 

33import typer 

34 

35from bijux_cli.cli.core.command import ( 

36 ascii_safe, 

37 new_run_command, 

38 validate_common_flags, 

39) 

40from bijux_cli.cli.core.constants import ( 

41 OPT_FORMAT, 

42 OPT_LOG_LEVEL, 

43 OPT_PRETTY, 

44 OPT_QUIET, 

45) 

46from bijux_cli.cli.core.help_text import ( 

47 HELP_FORMAT, 

48 HELP_LOG_LEVEL, 

49 HELP_NO_PRETTY, 

50 HELP_QUIET, 

51) 

52from bijux_cli.core.di import DIContainer 

53from bijux_cli.core.enums import ErrorType, LogLevel, OutputFormat 

54from bijux_cli.core.exit_policy import ExitIntentError 

55from bijux_cli.core.precedence import current_execution_policy, resolve_exit_intent 

56from bijux_cli.core.runtime import AsyncTyper 

57from bijux_cli.infra.contracts import Emitter 

58from bijux_cli.services.contracts import TelemetryProtocol 

59 

60typer.core.rich = None # type: ignore[attr-defined] 

61 

62status_app = AsyncTyper( 

63 name="status", 

64 help="Show the CLI Status (Lean probe).", 

65 rich_markup_mode=None, 

66 context_settings={"help_option_names": ["-h", "--help"]}, 

67 no_args_is_help=False, 

68) 

69 

70 

71def _build_payload(include_runtime: bool) -> dict[str, object]: 

72 """Constructs the status payload. 

73 

74 Args: 

75 include_runtime (bool): If True, includes Python version and platform 

76 information in the payload. 

77 

78 Returns: 

79 Mapping[str, object]: A dictionary containing the status and optional 

80 runtime details. 

81 """ 

82 payload: dict[str, object] = {"status": "ok"} 

83 if include_runtime: 

84 payload.update( 

85 { 

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

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

88 } 

89 ) 

90 return payload 

91 

92 

93def _run_watch_mode( 

94 *, 

95 command: str, 

96 watch_interval: float, 

97 fmt: OutputFormat, 

98 quiet: bool, 

99 effective_pretty: bool, 

100 include_runtime: bool, 

101 log_policy: Any, 

102 telemetry: TelemetryProtocol, 

103 emitter: Emitter, 

104) -> None: 

105 """Emits CLI status in a continuous watch mode. 

106 

107 This function enters a loop, emitting a JSON-formatted status payload at 

108 the specified interval. It handles graceful shutdown on SIGINT (Ctrl+C). 

109 

110 Args: 

111 command (str): The command name for telemetry and error contracts. 

112 watch_interval (float): The polling interval in seconds. 

113 fmt (str): The output format, which must be "json" for streaming. 

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

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

116 include_runtime (bool): If True, includes Python and platform fields. 

117 log_level (LogLevel): Logging level for diagnostics. 

118 telemetry (TelemetryProtocol): The telemetry sink for reporting events. 

119 emitter (Emitter): The output emitter instance. 

120 

121 Returns: 

122 None: 

123 

124 Raises: 

125 SystemExit: On an invalid format or an unrecoverable error during 

126 the watch loop. 

127 """ 

128 format_value = fmt 

129 if format_value is not OutputFormat.JSON: 

130 intent = resolve_exit_intent( 

131 message="Only JSON output is supported in watch mode.", 

132 code=2, 

133 failure="watch_fmt", 

134 command=command, 

135 fmt=format_value, 

136 quiet=quiet, 

137 include_runtime=include_runtime, 

138 error_type=ErrorType.USER_INPUT, 

139 log_level=log_policy.level, 

140 ) 

141 raise ExitIntentError(intent) 

142 

143 stop = False 

144 emit_output = not quiet 

145 emit_diagnostics = log_policy.show_internal and emit_output 

146 

147 def _sigint_handler(_sig: int, _frame: FrameType | None) -> None: 

148 """Handles SIGINT to allow for a graceful shutdown of the watch loop. 

149 

150 Args: 

151 _sig (int): The signal number (unused). 

152 _frame (FrameType | None): The current stack frame (unused). 

153 """ 

154 nonlocal stop 

155 stop = True 

156 

157 old_handler = None 

158 if threading.current_thread() is threading.main_thread(): 158 ↛ 163line 158 didn't jump to line 163 because the condition on line 158 was always true

159 try: 

160 old_handler = signal.signal(signal.SIGINT, _sigint_handler) 

161 except ValueError: 

162 old_handler = None 

163 try: 

164 while not stop: 

165 try: 

166 payload = _build_payload(include_runtime) 

167 payload["ts"] = time.time() 

168 if emit_diagnostics: 

169 emitter.emit( 

170 payload, 

171 fmt=OutputFormat.JSON, 

172 pretty=effective_pretty, 

173 level=LogLevel.DEBUG, 

174 message=f"Debug: Emitting payload at ts={payload['ts']}", 

175 output=None, 

176 emit_output=False, 

177 emit_diagnostics=True, 

178 ) 

179 if emit_output: 

180 emitter.emit( 

181 payload, 

182 fmt=OutputFormat.JSON, 

183 pretty=effective_pretty, 

184 level=LogLevel.INFO, 

185 message="Status update", 

186 output=None, 

187 emit_output=emit_output, 

188 emit_diagnostics=emit_diagnostics, 

189 ) 

190 telemetry.event( 

191 "COMMAND_SUCCESS", 

192 {"command": command, "format": fmt.value, "mode": "watch"}, 

193 ) 

194 time.sleep(watch_interval) 

195 except ValueError as exc: 

196 intent = resolve_exit_intent( 

197 message=str(exc), 

198 code=3, 

199 failure="ascii", 

200 command=command, 

201 fmt=fmt, 

202 quiet=quiet, 

203 include_runtime=include_runtime, 

204 error_type=ErrorType.ASCII, 

205 log_level=log_policy.level, 

206 ) 

207 raise ExitIntentError(intent) from exc 

208 except Exception as exc: 

209 intent = resolve_exit_intent( 

210 message=f"Watch mode failed: {exc}", 

211 code=1, 

212 failure="emit", 

213 command=command, 

214 fmt=fmt, 

215 quiet=quiet, 

216 include_runtime=include_runtime, 

217 error_type=ErrorType.INTERNAL, 

218 log_level=log_policy.level, 

219 ) 

220 raise ExitIntentError(intent) from exc 

221 finally: 

222 if old_handler is not None: 222 ↛ 224line 222 didn't jump to line 224 because the condition on line 222 was always true

223 signal.signal(signal.SIGINT, old_handler) 

224 try: 

225 stop_payload = _build_payload(include_runtime) 

226 stop_payload["status"] = "watch-stopped" 

227 if emit_diagnostics: 

228 emitter.emit( 

229 stop_payload, 

230 fmt=OutputFormat.JSON, 

231 pretty=effective_pretty, 

232 level=LogLevel.DEBUG, 

233 message="Debug: Emitting watch-stopped payload", 

234 output=None, 

235 emit_output=False, 

236 emit_diagnostics=True, 

237 ) 

238 if emit_output: 

239 emitter.emit( 

240 stop_payload, 

241 fmt=OutputFormat.JSON, 

242 pretty=effective_pretty, 

243 level=LogLevel.INFO, 

244 message="Status watch stopped", 

245 output=None, 

246 emit_output=emit_output, 

247 emit_diagnostics=emit_diagnostics, 

248 ) 

249 telemetry.event( 

250 "COMMAND_STOPPED", 

251 {"command": command, "format": fmt.value, "mode": "watch"}, 

252 ) 

253 except (ValueError, Exception): 

254 _ = None 

255 

256 

257@status_app.callback(invoke_without_command=True) 

258def status( 

259 ctx: typer.Context, 

260 watch: float | None = typer.Option(None, "--watch", help="Poll every N seconds"), 

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

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

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

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

265) -> None: 

266 """Defines the entrypoint and logic for the `bijux status` command. 

267 

268 This function orchestrates the status check. It validates flags and then 

269 dispatches to either the single-run logic or the continuous watch mode 

270 based on the presence of the `--watch` flag. 

271 

272 Args: 

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

274 watch (float | None): If provided, enters watch mode, polling at this 

275 interval in seconds. Must be a positive number. 

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

277 output payload. 

278 fmt (str): The output format, either "json" or "yaml". Watch mode only 

279 supports "json". 

280 pretty (bool): If True, pretty-prints the output for human readability. 

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

282 

283 Returns: 

284 None: 

285 

286 Raises: 

287 SystemExit: Exits with a contract-compliant status code and payload 

288 upon any error, such as an invalid watch interval. 

289 """ 

290 if ctx.invoked_subcommand: 

291 return 

292 

293 emitter = DIContainer.current().resolve(Emitter) 

294 telemetry = DIContainer.current().resolve(TelemetryProtocol) 

295 command = "status" 

296 

297 effective = current_execution_policy() 

298 fmt_lower = validate_common_flags( 

299 fmt, 

300 command, 

301 effective.quiet, 

302 include_runtime=effective.include_runtime, 

303 log_level=effective.log_level, 

304 ) 

305 quiet = effective.quiet 

306 log_policy = effective.log_policy 

307 log_level_value = effective.log_level 

308 pretty = effective.pretty 

309 

310 if watch is not None: 

311 try: 

312 interval = float(watch) 

313 if interval <= 0: 

314 raise ValueError 

315 except (ValueError, TypeError): 

316 intent = resolve_exit_intent( 

317 message="Invalid watch interval: must be > 0", 

318 code=2, 

319 failure="interval", 

320 command=command, 

321 fmt=fmt_lower, 

322 quiet=quiet, 

323 include_runtime=effective.include_runtime, 

324 error_type=ErrorType.USER_INPUT, 

325 log_level=log_level_value, 

326 ) 

327 raise ExitIntentError(intent) from None 

328 

329 _run_watch_mode( 

330 command=command, 

331 watch_interval=interval, 

332 fmt=fmt_lower, 

333 quiet=quiet, 

334 effective_pretty=pretty, 

335 include_runtime=effective.include_runtime, 

336 log_policy=log_policy, 

337 telemetry=telemetry, 

338 emitter=emitter, 

339 ) 

340 else: 

341 new_run_command( 

342 command_name=command, 

343 payload_builder=lambda include: _build_payload(include), 

344 quiet=quiet, 

345 fmt=fmt_lower, 

346 pretty=pretty, 

347 log_level=log_level_value, 

348 )