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

32 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 `sleep` command for the Bijux CLI. 

5 

6This module provides a simple command to pause execution for a specified duration. 

7It is primarily used for scripting, testing, or rate-limiting operations within 

8automated workflows. The command returns a structured payload confirming the 

9duration slept. 

10 

11Output Contract: 

12 * Success: `{"slept": float}` 

13 * Verbose: Adds `{"python": str, "platform": str}` to the payload. 

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

15 

16Exit Codes: 

17 * `0`: Success. 

18 * `1`: Internal or configuration-related error. 

19 * `2`: Invalid argument (e.g., negative duration) or timeout exceeded. 

20""" 

21 

22from __future__ import annotations 

23 

24from collections.abc import Mapping 

25import platform 

26import time 

27 

28import typer 

29 

30from bijux_cli.commands.utilities import ( 

31 ascii_safe, 

32 emit_error_and_exit, 

33 new_run_command, 

34 validate_common_flags, 

35) 

36from bijux_cli.contracts import ConfigProtocol 

37from bijux_cli.core.constants import ( 

38 DEFAULT_COMMAND_TIMEOUT, 

39 HELP_DEBUG, 

40 HELP_FORMAT, 

41 HELP_NO_PRETTY, 

42 HELP_QUIET, 

43 HELP_VERBOSE, 

44) 

45from bijux_cli.core.di import DIContainer 

46 

47typer.core.rich = None # type: ignore[attr-defined,assignment] 

48 

49sleep_app = typer.Typer( # pytype: skip-file 

50 name="sleep", 

51 help="Pause execution for a specified duration.", 

52 rich_markup_mode=None, 

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

54 no_args_is_help=False, 

55) 

56 

57 

58def _build_payload(include_runtime: bool, slept: float) -> Mapping[str, object]: 

59 """Constructs the structured payload for the sleep command. 

60 

61 Args: 

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

63 information in the payload. 

64 slept (float): The number of seconds the command slept. 

65 

66 Returns: 

67 Mapping[str, object]: A dictionary containing the sleep duration and 

68 optional runtime details. 

69 """ 

70 payload: dict[str, object] = {"slept": slept} 

71 if include_runtime: 

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

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

74 return payload 

75 

76 

77@sleep_app.callback(invoke_without_command=True) 

78def sleep( 

79 ctx: typer.Context, 

80 seconds: float = typer.Option( 

81 ..., "--seconds", "-s", help="Duration in seconds (must be ≥ 0)" 

82 ), 

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

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

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

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

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

88) -> None: 

89 """Defines the entrypoint and logic for the `bijux sleep` command. 

90 

91 This function validates the requested sleep duration against configuration 

92 limits, pauses execution, and then emits a structured payload confirming 

93 the duration. 

94 

95 Args: 

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

97 seconds (float): The duration in seconds to pause execution. Must be 

98 non-negative and not exceed the configured command timeout. 

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

100 verbose (bool): If True, includes Python and platform details in the 

101 output payload. 

102 fmt (str): The output format, either "json" or "yaml". Defaults to "json". 

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

104 debug (bool): If True, enables debug diagnostics, implying `verbose` 

105 and `pretty`. 

106 

107 Returns: 

108 None: 

109 

110 Raises: 

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

112 upon any error, such as a negative sleep duration or a timeout 

113 violation. 

114 """ 

115 command = "sleep" 

116 

117 fmt_lower = validate_common_flags(fmt, command, quiet) 

118 

119 if seconds < 0: 

120 emit_error_and_exit( 

121 "sleep length must be non-negative", 

122 code=2, 

123 failure="negative", 

124 command=command, 

125 fmt=fmt_lower, 

126 quiet=quiet, 

127 include_runtime=verbose, 

128 debug=debug, 

129 ) 

130 

131 cfg: ConfigProtocol = DIContainer.current().resolve(ConfigProtocol) 

132 

133 try: 

134 timeout = float(cfg.get("BIJUXCLI_COMMAND_TIMEOUT", DEFAULT_COMMAND_TIMEOUT)) 

135 except Exception as exc: 

136 emit_error_and_exit( 

137 f"Failed to read timeout: {exc}", 

138 code=1, 

139 failure="config", 

140 command=command, 

141 fmt=fmt_lower, 

142 quiet=quiet, 

143 include_runtime=verbose, 

144 debug=debug, 

145 ) 

146 

147 if seconds > timeout: 

148 emit_error_and_exit( 

149 "Command timed out because sleep duration exceeded the configured timeout.", 

150 code=2, 

151 failure="timeout", 

152 command=command, 

153 fmt=fmt_lower, 

154 quiet=quiet, 

155 include_runtime=verbose, 

156 debug=debug, 

157 ) 

158 

159 time.sleep(seconds) 

160 

161 new_run_command( 

162 command_name=command, 

163 payload_builder=lambda include: _build_payload(include, seconds), 

164 quiet=quiet, 

165 verbose=verbose, 

166 fmt=fmt_lower, 

167 pretty=pretty, 

168 debug=debug, 

169 )