0
# Extensions and Custom Syntax
1
2
Extensible plugin system for adding custom template syntax, processing logic, and integration with external tools. Includes built-in extensions for internationalization, loop controls, expression statements, and debugging.
3
4
## Capabilities
5
6
### Extension Base Class
7
8
Base class for implementing custom template extensions with hooks for preprocessing, parsing, and code generation.
9
10
```python { .api }
11
class Extension:
12
def __init__(self, environment):
13
"""
14
Initialize extension.
15
16
Parameters:
17
environment: Jinja2 environment instance
18
"""
19
20
def bind(self, environment):
21
"""
22
Bind extension to different environment.
23
24
Parameters:
25
environment: New Jinja2 environment instance
26
27
Returns:
28
Extension: New extension instance bound to environment
29
"""
30
31
def preprocess(self, source, name, filename=None):
32
"""
33
Preprocess template source before parsing.
34
35
Parameters:
36
source: Template source code
37
name: Template name
38
filename: Template filename
39
40
Returns:
41
str: Preprocessed template source
42
"""
43
44
def filter_stream(self, stream):
45
"""
46
Filter token stream before parsing.
47
48
Parameters:
49
stream: Token stream iterator
50
51
Returns:
52
Iterator: Filtered token stream
53
"""
54
55
def parse(self, parser):
56
"""
57
Parse extension tags and return AST nodes.
58
59
Parameters:
60
parser: Jinja2 parser instance
61
62
Returns:
63
Node: AST node for extension functionality
64
"""
65
66
def attr(self, name, lineno=None):
67
"""
68
Create extension attribute node.
69
70
Parameters:
71
name: Attribute name
72
lineno: Line number for debugging
73
74
Returns:
75
Node: Attribute access AST node
76
"""
77
78
def call_method(self, name, args=None, kwargs=None, dyn_args=None, dyn_kwargs=None, lineno=None):
79
"""
80
Create extension method call node.
81
82
Parameters:
83
name: Method name
84
args: Method arguments
85
kwargs: Method keyword arguments
86
dyn_args: Dynamic arguments
87
dyn_kwargs: Dynamic keyword arguments
88
lineno: Line number for debugging
89
90
Returns:
91
Node: Method call AST node
92
"""
93
```
94
95
### Internationalization Extension
96
97
Built-in extension for internationalization and localization support using gettext-style translation functions.
98
99
```python { .api }
100
class InternationalizationExtension(Extension):
101
"""
102
Extension for internationalization support.
103
104
Adds:
105
- {% trans %}...{% endtrans %} blocks for translatable text
106
- {% pluralize %} for plural forms within trans blocks
107
- Automatic message extraction for translation tools
108
109
Usage:
110
env = Environment(extensions=['jinja2.ext.i18n'])
111
env.install_gettext_translations(translations)
112
"""
113
```
114
115
Usage example:
116
117
```python
118
from jinja2 import Environment
119
import gettext
120
121
# Setup translations
122
translations = gettext.translation('messages', 'locale', languages=['es'])
123
124
env = Environment(extensions=['jinja2.ext.i18n'])
125
env.install_gettext_translations(translations)
126
127
template_str = '''
128
{% trans %}Hello World!{% endtrans %}
129
{% trans count=items|length %}
130
There is {{ count }} item.
131
{% pluralize %}
132
There are {{ count }} items.
133
{% endtrans %}
134
'''
135
```
136
137
### Expression Statement Extension
138
139
Extension that adds the `{% do %}` tag for executing expressions without output.
140
141
```python { .api }
142
class ExprStmtExtension(Extension):
143
"""
144
Extension that adds {% do %} tag for expression statements.
145
146
Adds:
147
- {% do expression %} for executing expressions without output
148
- Useful for variable assignments and side effects
149
150
Usage:
151
env = Environment(extensions=['jinja2.ext.do'])
152
"""
153
```
154
155
Usage example:
156
157
```python
158
from jinja2 import Environment
159
160
env = Environment(extensions=['jinja2.ext.do'])
161
162
template_str = '''
163
{% set items = [] %}
164
{% do items.append('first') %}
165
{% do items.extend(['second', 'third']) %}
166
Items: {{ items | join(', ') }}
167
'''
168
```
169
170
### Loop Controls Extension
171
172
Extension that adds `{% break %}` and `{% continue %}` statements for loop control.
173
174
```python { .api }
175
class LoopControlsExtension(Extension):
176
"""
177
Extension that adds loop control statements.
178
179
Adds:
180
- {% break %} to exit loops early
181
- {% continue %} to skip to next loop iteration
182
183
Usage:
184
env = Environment(extensions=['jinja2.ext.loopcontrols'])
185
"""
186
```
187
188
Usage example:
189
190
```python
191
from jinja2 import Environment
192
193
env = Environment(extensions=['jinja2.ext.loopcontrols'])
194
195
template_str = '''
196
{% for item in items %}
197
{% if item is number and item < 0 %}
198
{% continue %}
199
{% endif %}
200
{% if item is string and item == 'STOP' %}
201
{% break %}
202
{% endif %}
203
Item: {{ item }}
204
{% endfor %}
205
'''
206
```
207
208
### Debug Extension
209
210
Extension that adds the `{% debug %}` tag for debugging template context and variables.
211
212
```python { .api }
213
class DebugExtension(Extension):
214
"""
215
Extension that adds {% debug %} tag for template debugging.
216
217
Adds:
218
- {% debug %} to print current template context
219
- Useful for development and troubleshooting
220
221
Usage:
222
env = Environment(extensions=['jinja2.ext.debug'])
223
"""
224
```
225
226
Usage example:
227
228
```python
229
from jinja2 import Environment
230
231
env = Environment(extensions=['jinja2.ext.debug'])
232
233
template_str = '''
234
User: {{ user.name }}
235
{% debug %}
236
Age: {{ user.age }}
237
'''
238
```
239
240
### Extension Loading
241
242
Extensions can be loaded by name or class when creating environments.
243
244
```python { .api }
245
# Load by string name
246
env = Environment(extensions=['jinja2.ext.i18n', 'jinja2.ext.do'])
247
248
# Load by class
249
from jinja2.ext import InternationalizationExtension, ExprStmtExtension
250
env = Environment(extensions=[InternationalizationExtension, ExprStmtExtension])
251
252
# Add after environment creation
253
env.add_extension('jinja2.ext.loopcontrols')
254
env.add_extension(DebugExtension)
255
```
256
257
## Custom Extension Development
258
259
### Basic Custom Extension
260
261
Example of creating a custom extension that adds a `{% cache %}` tag:
262
263
```python
264
from jinja2 import nodes
265
from jinja2.ext import Extension
266
267
class CacheExtension(Extension):
268
tags = {'cache'} # Tags handled by this extension
269
270
def parse(self, parser):
271
lineno = next(parser.stream).lineno
272
273
# Parse cache key
274
cache_key = parser.parse_expression()
275
276
# Parse optional timeout
277
timeout = None
278
if parser.stream.current.test('name:for'):
279
next(parser.stream) # consume 'for'
280
timeout = parser.parse_expression()
281
282
# Parse body
283
node = parser.parse_statements(['name:endcache'], drop_needle=True)
284
285
# Create cache node
286
return self.call_method('_cache_support',
287
[cache_key, timeout, nodes.List(node)],
288
lineno=lineno)
289
290
def _cache_support(self, cache_key, timeout, body):
291
"""Runtime cache support method."""
292
# Implementation would check cache, render if needed, store result
293
cache = getattr(self.environment, '_cache', {})
294
295
if cache_key in cache:
296
return cache[cache_key]
297
298
# Render body and cache result
299
result = ''.join(body)
300
cache[cache_key] = result
301
302
if not hasattr(self.environment, '_cache'):
303
self.environment._cache = cache
304
305
return result
306
307
# Usage
308
env = Environment(extensions=[CacheExtension])
309
template_str = '''
310
{% cache 'expensive_computation' for 300 %}
311
{{ expensive_function() }}
312
{% endcache %}
313
'''
314
```
315
316
### Advanced Extension Features
317
318
Extensions can modify parsing behavior, add global functions, and integrate with the compilation process:
319
320
```python
321
class AdvancedExtension(Extension):
322
def __init__(self, environment):
323
super().__init__(environment)
324
# Add global functions
325
environment.globals['custom_func'] = self.custom_function
326
327
def preprocess(self, source, name, filename=None):
328
# Modify source before parsing
329
return source.replace('[[', '{%').replace(']]', '%}')
330
331
def filter_stream(self, stream):
332
# Modify token stream
333
for token in stream:
334
if token.type == 'name' and token.value == 'old_keyword':
335
token = token._replace(value='new_keyword')
336
yield token
337
338
def custom_function(self, *args, **kwargs):
339
"""Custom global function available in templates."""
340
return f"Custom: {args} {kwargs}"
341
```
342
343
## Utility Functions
344
345
```python { .api }
346
def babel_extract(fileobj, keywords, comment_tags, options):
347
"""
348
Babel message extraction function for i18n extension.
349
350
Parameters:
351
fileobj: File-like object containing template source
352
keywords: Extraction keywords
353
comment_tags: Comment tags to extract
354
options: Extraction options
355
356
Returns:
357
Iterator: Extracted messages with line numbers and context
358
"""
359
```
360
361
## Types
362
363
```python { .api }
364
class ExtensionEnvironment:
365
"""
366
Extension execution environment providing access to parsing and compilation context.
367
368
Attributes:
369
environment: Jinja2 environment instance
370
parser: Current parser instance (during parsing)
371
compiler: Current compiler instance (during compilation)
372
"""
373
374
class ExtensionRegistry:
375
"""
376
Registry for managing loaded extensions and their capabilities.
377
378
Attributes:
379
extensions: Dictionary of loaded extension instances
380
tags: Set of all registered tag names
381
"""
382
```