0
# Error Handling and Debugging
1
2
Comprehensive exception hierarchy and debugging tools for template development including detailed error messages, source location tracking, and runtime debugging capabilities.
3
4
## Capabilities
5
6
### Exception Hierarchy
7
8
Jinja2 provides a comprehensive exception hierarchy for different types of template errors.
9
10
```python { .api }
11
class TemplateError(Exception):
12
"""
13
Base class for all template-related errors.
14
15
Attributes:
16
message: Error message
17
lineno: Line number where error occurred (if available)
18
name: Template name where error occurred (if available)
19
filename: Template filename where error occurred (if available)
20
"""
21
22
class TemplateNotFound(TemplateError):
23
"""
24
Raised when a template cannot be found.
25
26
Attributes:
27
name: Template name that was not found
28
message: Error message
29
"""
30
31
class TemplatesNotFound(TemplateNotFound):
32
"""
33
Raised when multiple templates cannot be found (used by select_template).
34
35
Attributes:
36
names: List of template names that were not found
37
message: Error message including all attempted names
38
"""
39
40
class TemplateSyntaxError(TemplateError):
41
"""
42
Raised when template syntax is malformed.
43
44
Attributes:
45
message: Detailed syntax error message
46
lineno: Line number of syntax error
47
name: Template name
48
filename: Template filename
49
source: Template source code (if available)
50
"""
51
52
class TemplateRuntimeError(TemplateError):
53
"""
54
Raised during template execution for runtime errors.
55
56
Attributes:
57
message: Runtime error message
58
lineno: Line number where error occurred
59
name: Template name
60
filename: Template filename
61
"""
62
63
class TemplateAssertionError(TemplateRuntimeError):
64
"""
65
Raised when template assertion fails ({% assert %} tag).
66
67
Attributes:
68
message: Assertion error message
69
lineno: Line number of failed assertion
70
name: Template name
71
filename: Template filename
72
"""
73
74
class UndefinedError(TemplateRuntimeError):
75
"""
76
Raised when undefined variable is used inappropriately.
77
78
Attributes:
79
message: Undefined error message
80
name: Variable name that was undefined
81
exc_info: Original exception info (if available)
82
"""
83
84
class SecurityError(TemplateRuntimeError):
85
"""
86
Raised when template tries to perform insecure operations in sandbox mode.
87
88
Attributes:
89
message: Security violation description
90
lineno: Line number where violation occurred
91
name: Template name
92
filename: Template filename
93
"""
94
95
class FilterArgumentError(TemplateRuntimeError):
96
"""
97
Raised when filter is called with inappropriate arguments.
98
99
Attributes:
100
message: Argument error description
101
filter_name: Name of filter that failed (if available)
102
lineno: Line number where error occurred
103
name: Template name
104
filename: Template filename
105
"""
106
```
107
108
### Undefined Types for Debugging
109
110
Specialized undefined types that provide different behaviors for debugging and error handling.
111
112
```python { .api }
113
class Undefined:
114
"""
115
Default undefined type that raises UndefinedError on most operations.
116
117
Methods:
118
_undefined_hint: Get hint about undefined variable
119
_undefined_obj: Get undefined object info
120
_undefined_name: Get undefined variable name
121
_undefined_exception: Get undefined exception type
122
"""
123
124
class StrictUndefined(Undefined):
125
"""
126
Undefined that raises errors on any operation including __str__ and __iter__.
127
More strict than default Undefined for catching undefined usage early.
128
"""
129
130
class DebugUndefined(Undefined):
131
"""
132
Undefined that shows debug information when printed instead of raising errors.
133
Useful for template development and debugging.
134
135
When rendered shows: {{ undefined value 'variable_name' }}
136
"""
137
138
class ChainableUndefined(Undefined):
139
"""
140
Undefined that doesn't raise errors on getattr/getitem operations.
141
Allows chaining operations on undefined values without immediate errors.
142
143
Example: {{ undefined_obj.attr.nested_attr }} won't raise until final rendering
144
"""
145
146
def make_logging_undefined(logger=None, base=None):
147
"""
148
Create an undefined class that logs undefined variable access.
149
150
Parameters:
151
logger: Logger instance to use (default: creates new logger)
152
base: Base undefined class (default: Undefined)
153
154
Returns:
155
class: Custom undefined class that logs access attempts
156
"""
157
```
158
159
Usage examples:
160
161
```python
162
from jinja2 import Environment, DebugUndefined, StrictUndefined
163
164
# Debug mode - shows undefined variables without errors
165
debug_env = Environment(undefined=DebugUndefined)
166
template = debug_env.from_string('Hello {{ name }}! Age: {{ user.age }}')
167
result = template.render()
168
# Result: Hello {{ undefined value 'name' }}! Age: {{ undefined value 'user' }}
169
170
# Strict mode - raises errors immediately
171
strict_env = Environment(undefined=StrictUndefined)
172
template = strict_env.from_string('Hello {{ name }}!')
173
try:
174
result = template.render() # Raises UndefinedError
175
except UndefinedError as e:
176
print(f'Undefined variable: {e}')
177
178
# Logging undefined access
179
import logging
180
logging.basicConfig(level=logging.WARNING)
181
LoggingUndefined = make_logging_undefined()
182
log_env = Environment(undefined=LoggingUndefined)
183
template = log_env.from_string('Hello {{ name }}!')
184
result = template.render() # Logs warning about undefined 'name'
185
```
186
187
### Error Context and Debugging
188
189
Tools for understanding and debugging template errors with detailed context information.
190
191
```python { .api }
192
def get_template_module(env, template_name):
193
"""
194
Get template module for introspection and debugging.
195
196
Parameters:
197
env: Jinja2 environment
198
template_name: Name of template to introspect
199
200
Returns:
201
TemplateModule: Module with template exports and metadata
202
"""
203
204
def render_template_with_context(template, **context):
205
"""
206
Render template and return both result and execution context.
207
208
Parameters:
209
template: Template instance
210
**context: Template context variables
211
212
Returns:
213
tuple: (rendered_output, execution_context)
214
"""
215
```
216
217
## Error Handling Patterns
218
219
### Graceful Error Handling
220
221
Handle template errors gracefully in production applications:
222
223
```python
224
from jinja2 import Environment, TemplateNotFound, TemplateSyntaxError, TemplateRuntimeError
225
from jinja2 import FileSystemLoader, select_autoescape
226
import logging
227
228
logger = logging.getLogger(__name__)
229
230
class RobustTemplateRenderer:
231
def __init__(self):
232
self.env = Environment(
233
loader=FileSystemLoader('templates'),
234
autoescape=select_autoescape(),
235
undefined=ChainableUndefined # Don't fail on undefined variables
236
)
237
238
def render_template(self, template_name, fallback_template=None, **context):
239
"""
240
Render template with comprehensive error handling.
241
242
Parameters:
243
template_name: Primary template to render
244
fallback_template: Fallback template if primary fails
245
**context: Template context variables
246
247
Returns:
248
str: Rendered template or error message
249
"""
250
try:
251
template = self.env.get_template(template_name)
252
return template.render(**context)
253
254
except TemplateNotFound as e:
255
logger.error(f'Template not found: {e}')
256
if fallback_template:
257
try:
258
template = self.env.get_template(fallback_template)
259
return template.render(**context)
260
except Exception as fallback_error:
261
logger.error(f'Fallback template failed: {fallback_error}')
262
return f'<p>Template {template_name} not found</p>'
263
264
except TemplateSyntaxError as e:
265
logger.error(f'Template syntax error in {e.name} at line {e.lineno}: {e.message}')
266
return f'<p>Template syntax error in {template_name}</p>'
267
268
except TemplateRuntimeError as e:
269
logger.error(f'Template runtime error in {e.name} at line {e.lineno}: {e.message}')
270
return f'<p>Template runtime error in {template_name}</p>'
271
272
except Exception as e:
273
logger.error(f'Unexpected error rendering {template_name}: {e}')
274
return f'<p>Error rendering template {template_name}</p>'
275
276
# Usage
277
renderer = RobustTemplateRenderer()
278
result = renderer.render_template('user_profile.html', 'default_profile.html', user=user_data)
279
```
280
281
### Development Error Display
282
283
Detailed error display for development environments:
284
285
```python
286
from jinja2 import Environment, TemplateSyntaxError, TemplateRuntimeError
287
from jinja2 import DictLoader, DebugUndefined
288
import traceback
289
import html
290
291
class DevelopmentTemplateRenderer:
292
def __init__(self):
293
self.env = Environment(
294
loader=DictLoader({}),
295
undefined=DebugUndefined, # Show undefined variables
296
auto_reload=True # Reload templates on changes
297
)
298
299
def render_with_debug(self, template_source, **context):
300
"""
301
Render template with detailed debugging information.
302
303
Parameters:
304
template_source: Template source code
305
**context: Template context variables
306
307
Returns:
308
str: Rendered template or detailed error page
309
"""
310
try:
311
template = self.env.from_string(template_source)
312
return template.render(**context)
313
314
except TemplateSyntaxError as e:
315
return self._format_syntax_error(e, template_source)
316
317
except TemplateRuntimeError as e:
318
return self._format_runtime_error(e, template_source, context)
319
320
except Exception as e:
321
return self._format_generic_error(e, template_source, context)
322
323
def _format_syntax_error(self, error, source):
324
"""Format syntax error with source context."""
325
lines = source.split('\n')
326
error_line = error.lineno - 1 if error.lineno else 0
327
328
context_lines = []
329
for i, line in enumerate(lines):
330
line_num = i + 1
331
marker = ' >>> ' if i == error_line else ' '
332
context_lines.append(f'{marker}{line_num:3d}: {html.escape(line)}')
333
334
return f'''
335
<h2>Template Syntax Error</h2>
336
<p><strong>Error:</strong> {html.escape(str(error))}</p>
337
<p><strong>Line:</strong> {error.lineno}</p>
338
<h3>Template Source:</h3>
339
<pre>{"".join(context_lines)}</pre>
340
'''
341
342
def _format_runtime_error(self, error, source, context):
343
"""Format runtime error with context information."""
344
tb = traceback.format_exc()
345
346
return f'''
347
<h2>Template Runtime Error</h2>
348
<p><strong>Error:</strong> {html.escape(str(error))}</p>
349
<p><strong>Line:</strong> {error.lineno}</p>
350
<h3>Context Variables:</h3>
351
<pre>{html.escape(str(context))}</pre>
352
<h3>Template Source:</h3>
353
<pre>{html.escape(source)}</pre>
354
<h3>Full Traceback:</h3>
355
<pre>{html.escape(tb)}</pre>
356
'''
357
358
def _format_generic_error(self, error, source, context):
359
"""Format generic error with all available information."""
360
tb = traceback.format_exc()
361
362
return f'''
363
<h2>Template Error</h2>
364
<p><strong>Error Type:</strong> {type(error).__name__}</p>
365
<p><strong>Error:</strong> {html.escape(str(error))}</p>
366
<h3>Context Variables:</h3>
367
<pre>{html.escape(str(context))}</pre>
368
<h3>Template Source:</h3>
369
<pre>{html.escape(source)}</pre>
370
<h3>Full Traceback:</h3>
371
<pre>{html.escape(tb)}</pre>
372
'''
373
374
# Usage
375
dev_renderer = DevelopmentTemplateRenderer()
376
result = dev_renderer.render_with_debug(
377
'Hello {{ user.name }}! You have {{ user.messages | lenght }} messages.',
378
user={'name': 'Alice'}
379
)
380
```
381
382
### Template Validation
383
384
Validate templates before rendering to catch errors early:
385
386
```python
387
from jinja2 import Environment, TemplateSyntaxError
388
from jinja2.meta import find_undeclared_variables
389
import ast
390
391
class TemplateValidator:
392
def __init__(self, env):
393
self.env = env
394
395
def validate_template(self, source, required_vars=None, available_vars=None):
396
"""
397
Validate template syntax and variable usage.
398
399
Parameters:
400
source: Template source code
401
required_vars: Set of variables that must be used
402
available_vars: Set of variables that are available
403
404
Returns:
405
dict: Validation results with errors and warnings
406
"""
407
results = {
408
'valid': True,
409
'errors': [],
410
'warnings': [],
411
'undeclared_vars': set(),
412
'syntax_ok': True
413
}
414
415
# Check syntax
416
try:
417
ast_tree = self.env.parse(source)
418
results['syntax_ok'] = True
419
except TemplateSyntaxError as e:
420
results['valid'] = False
421
results['syntax_ok'] = False
422
results['errors'].append(f'Syntax error at line {e.lineno}: {e.message}')
423
return results # Can't continue without valid syntax
424
425
# Find undeclared variables
426
try:
427
undeclared = find_undeclared_variables(ast_tree)
428
results['undeclared_vars'] = undeclared
429
430
# Check against available variables
431
if available_vars is not None:
432
missing_vars = undeclared - set(available_vars)
433
if missing_vars:
434
results['warnings'].extend([
435
f'Undeclared variable: {var}' for var in missing_vars
436
])
437
438
# Check required variables are used
439
if required_vars is not None:
440
unused_required = set(required_vars) - undeclared
441
if unused_required:
442
results['warnings'].extend([
443
f'Required variable not used: {var}' for var in unused_required
444
])
445
446
except Exception as e:
447
results['errors'].append(f'Variable analysis failed: {e}')
448
449
return results
450
451
def validate_template_file(self, template_name, **validation_kwargs):
452
"""Validate template loaded from file."""
453
try:
454
template = self.env.get_template(template_name)
455
return self.validate_template(template.source, **validation_kwargs)
456
except Exception as e:
457
return {
458
'valid': False,
459
'errors': [f'Failed to load template {template_name}: {e}'],
460
'warnings': [],
461
'undeclared_vars': set(),
462
'syntax_ok': False
463
}
464
465
# Usage
466
from jinja2 import FileSystemLoader
467
468
env = Environment(loader=FileSystemLoader('templates'))
469
validator = TemplateValidator(env)
470
471
# Validate template source
472
results = validator.validate_template(
473
'Hello {{ user.name }}! You have {{ user.message_count }} messages.',
474
available_vars=['user'],
475
required_vars=['user']
476
)
477
478
if results['valid']:
479
print('Template is valid')
480
else:
481
print('Template validation failed:')
482
for error in results['errors']:
483
print(f' ERROR: {error}')
484
for warning in results['warnings']:
485
print(f' WARNING: {warning}')
486
```
487
488
## Types
489
490
```python { .api }
491
class ErrorContext:
492
"""
493
Context information for template errors.
494
495
Attributes:
496
template_name: Name of template where error occurred
497
line_number: Line number of error
498
column_number: Column number of error (if available)
499
source_line: Source code line where error occurred
500
context_vars: Template context variables at time of error
501
stack_trace: Full stack trace information
502
"""
503
504
class DebugInfo:
505
"""
506
Debug information for template execution.
507
508
Attributes:
509
template_name: Template name
510
execution_time: Template rendering time
511
context_size: Size of template context
512
undefined_vars: List of undefined variables accessed
513
filter_calls: List of filters called during rendering
514
include_chain: Chain of included/extended templates
515
"""
516
```