0
# Exception Handling
1
2
Comprehensive exception hierarchy for precise error handling and debugging in CLI applications.
3
4
## Capabilities
5
6
### Base Exception
7
8
Root exception class for all Cyclopts errors.
9
10
```python { .api }
11
class CycloptsError(Exception):
12
"""Base exception class for all Cyclopts errors."""
13
14
def __init__(self, message: str = ""):
15
"""
16
Create a Cyclopts exception.
17
18
Parameters
19
----------
20
message
21
Error message
22
"""
23
```
24
25
### Argument and Parameter Errors
26
27
Exceptions related to argument parsing and validation.
28
29
```python { .api }
30
class ArgumentOrderError(CycloptsError):
31
"""Error when positional arguments are in wrong order."""
32
33
class MissingArgumentError(CycloptsError):
34
"""Error when required argument is missing."""
35
36
class MixedArgumentError(CycloptsError):
37
"""Error when incompatible argument types are mixed."""
38
39
class RepeatArgumentError(CycloptsError):
40
"""Error when an argument is repeated inappropriately."""
41
42
class UnknownOptionError(CycloptsError):
43
"""Error when an unknown option is provided."""
44
45
class UnusedCliTokensError(CycloptsError):
46
"""Error when CLI tokens are not consumed during parsing."""
47
```
48
49
### Type Conversion Errors
50
51
Exceptions related to type coercion and conversion.
52
53
```python { .api }
54
class CoercionError(CycloptsError):
55
"""Error when type coercion fails."""
56
57
def __init__(
58
self,
59
message: str = "",
60
*,
61
type_: type | None = None,
62
value: Any = None,
63
tokens: list[Token] | None = None
64
):
65
"""
66
Create a coercion error.
67
68
Parameters
69
----------
70
message
71
Error message
72
type_
73
Target type that failed conversion
74
value
75
Value that failed conversion
76
tokens
77
Tokens that caused the error
78
"""
79
```
80
81
### Validation Errors
82
83
Exceptions related to value validation.
84
85
```python { .api }
86
class ValidationError(CycloptsError):
87
"""Error during value validation."""
88
89
def __init__(
90
self,
91
message: str = "",
92
*,
93
value: Any = None,
94
validator: Callable | None = None
95
):
96
"""
97
Create a validation error.
98
99
Parameters
100
----------
101
message
102
Error message
103
value
104
Value that failed validation
105
validator
106
Validator that rejected the value
107
"""
108
```
109
110
### Command and Option Errors
111
112
Exceptions related to command configuration and option handling.
113
114
```python { .api }
115
class CommandCollisionError(CycloptsError):
116
"""Error when command names collide."""
117
118
class InvalidCommandError(CycloptsError):
119
"""Error when a command is invalid or malformed."""
120
121
class CombinedShortOptionError(CycloptsError):
122
"""Error when combined short options are invalid."""
123
```
124
125
### Documentation Errors
126
127
Exceptions related to docstring parsing and help generation.
128
129
```python { .api }
130
class DocstringError(CycloptsError):
131
"""Error parsing function docstrings."""
132
133
def __init__(
134
self,
135
message: str = "",
136
*,
137
func: Callable | None = None,
138
docstring: str | None = None
139
):
140
"""
141
Create a docstring error.
142
143
Parameters
144
----------
145
message
146
Error message
147
func
148
Function with problematic docstring
149
docstring
150
The problematic docstring content
151
"""
152
```
153
154
### Editor Errors
155
156
Exceptions related to interactive text editing.
157
158
```python { .api }
159
class EditorError(CycloptsError):
160
"""Base error for editor operations."""
161
162
class EditorNotFoundError(EditorError):
163
"""Error when text editor is not found."""
164
165
def __init__(self, editor: str | None = None):
166
"""
167
Create editor not found error.
168
169
Parameters
170
----------
171
editor
172
Editor command that was not found
173
"""
174
175
class EditorDidNotSaveError(EditorError):
176
"""Error when user did not save in editor."""
177
178
class EditorDidNotChangeError(EditorError):
179
"""Error when user did not change content in editor."""
180
```
181
182
## Usage Examples
183
184
### Handling Specific Exceptions
185
186
```python
187
from cyclopts import App, run
188
from cyclopts import ValidationError, CoercionError, MissingArgumentError
189
190
app = App()
191
192
@app.command
193
def process_data(input_file: str, threshold: float):
194
"""Process data with error handling."""
195
print(f"Processing {input_file} with threshold {threshold}")
196
197
def main():
198
try:
199
app()
200
except ValidationError as e:
201
print(f"Validation failed: {e}")
202
return 1
203
except CoercionError as e:
204
print(f"Type conversion failed: {e}")
205
return 1
206
except MissingArgumentError as e:
207
print(f"Required argument missing: {e}")
208
return 1
209
except Exception as e:
210
print(f"Unexpected error: {e}")
211
return 1
212
return 0
213
214
if __name__ == "__main__":
215
exit(main())
216
```
217
218
### Custom Exception Handling
219
220
```python
221
from cyclopts import App, CycloptsError
222
import logging
223
224
logging.basicConfig(level=logging.INFO)
225
logger = logging.getLogger(__name__)
226
227
app = App()
228
229
@app.command
230
def risky_operation(count: int):
231
"""Operation that might fail."""
232
if count < 0:
233
raise ValueError("Count must be positive")
234
print(f"Processing {count} items")
235
236
def main():
237
try:
238
app()
239
except CycloptsError as e:
240
# Handle all Cyclopts-specific errors
241
logger.error(f"CLI error: {e}")
242
return 1
243
except ValueError as e:
244
# Handle application-specific errors
245
logger.error(f"Application error: {e}")
246
return 2
247
except KeyboardInterrupt:
248
logger.info("Operation cancelled by user")
249
return 130
250
return 0
251
252
if __name__ == "__main__":
253
exit(main())
254
```
255
256
### Editor Error Handling
257
258
```python
259
from cyclopts import App, edit
260
from cyclopts import EditorError, EditorNotFoundError, EditorDidNotSaveError
261
262
app = App()
263
264
@app.command
265
def edit_message():
266
"""Edit message with comprehensive error handling."""
267
try:
268
content = edit(
269
text="Enter your message here...",
270
require_save=True,
271
require_change=True
272
)
273
print("Message created successfully:")
274
print(content)
275
276
except EditorNotFoundError as e:
277
print(f"No editor available: {e}")
278
print("Please set the EDITOR environment variable")
279
return 1
280
281
except EditorDidNotSaveError:
282
print("Editor session cancelled - no changes saved")
283
return 1
284
285
except EditorError as e:
286
print(f"Editor error: {e}")
287
return 1
288
289
return 0
290
```
291
292
### Validation Error with Context
293
294
```python
295
from cyclopts import App, Parameter
296
from cyclopts.validators import Number
297
from cyclopts import ValidationError
298
299
def validate_even_number(value: int) -> int:
300
"""Validate that number is even."""
301
if value % 2 != 0:
302
raise ValidationError(f"Value {value} must be even", value=value)
303
return value
304
305
app = App()
306
307
@app.command
308
def process_batch(
309
batch_size: int = Parameter(
310
validator=[Number(min=1, max=1000), validate_even_number],
311
help="Batch size (1-1000, must be even)"
312
)
313
):
314
"""Process data in batches."""
315
print(f"Processing with batch size {batch_size}")
316
317
def main():
318
try:
319
app()
320
except ValidationError as e:
321
print(f"Invalid input: {e}")
322
if hasattr(e, 'value') and e.value is not None:
323
print(f"Problematic value: {e.value}")
324
return 1
325
return 0
326
327
if __name__ == "__main__":
328
exit(main())
329
```
330
331
### Comprehensive Error Handler
332
333
```python
334
from cyclopts import App, CycloptsError
335
from cyclopts import (
336
ValidationError, CoercionError, MissingArgumentError,
337
CommandCollisionError, DocstringError
338
)
339
import sys
340
341
def handle_cyclopts_error(error: CycloptsError) -> int:
342
"""Handle Cyclopts errors with appropriate messages and exit codes."""
343
344
if isinstance(error, ValidationError):
345
print(f"❌ Validation Error: {error}", file=sys.stderr)
346
return 2
347
348
elif isinstance(error, CoercionError):
349
print(f"❌ Type Error: {error}", file=sys.stderr)
350
return 3
351
352
elif isinstance(error, MissingArgumentError):
353
print(f"❌ Missing Argument: {error}", file=sys.stderr)
354
return 4
355
356
elif isinstance(error, CommandCollisionError):
357
print(f"❌ Configuration Error: {error}", file=sys.stderr)
358
return 5
359
360
elif isinstance(error, DocstringError):
361
print(f"❌ Documentation Error: {error}", file=sys.stderr)
362
return 6
363
364
else:
365
print(f"❌ CLI Error: {error}", file=sys.stderr)
366
return 1
367
368
app = App()
369
370
@app.command
371
def example(value: int):
372
"""Example command."""
373
print(f"Value: {value}")
374
375
def main():
376
try:
377
app()
378
return 0
379
except CycloptsError as e:
380
return handle_cyclopts_error(e)
381
except KeyboardInterrupt:
382
print("\n⚠️ Operation cancelled", file=sys.stderr)
383
return 130
384
except Exception as e:
385
print(f"💥 Unexpected error: {e}", file=sys.stderr)
386
return 1
387
388
if __name__ == "__main__":
389
exit(main())
390
```