0
# Exception Handling
1
2
Comprehensive exception hierarchy for handling various error conditions during AST building, inference, and analysis operations. Understanding these exceptions is crucial for robust astroid-based tools.
3
4
## Capabilities
5
6
### Core Exception Classes
7
8
Base exceptions that form the foundation of astroid's error handling system.
9
10
```python { .api }
11
class AstroidError(Exception):
12
"""Base class for all astroid exceptions."""
13
14
class AstroidBuildingError(AstroidError):
15
"""
16
Raised when AST building fails.
17
18
This includes failures to:
19
- Read source files
20
- Parse Python syntax
21
- Import modules
22
- Introspect live objects
23
"""
24
25
class AstroidSyntaxError(AstroidError):
26
"""
27
Raised when Python source code contains syntax errors.
28
29
Attributes:
30
- lineno: Line number of syntax error
31
- offset: Character offset of error
32
- text: Source line containing error
33
- filename: File containing the error
34
"""
35
36
class AstroidImportError(AstroidError):
37
"""
38
Raised when module imports fail.
39
40
This includes:
41
- Module not found
42
- Import permission errors
43
- Circular import detection
44
"""
45
```
46
47
### Type-Related Exceptions
48
49
Exceptions for type checking and validation errors.
50
51
```python { .api }
52
class AstroidTypeError(AstroidError):
53
"""
54
Raised when type-related operations fail.
55
56
Examples:
57
- Invalid type annotations
58
- Unsupported type operations
59
- Type constraint violations
60
"""
61
62
class AstroidValueError(AstroidError):
63
"""
64
Raised when value-related operations fail.
65
66
Examples:
67
- Invalid constant values
68
- Value conversion errors
69
- Range/boundary violations
70
"""
71
72
class AstroidIndexError(AstroidError):
73
"""
74
Raised when indexing operations fail.
75
76
Examples:
77
- List/tuple index out of range
78
- Invalid sequence access
79
- Dictionary key errors
80
"""
81
```
82
83
### Inference Exception Hierarchy
84
85
Specialized exceptions for the inference system.
86
87
```python { .api }
88
class InferenceError(AstroidError):
89
"""
90
Base class for all inference failures.
91
92
Inference can fail for many reasons:
93
- Unresolvable names
94
- Complex control flow
95
- Dynamic attribute access
96
- Runtime-dependent values
97
"""
98
99
class NameInferenceError(InferenceError):
100
"""
101
Raised when name lookup fails during inference.
102
103
Common causes:
104
- Undefined variables
105
- Names not in scope
106
- Import resolution failures
107
"""
108
109
class AttributeInferenceError(InferenceError):
110
"""
111
Raised when attribute access cannot be inferred.
112
113
Common causes:
114
- Dynamic attribute creation
115
- Descriptor protocol complexity
116
- Missing attribute definitions
117
"""
118
119
class InferenceOverwriteError(InferenceError):
120
"""
121
Raised when trying to overwrite existing inference tips.
122
123
This prevents accidental replacement of inference behavior
124
for built-in or previously registered functions.
125
"""
126
127
class UseInferenceDefault(InferenceError):
128
"""
129
Special exception to request fallback to default inference.
130
131
Used in custom inference tips to delegate back to
132
the standard inference mechanism.
133
"""
134
```
135
136
### MRO and Inheritance Exceptions
137
138
Exceptions related to method resolution order and class inheritance.
139
140
```python { .api }
141
class MroError(AstroidError):
142
"""
143
Base class for Method Resolution Order errors.
144
145
The MRO determines the order in which base classes
146
are searched for methods and attributes.
147
"""
148
149
class DuplicateBasesError(MroError):
150
"""
151
Raised when a class has duplicate base classes.
152
153
Example:
154
class Bad(Base, Base): # Duplicate base
155
pass
156
"""
157
158
class InconsistentMroError(MroError):
159
"""
160
Raised when MRO cannot be computed consistently.
161
162
This occurs with complex multiple inheritance
163
hierarchies that violate Python's MRO rules.
164
"""
165
166
class SuperError(AstroidError):
167
"""
168
Raised when super() calls cannot be resolved.
169
170
Common causes:
171
- Invalid super() usage
172
- Missing method in parent classes
173
- Complex MRO scenarios
174
"""
175
176
class SuperArgumentTypeError(SuperError):
177
"""
178
Raised when super() arguments have wrong types.
179
180
super() expects specific argument types depending
181
on the calling context.
182
"""
183
```
184
185
### Resolution and Lookup Exceptions
186
187
Exceptions for name and symbol resolution failures.
188
189
```python { .api }
190
class ResolveError(AstroidError):
191
"""
192
Raised when symbol resolution fails.
193
194
This covers various resolution failures beyond
195
simple name lookup.
196
"""
197
198
class NotFoundError(AttributeInferenceError):
199
"""
200
Alias for AttributeInferenceError.
201
202
Maintained for backward compatibility.
203
"""
204
205
class UnresolvableName(NameInferenceError):
206
"""
207
Alias for NameInferenceError.
208
209
Raised when a name cannot be resolved in any scope.
210
"""
211
```
212
213
### Structural and Validation Exceptions
214
215
Exceptions for AST structure and validation issues.
216
217
```python { .api }
218
class ParentMissingError(AstroidError):
219
"""
220
Raised when a node is missing its required parent.
221
222
Some operations require nodes to be properly
223
connected in the AST hierarchy.
224
"""
225
226
class StatementMissing(AstroidError):
227
"""
228
Raised when a required statement cannot be found.
229
230
Some analyses require specific statement types
231
to be present in the AST.
232
"""
233
234
class TooManyLevelsError(AstroidError):
235
"""
236
Raised when relative imports go too deep.
237
238
Example:
239
from ....module import something # Too many levels
240
"""
241
```
242
243
### Special Value Exceptions
244
245
Exceptions representing special states rather than errors.
246
247
```python { .api }
248
class NoDefault(AstroidError):
249
"""
250
Raised when no default value is available.
251
252
Used in contexts where a default value is requested
253
but none exists (e.g., function parameters).
254
"""
255
```
256
257
## Usage Examples
258
259
### Basic Exception Handling
260
261
```python
262
import astroid
263
264
# Handle building errors
265
try:
266
module = astroid.parse("invalid syntax here $$")
267
except astroid.AstroidSyntaxError as e:
268
print(f"Syntax error at line {e.lineno}: {e.msg}")
269
270
# Handle import errors
271
try:
272
module = astroid.MANAGER.ast_from_module_name("nonexistent_module")
273
except astroid.AstroidImportError as e:
274
print(f"Import failed: {e}")
275
276
# Handle inference errors
277
code = "x = unknown_variable"
278
module = astroid.parse(code)
279
name_node = next(module.nodes_of_class(astroid.Name))
280
281
try:
282
list(name_node.infer())
283
except astroid.NameInferenceError as e:
284
print(f"Cannot infer name: {e}")
285
```
286
287
### Specific Error Handling
288
289
```python
290
import astroid
291
292
# Handle attribute inference
293
code = '''
294
class MyClass:
295
pass
296
297
obj = MyClass()
298
value = obj.nonexistent_attr
299
'''
300
301
module = astroid.parse(code)
302
attr_node = next(module.nodes_of_class(astroid.Attribute))
303
304
try:
305
list(attr_node.infer())
306
except astroid.AttributeInferenceError as e:
307
print(f"Attribute not found: {e}")
308
except astroid.InferenceError as e:
309
print(f"General inference error: {e}")
310
```
311
312
### MRO Error Handling
313
314
```python
315
import astroid
316
317
# This would cause MRO issues in complex hierarchies
318
complex_code = '''
319
class A(object): pass
320
class B(A): pass
321
class C(A): pass
322
class D(B, C): pass # This is fine
323
'''
324
325
try:
326
module = astroid.parse(complex_code)
327
d_class = next(node for node in module.body if isinstance(node, astroid.ClassDef) and node.name == 'D')
328
mro = d_class.mro()
329
print(f"MRO: {[cls.name for cls in mro]}")
330
except astroid.MroError as e:
331
print(f"MRO computation failed: {e}")
332
```
333
334
### Safe Operations with Exception Handling
335
336
```python
337
import astroid
338
339
def safe_infer_name(node):
340
"""Safely infer a name node."""
341
try:
342
return list(node.infer())
343
except astroid.NameInferenceError:
344
return []
345
except astroid.InferenceError:
346
return []
347
348
def safe_get_attribute(node, attr_name):
349
"""Safely get an attribute from a node."""
350
try:
351
return node[attr_name]
352
except (KeyError, astroid.NotFoundError):
353
return None
354
355
# Usage
356
code = "x = some_function()"
357
module = astroid.parse(code)
358
359
for name in module.nodes_of_class(astroid.Name):
360
inferred = safe_infer_name(name)
361
print(f"Name {name.name}: {len(inferred)} inferences")
362
```
363
364
### Custom Exception Handling
365
366
```python
367
import astroid
368
369
class CustomAnalysisError(astroid.AstroidError):
370
"""Custom exception for analysis tools."""
371
pass
372
373
def analyze_function(func_node):
374
"""Analyze a function with custom error handling."""
375
if not isinstance(func_node, astroid.FunctionDef):
376
raise CustomAnalysisError("Expected function definition")
377
378
try:
379
# Analyze function body
380
if not func_node.body:
381
raise CustomAnalysisError("Function has no body")
382
383
# Check for return statements
384
returns = list(func_node.nodes_of_class(astroid.Return))
385
if not returns:
386
raise CustomAnalysisError("Function has no return statement")
387
388
except astroid.InferenceError as e:
389
raise CustomAnalysisError(f"Inference failed during analysis: {e}")
390
391
# Usage with proper error handling
392
code = '''
393
def empty_func():
394
pass
395
396
def good_func():
397
return 42
398
'''
399
400
module = astroid.parse(code)
401
for func in module.nodes_of_class(astroid.FunctionDef):
402
try:
403
analyze_function(func)
404
print(f"Function {func.name} analyzed successfully")
405
except CustomAnalysisError as e:
406
print(f"Analysis failed for {func.name}: {e}")
407
```
408
409
## Error Recovery Strategies
410
411
### Graceful Degradation
412
413
```python
414
import astroid
415
416
def robust_analysis(code):
417
"""Perform analysis with graceful error handling."""
418
try:
419
module = astroid.parse(code)
420
except astroid.AstroidSyntaxError:
421
return {"error": "syntax_error", "analysis": None}
422
except astroid.AstroidError as e:
423
return {"error": "parsing_error", "details": str(e)}
424
425
analysis_results = {}
426
427
# Safe function analysis
428
functions = []
429
for func in module.nodes_of_class(astroid.FunctionDef):
430
func_info = {"name": func.name, "args": len(func.args.args)}
431
try:
432
# Try to infer return type
433
returns = list(func.nodes_of_class(astroid.Return))
434
if returns:
435
inferred = astroid.safe_infer(returns[0].value)
436
func_info["return_type"] = type(inferred).__name__ if inferred else "unknown"
437
except Exception:
438
func_info["return_type"] = "error"
439
functions.append(func_info)
440
441
analysis_results["functions"] = functions
442
return {"error": None, "analysis": analysis_results}
443
```
444
445
### Exception Chaining
446
447
```python
448
import astroid
449
450
def detailed_error_reporting(code):
451
"""Provide detailed error information."""
452
try:
453
module = astroid.parse(code)
454
return module
455
except astroid.AstroidSyntaxError as e:
456
# Chain with more context
457
raise astroid.AstroidBuildingError(f"Syntax error in code: {e}") from e
458
except Exception as e:
459
# Wrap unexpected errors
460
raise astroid.AstroidError(f"Unexpected error during parsing: {e}") from e
461
```
462
463
## Best Practices
464
465
1. **Always handle AstroidError**: Use the base class to catch all astroid-related exceptions
466
2. **Use safe_infer() when appropriate**: Avoid exception handling for simple cases
467
3. **Provide fallback behavior**: Have default actions when operations fail
468
4. **Log errors appropriately**: Capture enough context for debugging
469
5. **Chain exceptions**: Preserve original error information when re-raising