conson-xp 1.18.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- conson_xp-1.18.0.dist-info/METADATA +412 -0
- conson_xp-1.18.0.dist-info/RECORD +176 -0
- conson_xp-1.18.0.dist-info/WHEEL +4 -0
- conson_xp-1.18.0.dist-info/entry_points.txt +5 -0
- conson_xp-1.18.0.dist-info/licenses/LICENSE +29 -0
- xp/__init__.py +9 -0
- xp/cli/__init__.py +5 -0
- xp/cli/__main__.py +6 -0
- xp/cli/commands/__init__.py +153 -0
- xp/cli/commands/conbus/__init__.py +25 -0
- xp/cli/commands/conbus/conbus.py +128 -0
- xp/cli/commands/conbus/conbus_actiontable_commands.py +233 -0
- xp/cli/commands/conbus/conbus_autoreport_commands.py +108 -0
- xp/cli/commands/conbus/conbus_blink_commands.py +163 -0
- xp/cli/commands/conbus/conbus_config_commands.py +29 -0
- xp/cli/commands/conbus/conbus_custom_commands.py +57 -0
- xp/cli/commands/conbus/conbus_datapoint_commands.py +113 -0
- xp/cli/commands/conbus/conbus_discover_commands.py +61 -0
- xp/cli/commands/conbus/conbus_event_commands.py +81 -0
- xp/cli/commands/conbus/conbus_lightlevel_commands.py +207 -0
- xp/cli/commands/conbus/conbus_linknumber_commands.py +102 -0
- xp/cli/commands/conbus/conbus_modulenumber_commands.py +104 -0
- xp/cli/commands/conbus/conbus_msactiontable_commands.py +94 -0
- xp/cli/commands/conbus/conbus_output_commands.py +163 -0
- xp/cli/commands/conbus/conbus_raw_commands.py +62 -0
- xp/cli/commands/conbus/conbus_receive_commands.py +59 -0
- xp/cli/commands/conbus/conbus_scan_commands.py +58 -0
- xp/cli/commands/file_commands.py +186 -0
- xp/cli/commands/homekit/__init__.py +3 -0
- xp/cli/commands/homekit/homekit.py +118 -0
- xp/cli/commands/homekit/homekit_start_commands.py +43 -0
- xp/cli/commands/module_commands.py +187 -0
- xp/cli/commands/reverse_proxy_commands.py +178 -0
- xp/cli/commands/server/__init__.py +3 -0
- xp/cli/commands/server/server_commands.py +135 -0
- xp/cli/commands/telegram/__init__.py +5 -0
- xp/cli/commands/telegram/telegram.py +41 -0
- xp/cli/commands/telegram/telegram_blink_commands.py +79 -0
- xp/cli/commands/telegram/telegram_checksum_commands.py +112 -0
- xp/cli/commands/telegram/telegram_discover_commands.py +41 -0
- xp/cli/commands/telegram/telegram_linknumber_commands.py +86 -0
- xp/cli/commands/telegram/telegram_parse_commands.py +75 -0
- xp/cli/commands/telegram/telegram_version_commands.py +52 -0
- xp/cli/main.py +87 -0
- xp/cli/utils/__init__.py +1 -0
- xp/cli/utils/click_tree.py +57 -0
- xp/cli/utils/datapoint_type_choice.py +57 -0
- xp/cli/utils/decorators.py +351 -0
- xp/cli/utils/error_handlers.py +201 -0
- xp/cli/utils/formatters.py +312 -0
- xp/cli/utils/module_type_choice.py +56 -0
- xp/cli/utils/serial_number_type.py +52 -0
- xp/cli/utils/system_function_choice.py +57 -0
- xp/cli/utils/xp_module_type.py +53 -0
- xp/connection/__init__.py +13 -0
- xp/connection/exceptions.py +22 -0
- xp/models/__init__.py +36 -0
- xp/models/actiontable/__init__.py +1 -0
- xp/models/actiontable/actiontable.py +43 -0
- xp/models/actiontable/msactiontable_xp20.py +53 -0
- xp/models/actiontable/msactiontable_xp24.py +58 -0
- xp/models/actiontable/msactiontable_xp33.py +65 -0
- xp/models/conbus/__init__.py +1 -0
- xp/models/conbus/conbus.py +87 -0
- xp/models/conbus/conbus_autoreport.py +67 -0
- xp/models/conbus/conbus_blink.py +80 -0
- xp/models/conbus/conbus_client_config.py +55 -0
- xp/models/conbus/conbus_connection_status.py +40 -0
- xp/models/conbus/conbus_custom.py +58 -0
- xp/models/conbus/conbus_datapoint.py +89 -0
- xp/models/conbus/conbus_discover.py +64 -0
- xp/models/conbus/conbus_event_raw.py +47 -0
- xp/models/conbus/conbus_lightlevel.py +52 -0
- xp/models/conbus/conbus_linknumber.py +54 -0
- xp/models/conbus/conbus_output.py +57 -0
- xp/models/conbus/conbus_raw.py +45 -0
- xp/models/conbus/conbus_receive.py +42 -0
- xp/models/conbus/conbus_writeconfig.py +60 -0
- xp/models/homekit/__init__.py +1 -0
- xp/models/homekit/homekit_accessory.py +35 -0
- xp/models/homekit/homekit_config.py +106 -0
- xp/models/homekit/homekit_conson_config.py +86 -0
- xp/models/log_entry.py +130 -0
- xp/models/protocol/__init__.py +1 -0
- xp/models/protocol/conbus_protocol.py +312 -0
- xp/models/response.py +42 -0
- xp/models/telegram/__init__.py +1 -0
- xp/models/telegram/action_type.py +31 -0
- xp/models/telegram/datapoint_type.py +82 -0
- xp/models/telegram/event_telegram.py +140 -0
- xp/models/telegram/event_type.py +15 -0
- xp/models/telegram/input_action_type.py +69 -0
- xp/models/telegram/input_type.py +17 -0
- xp/models/telegram/module_type.py +188 -0
- xp/models/telegram/module_type_code.py +205 -0
- xp/models/telegram/output_telegram.py +103 -0
- xp/models/telegram/reply_telegram.py +297 -0
- xp/models/telegram/system_function.py +116 -0
- xp/models/telegram/system_telegram.py +94 -0
- xp/models/telegram/telegram.py +28 -0
- xp/models/telegram/telegram_type.py +19 -0
- xp/models/telegram/timeparam_type.py +51 -0
- xp/models/write_config_type.py +33 -0
- xp/services/__init__.py +26 -0
- xp/services/actiontable/__init__.py +1 -0
- xp/services/actiontable/actiontable_serializer.py +273 -0
- xp/services/actiontable/msactiontable_serializer.py +7 -0
- xp/services/actiontable/msactiontable_xp20_serializer.py +169 -0
- xp/services/actiontable/msactiontable_xp24_serializer.py +120 -0
- xp/services/actiontable/msactiontable_xp33_serializer.py +239 -0
- xp/services/conbus/__init__.py +1 -0
- xp/services/conbus/actiontable/__init__.py +1 -0
- xp/services/conbus/actiontable/actiontable_download_service.py +158 -0
- xp/services/conbus/actiontable/actiontable_list_service.py +91 -0
- xp/services/conbus/actiontable/actiontable_show_service.py +89 -0
- xp/services/conbus/actiontable/actiontable_upload_service.py +211 -0
- xp/services/conbus/actiontable/msactiontable_service.py +232 -0
- xp/services/conbus/conbus_blink_all_service.py +181 -0
- xp/services/conbus/conbus_blink_service.py +158 -0
- xp/services/conbus/conbus_custom_service.py +156 -0
- xp/services/conbus/conbus_datapoint_queryall_service.py +182 -0
- xp/services/conbus/conbus_datapoint_service.py +170 -0
- xp/services/conbus/conbus_discover_service.py +312 -0
- xp/services/conbus/conbus_event_raw_service.py +181 -0
- xp/services/conbus/conbus_output_service.py +194 -0
- xp/services/conbus/conbus_raw_service.py +122 -0
- xp/services/conbus/conbus_receive_service.py +115 -0
- xp/services/conbus/conbus_scan_service.py +150 -0
- xp/services/conbus/write_config_service.py +194 -0
- xp/services/homekit/__init__.py +1 -0
- xp/services/homekit/homekit_cache_service.py +307 -0
- xp/services/homekit/homekit_conbus_service.py +93 -0
- xp/services/homekit/homekit_config_validator.py +310 -0
- xp/services/homekit/homekit_conson_validator.py +121 -0
- xp/services/homekit/homekit_dimminglight.py +182 -0
- xp/services/homekit/homekit_dimminglight_service.py +148 -0
- xp/services/homekit/homekit_hap_service.py +342 -0
- xp/services/homekit/homekit_lightbulb.py +120 -0
- xp/services/homekit/homekit_lightbulb_service.py +86 -0
- xp/services/homekit/homekit_module_service.py +56 -0
- xp/services/homekit/homekit_outlet.py +168 -0
- xp/services/homekit/homekit_outlet_service.py +121 -0
- xp/services/homekit/homekit_service.py +359 -0
- xp/services/log_file_service.py +309 -0
- xp/services/module_type_service.py +257 -0
- xp/services/protocol/__init__.py +21 -0
- xp/services/protocol/conbus_event_protocol.py +360 -0
- xp/services/protocol/conbus_protocol.py +318 -0
- xp/services/protocol/protocol_factory.py +78 -0
- xp/services/protocol/telegram_protocol.py +264 -0
- xp/services/reverse_proxy_service.py +435 -0
- xp/services/server/__init__.py +1 -0
- xp/services/server/base_server_service.py +366 -0
- xp/services/server/cp20_server_service.py +65 -0
- xp/services/server/device_service_factory.py +94 -0
- xp/services/server/server_service.py +428 -0
- xp/services/server/xp130_server_service.py +67 -0
- xp/services/server/xp20_server_service.py +92 -0
- xp/services/server/xp230_server_service.py +58 -0
- xp/services/server/xp24_server_service.py +245 -0
- xp/services/server/xp33_server_service.py +535 -0
- xp/services/telegram/__init__.py +1 -0
- xp/services/telegram/telegram_blink_service.py +138 -0
- xp/services/telegram/telegram_checksum_service.py +149 -0
- xp/services/telegram/telegram_datapoint_service.py +82 -0
- xp/services/telegram/telegram_discover_service.py +277 -0
- xp/services/telegram/telegram_link_number_service.py +216 -0
- xp/services/telegram/telegram_output_service.py +322 -0
- xp/services/telegram/telegram_service.py +380 -0
- xp/services/telegram/telegram_version_service.py +288 -0
- xp/utils/__init__.py +12 -0
- xp/utils/checksum.py +61 -0
- xp/utils/dependencies.py +531 -0
- xp/utils/event_helper.py +31 -0
- xp/utils/serialization.py +205 -0
- xp/utils/time_utils.py +134 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
"""Common decorators for CLI commands."""
|
|
2
|
+
|
|
3
|
+
import functools
|
|
4
|
+
from typing import Any, Callable, Tuple, Type
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
|
|
8
|
+
from xp.cli.utils.formatters import OutputFormatter
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def handle_service_errors(
|
|
12
|
+
*service_exceptions: Type[Exception],
|
|
13
|
+
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
|
|
14
|
+
"""Handle common service exceptions with consistent JSON error formatting.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
service_exceptions: Tuple of exception types to catch and handle.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
Decorator function that wraps commands with error handling.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
24
|
+
"""Apply error handling to the decorated function.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
func: The function to decorate.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
Wrapped function with error handling.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
@functools.wraps(func)
|
|
34
|
+
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
35
|
+
"""Execute function with error handling.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
args: Positional arguments passed to the decorated function.
|
|
39
|
+
kwargs: Keyword arguments passed to the decorated function.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
Result from the decorated function.
|
|
43
|
+
|
|
44
|
+
Raises:
|
|
45
|
+
SystemExit: When a service exception or unexpected error occurs.
|
|
46
|
+
"""
|
|
47
|
+
formatter = OutputFormatter(True)
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
return func(*args, **kwargs)
|
|
51
|
+
except service_exceptions as e:
|
|
52
|
+
error_response = formatter.error_response(str(e))
|
|
53
|
+
click.echo(error_response)
|
|
54
|
+
raise SystemExit(1)
|
|
55
|
+
except Exception as e:
|
|
56
|
+
# Handle unexpected errors
|
|
57
|
+
error_response = formatter.error_response(f"Unexpected error: {e}")
|
|
58
|
+
click.echo(error_response)
|
|
59
|
+
raise SystemExit(1)
|
|
60
|
+
|
|
61
|
+
return wrapper
|
|
62
|
+
|
|
63
|
+
return decorator
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def common_options(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
67
|
+
"""Add validation option to command.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
func: The function to decorate.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
Decorated function with common options.
|
|
74
|
+
"""
|
|
75
|
+
return func
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def telegram_parser_command(
|
|
79
|
+
service_exceptions: Tuple[Type[Exception], ...] = (),
|
|
80
|
+
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
|
|
81
|
+
"""Apply telegram parsing commands with standard error handling.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
service_exceptions: Additional service exceptions to handle.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
Decorator function for telegram parsing commands.
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
91
|
+
"""Apply telegram parser decorators.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
func: The function to decorate.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
Decorated function with telegram parsing support.
|
|
98
|
+
"""
|
|
99
|
+
# Apply common options
|
|
100
|
+
func = common_options(func)
|
|
101
|
+
|
|
102
|
+
# Apply error handling for telegram parsing
|
|
103
|
+
from xp.services.telegram.telegram_service import TelegramParsingError
|
|
104
|
+
|
|
105
|
+
exceptions = (TelegramParsingError,) + service_exceptions
|
|
106
|
+
func = handle_service_errors(*exceptions)(func)
|
|
107
|
+
|
|
108
|
+
return func
|
|
109
|
+
|
|
110
|
+
return decorator
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def service_command(
|
|
114
|
+
*service_exceptions: Type[Exception],
|
|
115
|
+
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
|
|
116
|
+
"""Apply service-based commands with error handling and JSON output.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
service_exceptions: Service exception types to handle.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Decorator function for service commands.
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
126
|
+
"""Apply service command decorators.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
func: The function to decorate.
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
Decorated function with service error handling.
|
|
133
|
+
"""
|
|
134
|
+
func = handle_service_errors(*service_exceptions)(func)
|
|
135
|
+
return func
|
|
136
|
+
|
|
137
|
+
return decorator
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def list_command(
|
|
141
|
+
*service_exceptions: Type[Exception],
|
|
142
|
+
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
|
|
143
|
+
"""Apply list/search commands with common options.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
service_exceptions: Service exception types to handle.
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
Decorator function for list commands.
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
153
|
+
"""Apply list command decorators.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
func: The function to decorate.
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
Decorated function with list error handling.
|
|
160
|
+
"""
|
|
161
|
+
func = handle_service_errors(*service_exceptions)(func)
|
|
162
|
+
return func
|
|
163
|
+
|
|
164
|
+
return decorator
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def file_operation_command() -> Callable[[Callable[..., Any]], Callable[..., Any]]:
|
|
168
|
+
"""Apply file operation commands with common filters.
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
Decorator function for file operation commands.
|
|
172
|
+
"""
|
|
173
|
+
|
|
174
|
+
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
175
|
+
"""Apply file operation decorators.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
func: The function to decorate.
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
Decorated function with filter options.
|
|
182
|
+
"""
|
|
183
|
+
func = click.option(
|
|
184
|
+
"--time-range", help="Filter by time range (HH:MM:SS,mmm-HH:MM:SS,mmm)"
|
|
185
|
+
)(func)
|
|
186
|
+
func = click.option(
|
|
187
|
+
"--filter-direction",
|
|
188
|
+
type=click.Choice(["tx", "rx"]),
|
|
189
|
+
help="Filter by direction",
|
|
190
|
+
)(func)
|
|
191
|
+
func = click.option(
|
|
192
|
+
"--filter-type",
|
|
193
|
+
type=click.Choice(["event", "system", "reply"]),
|
|
194
|
+
help="Filter by telegram type",
|
|
195
|
+
)(func)
|
|
196
|
+
return func
|
|
197
|
+
|
|
198
|
+
return decorator
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def with_formatter(
|
|
202
|
+
formatter_class: Any = None,
|
|
203
|
+
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
|
|
204
|
+
"""Inject a formatter instance into the command.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
formatter_class: Custom formatter class to use.
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
Decorator function that injects a formatter.
|
|
211
|
+
"""
|
|
212
|
+
|
|
213
|
+
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
214
|
+
"""Apply formatter injection.
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
func: The function to decorate.
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
Wrapped function with formatter injection.
|
|
221
|
+
"""
|
|
222
|
+
|
|
223
|
+
@functools.wraps(func)
|
|
224
|
+
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
225
|
+
"""Execute function with injected formatter.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
args: Positional arguments passed to the decorated function.
|
|
229
|
+
kwargs: Keyword arguments passed to the decorated function.
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
Result from the decorated function.
|
|
233
|
+
"""
|
|
234
|
+
formatter_cls = formatter_class or OutputFormatter
|
|
235
|
+
formatter = formatter_cls(True)
|
|
236
|
+
kwargs["formatter"] = formatter
|
|
237
|
+
return func(*args, **kwargs)
|
|
238
|
+
|
|
239
|
+
return wrapper
|
|
240
|
+
|
|
241
|
+
return decorator
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def require_arguments(
|
|
245
|
+
*required_args: str,
|
|
246
|
+
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
|
|
247
|
+
"""Validate required arguments are present.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
required_args: Names of required arguments.
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
Decorator function that validates required arguments.
|
|
254
|
+
"""
|
|
255
|
+
|
|
256
|
+
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
257
|
+
"""Apply argument validation.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
func: The function to decorate.
|
|
261
|
+
|
|
262
|
+
Returns:
|
|
263
|
+
Wrapped function with argument validation.
|
|
264
|
+
"""
|
|
265
|
+
|
|
266
|
+
@functools.wraps(func)
|
|
267
|
+
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
268
|
+
"""Execute function with argument validation.
|
|
269
|
+
|
|
270
|
+
Args:
|
|
271
|
+
args: Positional arguments passed to the decorated function.
|
|
272
|
+
kwargs: Keyword arguments passed to the decorated function.
|
|
273
|
+
|
|
274
|
+
Returns:
|
|
275
|
+
Result from the decorated function.
|
|
276
|
+
|
|
277
|
+
Raises:
|
|
278
|
+
SystemExit: When required arguments are missing.
|
|
279
|
+
"""
|
|
280
|
+
formatter = OutputFormatter(True)
|
|
281
|
+
|
|
282
|
+
# Check for missing required arguments
|
|
283
|
+
missing_args = [
|
|
284
|
+
arg_name
|
|
285
|
+
for arg_name in required_args
|
|
286
|
+
if arg_name in kwargs and kwargs[arg_name] is None
|
|
287
|
+
]
|
|
288
|
+
|
|
289
|
+
if missing_args:
|
|
290
|
+
error_msg = f"Missing required arguments: {', '.join(missing_args)}"
|
|
291
|
+
error_response = formatter.error_response(error_msg)
|
|
292
|
+
click.echo(error_response)
|
|
293
|
+
raise SystemExit(1)
|
|
294
|
+
|
|
295
|
+
return func(*args, **kwargs)
|
|
296
|
+
|
|
297
|
+
return wrapper
|
|
298
|
+
|
|
299
|
+
return decorator
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def connection_command() -> Callable[[Callable[..., Any]], Callable[..., Any]]:
|
|
303
|
+
"""Apply commands that connect to remote services.
|
|
304
|
+
|
|
305
|
+
Returns:
|
|
306
|
+
Decorator function for connection commands.
|
|
307
|
+
"""
|
|
308
|
+
|
|
309
|
+
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
310
|
+
"""Apply connection command decorators.
|
|
311
|
+
|
|
312
|
+
Args:
|
|
313
|
+
func: The function to decorate.
|
|
314
|
+
|
|
315
|
+
Returns:
|
|
316
|
+
Wrapped function with connection error handling.
|
|
317
|
+
"""
|
|
318
|
+
|
|
319
|
+
@functools.wraps(func)
|
|
320
|
+
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
321
|
+
"""Execute function with connection error handling.
|
|
322
|
+
|
|
323
|
+
Args:
|
|
324
|
+
args: Positional arguments passed to the decorated function.
|
|
325
|
+
kwargs: Keyword arguments passed to the decorated function.
|
|
326
|
+
|
|
327
|
+
Returns:
|
|
328
|
+
Result from the decorated function.
|
|
329
|
+
|
|
330
|
+
Raises:
|
|
331
|
+
SystemExit: When a connection timeout occurs.
|
|
332
|
+
Exception: Re-raises other exceptions for handling by other decorators.
|
|
333
|
+
"""
|
|
334
|
+
formatter = OutputFormatter(True)
|
|
335
|
+
|
|
336
|
+
try:
|
|
337
|
+
return func(*args, **kwargs)
|
|
338
|
+
except Exception as e:
|
|
339
|
+
if "Connection timeout" in str(e):
|
|
340
|
+
# Special handling for connection timeouts
|
|
341
|
+
error_msg = "Connection timeout - server may be unreachable"
|
|
342
|
+
error_response = formatter.error_response(error_msg)
|
|
343
|
+
click.echo(error_response)
|
|
344
|
+
raise SystemExit(1)
|
|
345
|
+
else:
|
|
346
|
+
# Re-raise other exceptions to be handled by other decorators
|
|
347
|
+
raise
|
|
348
|
+
|
|
349
|
+
return wrapper
|
|
350
|
+
|
|
351
|
+
return decorator
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"""Error handling utilities for CLI commands."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, Optional
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
|
|
7
|
+
from xp.cli.utils.formatters import OutputFormatter
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CLIErrorHandler:
|
|
11
|
+
"""Centralized error handling for CLI commands."""
|
|
12
|
+
|
|
13
|
+
@staticmethod
|
|
14
|
+
def handle_parsing_error(
|
|
15
|
+
error: Exception,
|
|
16
|
+
raw_input: str,
|
|
17
|
+
context: Optional[Dict[str, Any]] = None,
|
|
18
|
+
) -> None:
|
|
19
|
+
"""Handle telegram parsing errors with JSON formatting.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
error: The parsing error that occurred.
|
|
23
|
+
raw_input: The raw input that failed to parse.
|
|
24
|
+
context: Additional context information.
|
|
25
|
+
|
|
26
|
+
Raises:
|
|
27
|
+
SystemExit: Always exits with code 1 after displaying error.
|
|
28
|
+
"""
|
|
29
|
+
formatter = OutputFormatter(True)
|
|
30
|
+
error_data = {"raw_input": raw_input}
|
|
31
|
+
|
|
32
|
+
if context:
|
|
33
|
+
error_data.update(context)
|
|
34
|
+
|
|
35
|
+
error_response = formatter.error_response(str(error), error_data)
|
|
36
|
+
click.echo(error_response)
|
|
37
|
+
raise SystemExit(1)
|
|
38
|
+
|
|
39
|
+
@staticmethod
|
|
40
|
+
def handle_connection_error(
|
|
41
|
+
error: Exception, config: Optional[Dict[str, Any]] = None
|
|
42
|
+
) -> None:
|
|
43
|
+
"""Handle connection/network errors with JSON formatting.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
error: The connection error that occurred.
|
|
47
|
+
config: Configuration information (IP, port, timeout).
|
|
48
|
+
|
|
49
|
+
Raises:
|
|
50
|
+
SystemExit: Always exits with code 1 after displaying error.
|
|
51
|
+
"""
|
|
52
|
+
formatter = OutputFormatter(True)
|
|
53
|
+
|
|
54
|
+
if "Connection timeout" in str(error):
|
|
55
|
+
if config:
|
|
56
|
+
error_msg = f"Connection timeout after {config.get('timeout', 'unknown')} seconds"
|
|
57
|
+
error_data = {
|
|
58
|
+
"host": config.get("ip", "unknown"),
|
|
59
|
+
"port": config.get("port", "unknown"),
|
|
60
|
+
"timeout": config.get("timeout", "unknown"),
|
|
61
|
+
}
|
|
62
|
+
else:
|
|
63
|
+
error_msg = "Connection timeout"
|
|
64
|
+
error_data = {}
|
|
65
|
+
|
|
66
|
+
error_response = formatter.error_response(error_msg, error_data)
|
|
67
|
+
click.echo(error_response)
|
|
68
|
+
raise SystemExit(1)
|
|
69
|
+
else:
|
|
70
|
+
# Generic connection error
|
|
71
|
+
error_response = formatter.error_response(str(error))
|
|
72
|
+
click.echo(error_response)
|
|
73
|
+
raise SystemExit(1)
|
|
74
|
+
|
|
75
|
+
@staticmethod
|
|
76
|
+
def handle_service_error(
|
|
77
|
+
error: Exception, operation: str, context: Optional[Dict[str, Any]] = None
|
|
78
|
+
) -> None:
|
|
79
|
+
"""Handle general service errors with JSON formatting.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
error: The service error that occurred.
|
|
83
|
+
operation: Description of the operation that failed.
|
|
84
|
+
context: Additional context information.
|
|
85
|
+
|
|
86
|
+
Raises:
|
|
87
|
+
SystemExit: Always exits with code 1 after displaying error.
|
|
88
|
+
"""
|
|
89
|
+
formatter = OutputFormatter(True)
|
|
90
|
+
error_data = {"operation": operation}
|
|
91
|
+
|
|
92
|
+
if context:
|
|
93
|
+
error_data.update(context)
|
|
94
|
+
|
|
95
|
+
error_response = formatter.error_response(str(error), error_data)
|
|
96
|
+
click.echo(error_response)
|
|
97
|
+
raise SystemExit(1)
|
|
98
|
+
|
|
99
|
+
@staticmethod
|
|
100
|
+
def handle_validation_error(error: Exception, input_data: str) -> None:
|
|
101
|
+
"""Handle validation errors with JSON formatting.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
error: The validation error that occurred.
|
|
105
|
+
input_data: The input that failed validation.
|
|
106
|
+
|
|
107
|
+
Raises:
|
|
108
|
+
SystemExit: Always exits with code 1 after displaying error.
|
|
109
|
+
"""
|
|
110
|
+
formatter = OutputFormatter(True)
|
|
111
|
+
error_data = {"valid_format": False, "raw_input": input_data}
|
|
112
|
+
|
|
113
|
+
error_response = formatter.error_response(str(error), error_data)
|
|
114
|
+
click.echo(error_response)
|
|
115
|
+
raise SystemExit(1)
|
|
116
|
+
|
|
117
|
+
@staticmethod
|
|
118
|
+
def handle_file_error(
|
|
119
|
+
error: Exception,
|
|
120
|
+
file_path: str,
|
|
121
|
+
operation: str = "processing",
|
|
122
|
+
) -> None:
|
|
123
|
+
"""Handle file operation errors with JSON formatting.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
error: The file error that occurred.
|
|
127
|
+
file_path: Path to the file that caused the error.
|
|
128
|
+
operation: Type of file operation (parsing, reading, etc.).
|
|
129
|
+
|
|
130
|
+
Raises:
|
|
131
|
+
SystemExit: Always exits with code 1 after displaying error.
|
|
132
|
+
"""
|
|
133
|
+
formatter = OutputFormatter(True)
|
|
134
|
+
error_data = {"file_path": file_path, "operation": operation}
|
|
135
|
+
|
|
136
|
+
error_response = formatter.error_response(str(error), error_data)
|
|
137
|
+
click.echo(error_response)
|
|
138
|
+
raise SystemExit(1)
|
|
139
|
+
|
|
140
|
+
@staticmethod
|
|
141
|
+
def handle_not_found_error(
|
|
142
|
+
error: Exception, item_type: str, identifier: str
|
|
143
|
+
) -> None:
|
|
144
|
+
"""Handle 'not found' errors with JSON formatting.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
error: The not found error that occurred.
|
|
148
|
+
item_type: Type of item that was not found.
|
|
149
|
+
identifier: Identifier used to search for the item.
|
|
150
|
+
|
|
151
|
+
Raises:
|
|
152
|
+
SystemExit: Always exits with code 1 after displaying error.
|
|
153
|
+
"""
|
|
154
|
+
formatter = OutputFormatter(True)
|
|
155
|
+
error_data = {"item_type": item_type, "identifier": identifier}
|
|
156
|
+
|
|
157
|
+
error_response = formatter.error_response(str(error), error_data)
|
|
158
|
+
click.echo(error_response)
|
|
159
|
+
raise SystemExit(1)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class ServerErrorHandler(CLIErrorHandler):
|
|
163
|
+
"""Specialized error handler for server operations."""
|
|
164
|
+
|
|
165
|
+
@staticmethod
|
|
166
|
+
def handle_server_startup_error(
|
|
167
|
+
error: Exception, port: int, config_path: str
|
|
168
|
+
) -> None:
|
|
169
|
+
"""Handle server startup errors with JSON formatting.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
error: The server startup error that occurred.
|
|
173
|
+
port: Port number the server attempted to use.
|
|
174
|
+
config_path: Path to the configuration file.
|
|
175
|
+
|
|
176
|
+
Raises:
|
|
177
|
+
SystemExit: Always exits with code 1 after displaying error.
|
|
178
|
+
"""
|
|
179
|
+
formatter = OutputFormatter(True)
|
|
180
|
+
error_data = {
|
|
181
|
+
"port": port,
|
|
182
|
+
"config": config_path,
|
|
183
|
+
"operation": "server_startup",
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
error_response = formatter.error_response(str(error), error_data)
|
|
187
|
+
click.echo(error_response)
|
|
188
|
+
raise SystemExit(1)
|
|
189
|
+
|
|
190
|
+
@staticmethod
|
|
191
|
+
def handle_server_not_running_error() -> None:
|
|
192
|
+
"""Handle errors when server is not running with JSON formatting.
|
|
193
|
+
|
|
194
|
+
Raises:
|
|
195
|
+
SystemExit: Always exits with code 1 after displaying error.
|
|
196
|
+
"""
|
|
197
|
+
error_response = OutputFormatter(True).error_response(
|
|
198
|
+
"No server is currently running"
|
|
199
|
+
)
|
|
200
|
+
click.echo(error_response)
|
|
201
|
+
raise SystemExit(1)
|