0
# Exceptions
1
2
Comprehensive exception hierarchy for task failures, configuration errors, execution problems, and user input validation with detailed error reporting.
3
4
## Capabilities
5
6
### User Input and Configuration Errors
7
8
Exceptions raised for invalid user input, command line arguments, and configuration problems.
9
10
```python { .api }
11
class InvalidCommand(Exception):
12
"""
13
Invalid command line argument or command usage.
14
15
Raised when user provides invalid command line arguments, unknown commands,
16
or invalid task/target names. Provides contextual error messages.
17
"""
18
19
def __init__(self, *args, **kwargs):
20
"""
21
Initialize command error.
22
23
Args:
24
*args: Standard exception arguments
25
not_found (str, optional): Name of command/task/target not found
26
"""
27
self.not_found = kwargs.pop('not_found', None)
28
self.cmd_used = None # Set by CLI when command is known
29
self.bin_name = 'doit' # Binary name for error messages
30
31
def __str__(self):
32
"""Return formatted error message with usage hints"""
33
```
34
35
```python { .api }
36
class InvalidDodoFile(Exception):
37
"""
38
Invalid dodo file or dodo file loading error.
39
40
Raised when dodo file cannot be found, imported, or contains invalid
41
configuration or task definitions.
42
"""
43
```
44
45
```python { .api }
46
class InvalidTask(Exception):
47
"""
48
Invalid task instance or task definition.
49
50
Raised when task definition contains invalid attributes, missing required
51
fields, or violates task specification rules.
52
"""
53
```
54
55
### Task Execution Failures
56
57
Exception hierarchy for different types of task execution problems and failures.
58
59
```python { .api }
60
class BaseFail(CatchedException):
61
"""
62
Base class for task failures and errors.
63
64
Contains information about task failures including exception details,
65
traceback information, and reporting flags. Used to save failure info
66
and might contain a caught Exception.
67
"""
68
```
69
70
```python { .api }
71
class TaskFailed(BaseFail):
72
"""
73
Task execution was not successful.
74
75
Raised when a task's action execution fails (non-zero exit code for shell
76
commands, raised exception for Python actions, etc.). Indicates the task
77
itself failed, not an error in doit's execution.
78
"""
79
```
80
81
```python { .api }
82
class TaskError(BaseFail):
83
"""
84
Error while trying to execute task.
85
86
Raised when there's an error in doit's task execution machinery rather
87
than in the task action itself. Examples: missing dependencies,
88
action setup failures, internal doit errors.
89
"""
90
```
91
92
```python { .api }
93
class UnmetDependency(TaskError):
94
"""
95
Task was not executed because a dependent task failed or is ignored.
96
97
Raised when task cannot run due to dependency failures. Prevents execution
98
cascade when prerequisite tasks fail or are explicitly ignored.
99
"""
100
```
101
102
```python { .api }
103
class SetupError(TaskError):
104
"""
105
Error while trying to execute setup object.
106
107
Raised when task setup actions (executed before main actions) fail
108
or cannot be properly initialized and executed.
109
"""
110
```
111
112
```python { .api }
113
class DependencyError(TaskError):
114
"""
115
Error while checking if task is up-to-date or saving task status.
116
117
Raised when dependency checking fails due to file system errors,
118
database problems, or issues with uptodate checkers.
119
"""
120
```
121
122
### Legacy Exception Support
123
124
Deprecated exception classes maintained for backward compatibility.
125
126
```python { .api }
127
class CatchedException:
128
"""
129
DEPRECATED: Use BaseFail instead (deprecated 2022-04-22, 0.36.0 release).
130
131
Legacy base class for caught exceptions. Contains poor grammar and
132
doesn't handle all BaseFail cases properly. Maintained for compatibility.
133
"""
134
135
def __init__(self, msg, exception=None, report=True):
136
"""
137
Initialize caught exception.
138
139
Args:
140
msg (str): Error message
141
exception (Exception, optional): Original exception
142
report (bool): Whether failure should be reported
143
"""
144
self.message = msg
145
self.traceback = ''
146
self.report = report
147
148
def get_msg(self):
149
"""Return full exception description including traceback"""
150
151
def get_name(self):
152
"""Get failure kind name"""
153
154
def __str__(self):
155
"""Return formatted exception string"""
156
```
157
158
### Usage Examples
159
160
#### Command Line Error Handling
161
162
```python
163
from doit.exceptions import InvalidCommand
164
165
def validate_task_name(task_name, available_tasks):
166
"""Validate that task name exists"""
167
if task_name not in available_tasks:
168
raise InvalidCommand(
169
f"Task '{task_name}' not found",
170
not_found=task_name
171
)
172
173
# Error message will include helpful suggestions:
174
# "Invalid parameter: 'nonexistent'. Must be a command, task, or a target.
175
# Type 'doit help' to see available commands.
176
# Type 'doit list' to see available tasks."
177
```
178
179
#### Task Definition Validation
180
181
```python
182
from doit.exceptions import InvalidTask
183
184
def validate_task_definition(task_dict):
185
"""Validate task definition dictionary"""
186
if 'actions' not in task_dict and 'task_dep' not in task_dict:
187
raise InvalidTask(
188
"Task must contain either 'actions' or 'task_dep'"
189
)
190
191
if 'name' in task_dict and task_dict['name'].startswith('_'):
192
raise InvalidTask(
193
"Task names cannot start with underscore"
194
)
195
196
def task_invalid_example():
197
"""This will raise InvalidTask"""
198
return {
199
'name': '_private', # Invalid name
200
# Missing actions or task_dep
201
}
202
```
203
204
#### Action Failure Handling
205
206
```python
207
from doit.exceptions import TaskFailed, TaskError
208
209
def failing_action():
210
"""Action that might fail"""
211
import random
212
213
if random.random() < 0.5:
214
# Task logic failure
215
raise TaskFailed("Business logic failed")
216
else:
217
# System/infrastructure error
218
raise TaskError("Database connection failed")
219
220
def task_with_error_handling():
221
"""Task with custom error handling"""
222
def safe_action():
223
try:
224
# Some risky operation
225
result = risky_operation()
226
return result
227
except ValueError as e:
228
# Convert to doit exception
229
raise TaskFailed(f"Invalid input data: {e}")
230
except ConnectionError as e:
231
# Infrastructure error
232
raise TaskError(f"Service unavailable: {e}")
233
234
return {
235
'actions': [safe_action],
236
'verbosity': 2
237
}
238
```
239
240
#### Dependency Error Scenarios
241
242
```python
243
from doit.exceptions import UnmetDependency, DependencyError
244
245
def task_primary():
246
"""Primary task that might fail"""
247
return {
248
'actions': ['exit 1'], # Will fail
249
}
250
251
def task_dependent():
252
"""Task that depends on primary"""
253
return {
254
'actions': ['echo "This will not run"'],
255
'task_dep': ['primary'] # UnmetDependency if primary fails
256
}
257
258
def check_file_dependency():
259
"""Custom uptodate checker that might error"""
260
try:
261
import os
262
return os.path.exists('required_file.txt')
263
except PermissionError:
264
# File system access error
265
raise DependencyError("Cannot access dependency file")
266
267
def task_with_dep_check():
268
return {
269
'actions': ['echo "Running with dependency check"'],
270
'uptodate': [check_file_dependency]
271
}
272
```
273
274
#### Exception Information Access
275
276
```python
277
from doit.exceptions import BaseFail
278
279
def handle_task_failure(exception):
280
"""Handle task failure with detailed information"""
281
if isinstance(exception, BaseFail):
282
print(f"Failure type: {exception.get_name()}")
283
print(f"Message: {exception.message}")
284
285
if exception.traceback:
286
print("Traceback:")
287
print(''.join(exception.traceback))
288
289
if not exception.report:
290
print("(This failure was marked as non-reportable)")
291
292
return exception.get_msg() # Full description
293
```