A very fast and expressive template engine for Python applications
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Comprehensive exception hierarchy and debugging tools for template development including detailed error messages, source location tracking, and runtime debugging capabilities.
Jinja2 provides a comprehensive exception hierarchy for different types of template errors.
class TemplateError(Exception):
"""
Base class for all template-related errors.
Attributes:
message: Error message
lineno: Line number where error occurred (if available)
name: Template name where error occurred (if available)
filename: Template filename where error occurred (if available)
"""
class TemplateNotFound(TemplateError):
"""
Raised when a template cannot be found.
Attributes:
name: Template name that was not found
message: Error message
"""
class TemplatesNotFound(TemplateNotFound):
"""
Raised when multiple templates cannot be found (used by select_template).
Attributes:
names: List of template names that were not found
message: Error message including all attempted names
"""
class TemplateSyntaxError(TemplateError):
"""
Raised when template syntax is malformed.
Attributes:
message: Detailed syntax error message
lineno: Line number of syntax error
name: Template name
filename: Template filename
source: Template source code (if available)
"""
class TemplateRuntimeError(TemplateError):
"""
Raised during template execution for runtime errors.
Attributes:
message: Runtime error message
lineno: Line number where error occurred
name: Template name
filename: Template filename
"""
class TemplateAssertionError(TemplateRuntimeError):
"""
Raised when template assertion fails ({% assert %} tag).
Attributes:
message: Assertion error message
lineno: Line number of failed assertion
name: Template name
filename: Template filename
"""
class UndefinedError(TemplateRuntimeError):
"""
Raised when undefined variable is used inappropriately.
Attributes:
message: Undefined error message
name: Variable name that was undefined
exc_info: Original exception info (if available)
"""
class SecurityError(TemplateRuntimeError):
"""
Raised when template tries to perform insecure operations in sandbox mode.
Attributes:
message: Security violation description
lineno: Line number where violation occurred
name: Template name
filename: Template filename
"""
class FilterArgumentError(TemplateRuntimeError):
"""
Raised when filter is called with inappropriate arguments.
Attributes:
message: Argument error description
filter_name: Name of filter that failed (if available)
lineno: Line number where error occurred
name: Template name
filename: Template filename
"""Specialized undefined types that provide different behaviors for debugging and error handling.
class Undefined:
"""
Default undefined type that raises UndefinedError on most operations.
Methods:
_undefined_hint: Get hint about undefined variable
_undefined_obj: Get undefined object info
_undefined_name: Get undefined variable name
_undefined_exception: Get undefined exception type
"""
class StrictUndefined(Undefined):
"""
Undefined that raises errors on any operation including __str__ and __iter__.
More strict than default Undefined for catching undefined usage early.
"""
class DebugUndefined(Undefined):
"""
Undefined that shows debug information when printed instead of raising errors.
Useful for template development and debugging.
When rendered shows: {{ undefined value 'variable_name' }}
"""
class ChainableUndefined(Undefined):
"""
Undefined that doesn't raise errors on getattr/getitem operations.
Allows chaining operations on undefined values without immediate errors.
Example: {{ undefined_obj.attr.nested_attr }} won't raise until final rendering
"""
def make_logging_undefined(logger=None, base=None):
"""
Create an undefined class that logs undefined variable access.
Parameters:
logger: Logger instance to use (default: creates new logger)
base: Base undefined class (default: Undefined)
Returns:
class: Custom undefined class that logs access attempts
"""Usage examples:
from jinja2 import Environment, DebugUndefined, StrictUndefined
# Debug mode - shows undefined variables without errors
debug_env = Environment(undefined=DebugUndefined)
template = debug_env.from_string('Hello {{ name }}! Age: {{ user.age }}')
result = template.render()
# Result: Hello {{ undefined value 'name' }}! Age: {{ undefined value 'user' }}
# Strict mode - raises errors immediately
strict_env = Environment(undefined=StrictUndefined)
template = strict_env.from_string('Hello {{ name }}!')
try:
result = template.render() # Raises UndefinedError
except UndefinedError as e:
print(f'Undefined variable: {e}')
# Logging undefined access
import logging
logging.basicConfig(level=logging.WARNING)
LoggingUndefined = make_logging_undefined()
log_env = Environment(undefined=LoggingUndefined)
template = log_env.from_string('Hello {{ name }}!')
result = template.render() # Logs warning about undefined 'name'Tools for understanding and debugging template errors with detailed context information.
def get_template_module(env, template_name):
"""
Get template module for introspection and debugging.
Parameters:
env: Jinja2 environment
template_name: Name of template to introspect
Returns:
TemplateModule: Module with template exports and metadata
"""
def render_template_with_context(template, **context):
"""
Render template and return both result and execution context.
Parameters:
template: Template instance
**context: Template context variables
Returns:
tuple: (rendered_output, execution_context)
"""Handle template errors gracefully in production applications:
from jinja2 import Environment, TemplateNotFound, TemplateSyntaxError, TemplateRuntimeError
from jinja2 import FileSystemLoader, select_autoescape
import logging
logger = logging.getLogger(__name__)
class RobustTemplateRenderer:
def __init__(self):
self.env = Environment(
loader=FileSystemLoader('templates'),
autoescape=select_autoescape(),
undefined=ChainableUndefined # Don't fail on undefined variables
)
def render_template(self, template_name, fallback_template=None, **context):
"""
Render template with comprehensive error handling.
Parameters:
template_name: Primary template to render
fallback_template: Fallback template if primary fails
**context: Template context variables
Returns:
str: Rendered template or error message
"""
try:
template = self.env.get_template(template_name)
return template.render(**context)
except TemplateNotFound as e:
logger.error(f'Template not found: {e}')
if fallback_template:
try:
template = self.env.get_template(fallback_template)
return template.render(**context)
except Exception as fallback_error:
logger.error(f'Fallback template failed: {fallback_error}')
return f'<p>Template {template_name} not found</p>'
except TemplateSyntaxError as e:
logger.error(f'Template syntax error in {e.name} at line {e.lineno}: {e.message}')
return f'<p>Template syntax error in {template_name}</p>'
except TemplateRuntimeError as e:
logger.error(f'Template runtime error in {e.name} at line {e.lineno}: {e.message}')
return f'<p>Template runtime error in {template_name}</p>'
except Exception as e:
logger.error(f'Unexpected error rendering {template_name}: {e}')
return f'<p>Error rendering template {template_name}</p>'
# Usage
renderer = RobustTemplateRenderer()
result = renderer.render_template('user_profile.html', 'default_profile.html', user=user_data)Detailed error display for development environments:
from jinja2 import Environment, TemplateSyntaxError, TemplateRuntimeError
from jinja2 import DictLoader, DebugUndefined
import traceback
import html
class DevelopmentTemplateRenderer:
def __init__(self):
self.env = Environment(
loader=DictLoader({}),
undefined=DebugUndefined, # Show undefined variables
auto_reload=True # Reload templates on changes
)
def render_with_debug(self, template_source, **context):
"""
Render template with detailed debugging information.
Parameters:
template_source: Template source code
**context: Template context variables
Returns:
str: Rendered template or detailed error page
"""
try:
template = self.env.from_string(template_source)
return template.render(**context)
except TemplateSyntaxError as e:
return self._format_syntax_error(e, template_source)
except TemplateRuntimeError as e:
return self._format_runtime_error(e, template_source, context)
except Exception as e:
return self._format_generic_error(e, template_source, context)
def _format_syntax_error(self, error, source):
"""Format syntax error with source context."""
lines = source.split('\n')
error_line = error.lineno - 1 if error.lineno else 0
context_lines = []
for i, line in enumerate(lines):
line_num = i + 1
marker = ' >>> ' if i == error_line else ' '
context_lines.append(f'{marker}{line_num:3d}: {html.escape(line)}')
return f'''
<h2>Template Syntax Error</h2>
<p><strong>Error:</strong> {html.escape(str(error))}</p>
<p><strong>Line:</strong> {error.lineno}</p>
<h3>Template Source:</h3>
<pre>{"".join(context_lines)}</pre>
'''
def _format_runtime_error(self, error, source, context):
"""Format runtime error with context information."""
tb = traceback.format_exc()
return f'''
<h2>Template Runtime Error</h2>
<p><strong>Error:</strong> {html.escape(str(error))}</p>
<p><strong>Line:</strong> {error.lineno}</p>
<h3>Context Variables:</h3>
<pre>{html.escape(str(context))}</pre>
<h3>Template Source:</h3>
<pre>{html.escape(source)}</pre>
<h3>Full Traceback:</h3>
<pre>{html.escape(tb)}</pre>
'''
def _format_generic_error(self, error, source, context):
"""Format generic error with all available information."""
tb = traceback.format_exc()
return f'''
<h2>Template Error</h2>
<p><strong>Error Type:</strong> {type(error).__name__}</p>
<p><strong>Error:</strong> {html.escape(str(error))}</p>
<h3>Context Variables:</h3>
<pre>{html.escape(str(context))}</pre>
<h3>Template Source:</h3>
<pre>{html.escape(source)}</pre>
<h3>Full Traceback:</h3>
<pre>{html.escape(tb)}</pre>
'''
# Usage
dev_renderer = DevelopmentTemplateRenderer()
result = dev_renderer.render_with_debug(
'Hello {{ user.name }}! You have {{ user.messages | lenght }} messages.',
user={'name': 'Alice'}
)Validate templates before rendering to catch errors early:
from jinja2 import Environment, TemplateSyntaxError
from jinja2.meta import find_undeclared_variables
import ast
class TemplateValidator:
def __init__(self, env):
self.env = env
def validate_template(self, source, required_vars=None, available_vars=None):
"""
Validate template syntax and variable usage.
Parameters:
source: Template source code
required_vars: Set of variables that must be used
available_vars: Set of variables that are available
Returns:
dict: Validation results with errors and warnings
"""
results = {
'valid': True,
'errors': [],
'warnings': [],
'undeclared_vars': set(),
'syntax_ok': True
}
# Check syntax
try:
ast_tree = self.env.parse(source)
results['syntax_ok'] = True
except TemplateSyntaxError as e:
results['valid'] = False
results['syntax_ok'] = False
results['errors'].append(f'Syntax error at line {e.lineno}: {e.message}')
return results # Can't continue without valid syntax
# Find undeclared variables
try:
undeclared = find_undeclared_variables(ast_tree)
results['undeclared_vars'] = undeclared
# Check against available variables
if available_vars is not None:
missing_vars = undeclared - set(available_vars)
if missing_vars:
results['warnings'].extend([
f'Undeclared variable: {var}' for var in missing_vars
])
# Check required variables are used
if required_vars is not None:
unused_required = set(required_vars) - undeclared
if unused_required:
results['warnings'].extend([
f'Required variable not used: {var}' for var in unused_required
])
except Exception as e:
results['errors'].append(f'Variable analysis failed: {e}')
return results
def validate_template_file(self, template_name, **validation_kwargs):
"""Validate template loaded from file."""
try:
template = self.env.get_template(template_name)
return self.validate_template(template.source, **validation_kwargs)
except Exception as e:
return {
'valid': False,
'errors': [f'Failed to load template {template_name}: {e}'],
'warnings': [],
'undeclared_vars': set(),
'syntax_ok': False
}
# Usage
from jinja2 import FileSystemLoader
env = Environment(loader=FileSystemLoader('templates'))
validator = TemplateValidator(env)
# Validate template source
results = validator.validate_template(
'Hello {{ user.name }}! You have {{ user.message_count }} messages.',
available_vars=['user'],
required_vars=['user']
)
if results['valid']:
print('Template is valid')
else:
print('Template validation failed:')
for error in results['errors']:
print(f' ERROR: {error}')
for warning in results['warnings']:
print(f' WARNING: {warning}')class ErrorContext:
"""
Context information for template errors.
Attributes:
template_name: Name of template where error occurred
line_number: Line number of error
column_number: Column number of error (if available)
source_line: Source code line where error occurred
context_vars: Template context variables at time of error
stack_trace: Full stack trace information
"""
class DebugInfo:
"""
Debug information for template execution.
Attributes:
template_name: Template name
execution_time: Template rendering time
context_size: Size of template context
undefined_vars: List of undefined variables accessed
filter_calls: List of filters called during rendering
include_chain: Chain of included/extended templates
"""