0
# Exception Handling
1
2
Molecule provides a comprehensive exception system for handling errors during testing workflows, with specific exception types for different failure scenarios and detailed error reporting capabilities.
3
4
## Capabilities
5
6
### Core Exception Classes
7
8
Base exception classes for handling Molecule-specific errors and failures.
9
10
```python { .api }
11
class MoleculeError(Exception):
12
"""
13
Generic Molecule error.
14
15
Custom exception to handle scenario run failures with support
16
for exit codes, detailed messages, and warning aggregation.
17
18
Args:
19
message (str): The message to display about the problem
20
code (int): Exit code to use when exiting (default: 1)
21
warns (Sequence[WarningMessage]): Warnings about the problem to issue
22
23
Attributes:
24
code (int): Exit code for process termination
25
"""
26
27
def __init__(self, message="", code=1, warns=()):
28
"""Initialize MoleculeError with message, code, and warnings."""
29
30
class ScenarioFailureError(MoleculeError):
31
"""
32
Details about a scenario that failed.
33
34
Specialized exception for scenario execution failures,
35
inheriting all capabilities from MoleculeError.
36
"""
37
```
38
39
### Runtime Warning Classes
40
41
Warning classes for handling runtime issues and compatibility problems.
42
43
```python { .api }
44
class MoleculeRuntimeWarning(RuntimeWarning):
45
"""
46
A runtime warning used by Molecule and its plugins.
47
48
Base warning class for Molecule-specific runtime issues
49
that don't prevent execution but should be reported.
50
"""
51
52
class IncompatibleMoleculeRuntimeWarning(MoleculeRuntimeWarning):
53
"""
54
A warning noting an unsupported runtime environment.
55
56
Issued when Molecule detects compatibility issues with
57
the current runtime environment or dependencies.
58
"""
59
```
60
61
## Usage Examples
62
63
### Basic Exception Handling
64
65
```python
66
from molecule.exceptions import MoleculeError, ScenarioFailureError
67
68
try:
69
# Molecule operation that might fail
70
config = Config()
71
result = config.driver.status()
72
except MoleculeError as e:
73
print(f"Molecule error occurred: {e}")
74
print(f"Exit code: {e.code}")
75
# Handle the error appropriately
76
exit(e.code)
77
except ScenarioFailureError as e:
78
print(f"Scenario failed: {e}")
79
# Specific handling for scenario failures
80
exit(e.code)
81
```
82
83
### Raising Custom Molecule Errors
84
85
```python
86
from molecule.exceptions import MoleculeError
87
import warnings
88
89
def validate_configuration():
90
"""Validate Molecule configuration and raise errors if invalid."""
91
92
# Check for missing required files
93
if not os.path.exists("molecule.yml"):
94
raise MoleculeError(
95
message="molecule.yml configuration file not found",
96
code=4 # RC_SETUP_ERROR
97
)
98
99
# Check for compatibility issues
100
warning_msgs = []
101
if incompatible_version_detected():
102
warning = warnings.WarningMessage(
103
message="Incompatible version detected",
104
category=UserWarning,
105
filename=__file__,
106
lineno=0
107
)
108
warning_msgs.append(warning)
109
110
if warning_msgs:
111
raise MoleculeError(
112
message="Configuration validation failed",
113
code=1,
114
warns=warning_msgs
115
)
116
```
117
118
### Exception Handling in Custom Drivers
119
120
```python
121
from molecule.driver.base import Driver
122
from molecule.exceptions import MoleculeError
123
124
class CustomDriver(Driver):
125
def sanity_checks(self):
126
"""Validate driver prerequisites."""
127
128
# Check for required external tools
129
if not self._tool_available("custom-tool"):
130
raise MoleculeError(
131
message="custom-tool is required but not found in PATH",
132
code=4 # RC_SETUP_ERROR
133
)
134
135
# Check for valid configuration
136
if not self.options.get("api_key"):
137
raise MoleculeError(
138
message="API key is required for custom driver",
139
code=4
140
)
141
142
def _tool_available(self, tool_name):
143
"""Check if external tool is available."""
144
import subprocess
145
try:
146
subprocess.run([tool_name, "--version"],
147
capture_output=True, check=True)
148
return True
149
except (subprocess.CalledProcessError, FileNotFoundError):
150
return False
151
```
152
153
### Exception Handling in Custom Verifiers
154
155
```python
156
from molecule.verifier.base import Verifier
157
from molecule.exceptions import MoleculeError
158
159
class CustomVerifier(Verifier):
160
def execute(self, action_args):
161
"""Execute verification tests with error handling."""
162
163
try:
164
result = self._run_tests(action_args)
165
166
if result.returncode != 0:
167
raise ScenarioFailureError(
168
message=f"Verification tests failed: {result.stderr}",
169
code=result.returncode
170
)
171
172
return result
173
174
except FileNotFoundError as e:
175
raise MoleculeError(
176
message=f"Test runner not found: {e}",
177
code=4 # RC_SETUP_ERROR
178
)
179
except TimeoutError:
180
raise MoleculeError(
181
message="Verification tests timed out",
182
code=3 # RC_TIMEOUT
183
)
184
```
185
186
### Warning Handling
187
188
```python
189
import warnings
190
from molecule.api import MoleculeRuntimeWarning, IncompatibleMoleculeRuntimeWarning
191
192
def check_runtime_compatibility():
193
"""Check runtime environment and issue warnings."""
194
195
# Check Python version compatibility
196
import sys
197
if sys.version_info < (3, 10):
198
warnings.warn(
199
"Python version < 3.10 is not officially supported",
200
IncompatibleMoleculeRuntimeWarning,
201
stacklevel=2
202
)
203
204
# Check for deprecated features
205
if using_deprecated_feature():
206
warnings.warn(
207
"This feature is deprecated and will be removed in a future version",
208
MoleculeRuntimeWarning,
209
stacklevel=2
210
)
211
212
# Configure warning filters
213
warnings.filterwarnings("always", category=MoleculeRuntimeWarning)
214
warnings.filterwarnings("error", category=IncompatibleMoleculeRuntimeWarning)
215
```
216
217
### Error Code Constants
218
219
```python
220
from molecule.constants import (
221
RC_SUCCESS, # 0
222
RC_TIMEOUT, # 3
223
RC_SETUP_ERROR, # 4
224
RC_UNKNOWN_ERROR # 5
225
)
226
227
def handle_molecule_operation():
228
"""Handle Molecule operation with proper exit codes."""
229
230
try:
231
# Perform Molecule operation
232
result = run_molecule_test()
233
return RC_SUCCESS
234
235
except TimeoutError:
236
return RC_TIMEOUT
237
238
except MoleculeError as e:
239
if "setup" in str(e).lower():
240
return RC_SETUP_ERROR
241
return e.code
242
243
except Exception:
244
return RC_UNKNOWN_ERROR
245
```
246
247
## Integration Notes
248
249
- All Molecule exceptions inherit from `MoleculeError` for consistent error handling
250
- Exit codes follow standard conventions (0=success, >0=error)
251
- Warning messages are logged through the Molecule logging system
252
- Exception messages support rich formatting and highlighting
253
- Custom plugins should use appropriate exception types for consistency
254
- Error reporting includes context information for debugging
255
- Exception handling integrates with Click framework for CLI error display