0
# Error Handling
1
2
Exception classes for handling various YAML processing errors with position information and detailed error messages. PyYAML provides a comprehensive error hierarchy for diagnosing YAML processing issues.
3
4
## Capabilities
5
6
### Base Error Classes
7
8
Foundation exception classes that provide basic error information and position tracking.
9
10
```python { .api }
11
class YAMLError(Exception):
12
"""
13
Base exception for all YAML-related errors.
14
15
All other YAML exceptions inherit from this class, allowing
16
for broad exception handling.
17
"""
18
19
class MarkedYAMLError(YAMLError):
20
"""
21
YAML error with position information for precise error location.
22
23
Attributes:
24
- context: str | None - Description of the error context
25
- context_mark: Mark | None - Position where context was detected
26
- problem: str | None - Description of the specific problem
27
- problem_mark: Mark | None - Position where problem was detected
28
- note: str | None - Additional information about the error
29
"""
30
```
31
32
### Position Tracking
33
34
Mark class for tracking positions in YAML streams with detailed location information.
35
36
```python { .api }
37
class Mark:
38
"""
39
Represents a position in a YAML stream for error reporting.
40
41
Attributes:
42
- name: str - Name of the input source (filename, '<string>', etc.)
43
- index: int - Character index from start of stream
44
- line: int - Line number (0-based)
45
- column: int - Column number (0-based)
46
- buffer: str | None - Buffer content around the position
47
- pointer: int - Pointer position in buffer
48
"""
49
50
def get_snippet(self, indent: int = 4, max_length: int = 75) -> str:
51
"""
52
Get a code snippet showing the error location.
53
54
Parameters:
55
- indent: Number of spaces to indent the snippet
56
- max_length: Maximum length of the snippet line
57
58
Returns:
59
- Formatted code snippet with error pointer
60
"""
61
```
62
63
### Specific Error Types
64
65
Specialized exception classes for different types of YAML processing errors.
66
67
```python { .api }
68
class ScannerError(MarkedYAMLError):
69
"""
70
Error during YAML token scanning phase.
71
72
Raised when the scanner encounters invalid YAML syntax
73
at the character level (e.g., invalid escape sequences,
74
unterminated strings).
75
"""
76
77
class ParserError(MarkedYAMLError):
78
"""
79
Error during YAML parsing phase.
80
81
Raised when the parser encounters invalid YAML structure
82
(e.g., malformed documents, invalid indentation).
83
"""
84
85
class ComposerError(MarkedYAMLError):
86
"""
87
Error during YAML composition phase.
88
89
Raised when the composer encounters issues with node
90
relationships (e.g., duplicate anchors, invalid aliases).
91
"""
92
93
class ConstructorError(MarkedYAMLError):
94
"""
95
Error during YAML construction phase.
96
97
Raised when the constructor cannot build Python objects
98
from YAML nodes (e.g., unknown tags, invalid values).
99
"""
100
101
class RepresenterError(YAMLError):
102
"""
103
Error during YAML representation phase.
104
105
Raised when the representer cannot convert Python objects
106
to YAML nodes (e.g., unrepresentable objects).
107
"""
108
109
class SerializerError(YAMLError):
110
"""
111
Error during YAML serialization phase.
112
113
Raised when the serializer encounters issues converting
114
nodes to events.
115
"""
116
117
class EmitterError(YAMLError):
118
"""
119
Error during YAML emission phase.
120
121
Raised when the emitter cannot convert events to YAML text
122
(e.g., encoding issues, stream write errors).
123
"""
124
125
class ReaderError(YAMLError):
126
"""
127
Error during YAML reading phase.
128
129
Raised when the reader encounters issues with the input stream
130
(e.g., encoding problems, unreadable characters).
131
132
Attributes:
133
- name: Any - Source name
134
- character: Any - Problem character
135
- position: Any - Character position
136
- encoding: Any - Stream encoding
137
- reason: Any - Error reason
138
"""
139
140
class ResolverError(YAMLError):
141
"""
142
Error during YAML tag resolution phase.
143
144
Raised when the resolver encounters issues determining
145
appropriate tags for values.
146
"""
147
```
148
149
## Usage Examples
150
151
### Basic Error Handling
152
153
```python
154
import yaml
155
156
def safe_load_yaml(yaml_string):
157
"""Load YAML with comprehensive error handling."""
158
try:
159
return yaml.safe_load(yaml_string)
160
except yaml.YAMLError as e:
161
print(f"YAML Error: {e}")
162
return None
163
164
# Test with invalid YAML
165
invalid_yaml = """
166
name: John
167
age: 30
168
items:
169
- item1
170
- item2
171
- nested_wrong # Invalid indentation
172
"""
173
174
result = safe_load_yaml(invalid_yaml)
175
```
176
177
### Detailed Error Information
178
179
```python
180
import yaml
181
182
def detailed_yaml_error_handler(yaml_string):
183
"""Handle YAML errors with detailed position information."""
184
try:
185
return yaml.safe_load(yaml_string)
186
except yaml.MarkedYAMLError as e:
187
print(f"YAML Error: {e}")
188
189
if e.context_mark:
190
print(f"Context at line {e.context_mark.line + 1}, "
191
f"column {e.context_mark.column + 1}")
192
193
if e.problem_mark:
194
print(f"Problem at line {e.problem_mark.line + 1}, "
195
f"column {e.problem_mark.column + 1}")
196
197
# Show code snippet with error location
198
snippet = e.problem_mark.get_snippet()
199
if snippet:
200
print("Code snippet:")
201
print(snippet)
202
203
if e.problem:
204
print(f"Problem: {e.problem}")
205
206
if e.context:
207
print(f"Context: {e.context}")
208
209
return None
210
except yaml.YAMLError as e:
211
print(f"General YAML Error: {e}")
212
return None
213
214
# Test with malformed YAML
215
malformed_yaml = """
216
name: "John Doe
217
age: 30
218
"""
219
220
result = detailed_yaml_error_handler(malformed_yaml)
221
```
222
223
### Specific Error Type Handling
224
225
```python
226
import yaml
227
228
def handle_specific_errors(yaml_string):
229
"""Handle different types of YAML errors specifically."""
230
try:
231
return yaml.safe_load(yaml_string)
232
233
except yaml.ScannerError as e:
234
print(f"Scanner Error - Invalid YAML syntax: {e}")
235
# Handle character-level syntax errors
236
237
except yaml.ParserError as e:
238
print(f"Parser Error - Invalid YAML structure: {e}")
239
# Handle structural errors
240
241
except yaml.ConstructorError as e:
242
print(f"Constructor Error - Cannot build objects: {e}")
243
# Handle object construction errors
244
245
except yaml.ComposerError as e:
246
print(f"Composer Error - Node relationship issues: {e}")
247
# Handle anchor/alias errors
248
249
except yaml.YAMLError as e:
250
print(f"Other YAML Error: {e}")
251
# Handle any other YAML errors
252
253
return None
254
255
# Test various error types
256
test_cases = [
257
# Scanner error - unterminated string
258
'name: "unterminated string',
259
260
# Parser error - invalid indentation
261
'''
262
items:
263
- item1
264
- item2 # Wrong indentation
265
''',
266
267
# Constructor error - unknown tag
268
'value: !!unknown_tag some_value',
269
270
# Composer error - duplicate anchor
271
'''
272
item1: &anchor value1
273
item2: &anchor value2 # Duplicate anchor
274
'''
275
]
276
277
for i, test_yaml in enumerate(test_cases):
278
print(f"\nTest case {i + 1}:")
279
handle_specific_errors(test_yaml)
280
```
281
282
### Error Recovery and Validation
283
284
```python
285
import yaml
286
287
def validate_yaml_file(filename):
288
"""Validate a YAML file and report any errors."""
289
try:
290
with open(filename, 'r', encoding='utf-8') as file:
291
documents = list(yaml.safe_load_all(file))
292
print(f"✓ Valid YAML file with {len(documents)} document(s)")
293
return documents
294
295
except FileNotFoundError:
296
print(f"✗ File not found: {filename}")
297
298
except yaml.ReaderError as e:
299
print(f"✗ Reading error: {e}")
300
print(f" Encoding: {e.encoding}")
301
print(f" Position: {e.position}")
302
303
except yaml.ScannerError as e:
304
print(f"✗ Scanner error at line {e.problem_mark.line + 1}: {e.problem}")
305
306
except yaml.ParserError as e:
307
print(f"✗ Parser error at line {e.problem_mark.line + 1}: {e.problem}")
308
309
except yaml.YAMLError as e:
310
print(f"✗ YAML error: {e}")
311
312
return None
313
314
def safe_yaml_merge(*yaml_strings):
315
"""Safely merge multiple YAML strings, skipping invalid ones."""
316
merged_data = {}
317
318
for i, yaml_str in enumerate(yaml_strings):
319
try:
320
data = yaml.safe_load(yaml_str)
321
if isinstance(data, dict):
322
merged_data.update(data)
323
print(f"✓ Merged YAML string {i + 1}")
324
else:
325
print(f"⚠ YAML string {i + 1} is not a mapping, skipped")
326
327
except yaml.YAMLError as e:
328
print(f"✗ YAML string {i + 1} error: {e}")
329
continue
330
331
return merged_data
332
333
# Usage examples
334
yaml_strings = [
335
'name: John\nage: 30', # Valid
336
'city: "New York\nstate: NY', # Invalid - unterminated string
337
'skills:\n - Python\n - YAML', # Valid
338
'invalid: !!bad_tag value' # Invalid - unknown tag
339
]
340
341
result = safe_yaml_merge(*yaml_strings)
342
print(f"Final merged data: {result}")
343
```
344
345
### Custom Error Handling for Applications
346
347
```python
348
import yaml
349
import logging
350
351
class YAMLConfigLoader:
352
"""Configuration loader with robust YAML error handling."""
353
354
def __init__(self, logger=None):
355
self.logger = logger or logging.getLogger(__name__)
356
357
def load_config(self, config_path, default_config=None):
358
"""Load configuration with fallback to defaults on errors."""
359
try:
360
with open(config_path, 'r') as file:
361
config = yaml.safe_load(file)
362
self.logger.info(f"Successfully loaded config from {config_path}")
363
return config
364
365
except FileNotFoundError:
366
self.logger.warning(f"Config file not found: {config_path}")
367
368
except yaml.YAMLError as e:
369
self.logger.error(f"YAML error in {config_path}: {e}")
370
371
# Log detailed error information for debugging
372
if hasattr(e, 'problem_mark') and e.problem_mark:
373
mark = e.problem_mark
374
self.logger.error(f"Error at line {mark.line + 1}, column {mark.column + 1}")
375
376
except Exception as e:
377
self.logger.error(f"Unexpected error loading {config_path}: {e}")
378
379
# Return default config if loading failed
380
if default_config is not None:
381
self.logger.info("Using default configuration")
382
return default_config
383
else:
384
raise RuntimeError(f"Could not load configuration from {config_path}")
385
386
# Usage
387
loader = YAMLConfigLoader()
388
config = loader.load_config(
389
'app_config.yaml',
390
default_config={'debug': False, 'port': 8080}
391
)
392
```