0
# Module System
1
2
Interface for defining custom variables, macros, and filters in Python modules. Provides the primary extension mechanism for adding functionality to the template environment.
3
4
## Capabilities
5
6
### Primary Module Hook
7
8
The main entry point for defining template functionality in Python modules.
9
10
```python { .api }
11
def define_env(env):
12
"""
13
Primary hook for module definition.
14
15
Args:
16
env: MacrosPlugin instance with access to variables, macros, filters
17
18
This function is called during plugin initialization and must be present
19
in the module for it to be loaded successfully.
20
"""
21
```
22
23
#### Usage Examples
24
25
Basic module definition:
26
```python
27
# main.py
28
def define_env(env):
29
"""Define variables, macros, and filters"""
30
31
# Add variables
32
env.variables['version'] = '1.0.0'
33
env.variables['author'] = 'John Doe'
34
35
# Define macro using decorator
36
@env.macro
37
def greeting(name):
38
return f"Hello, {name}!"
39
40
# Define filter using decorator
41
@env.filter
42
def uppercase(text):
43
return text.upper()
44
45
# Direct registration with custom name
46
def calculate_discount(price, percent):
47
return price * (1 - percent / 100)
48
49
env.macro(calculate_discount, 'discount')
50
```
51
52
Advanced module with debugging:
53
```python
54
def define_env(env):
55
"""Advanced module with debug capabilities"""
56
57
# Enable debug output
58
chatter = env.start_chatting('MY_MODULE')
59
chatter("Module loading started")
60
61
# Access configuration
62
if env.conf.get('site_name'):
63
env.variables['site_title'] = env.conf['site_name'].upper()
64
65
# Complex macro with error handling
66
@env.macro
67
def include_file(filename, start_line=0, end_line=None):
68
"""Include file content with optional line range"""
69
try:
70
full_path = os.path.join(env.project_dir, filename)
71
with open(full_path, 'r') as f:
72
lines = f.readlines()
73
return ''.join(lines[start_line:end_line])
74
except FileNotFoundError:
75
return f"File not found: {filename}"
76
77
chatter("Module loaded successfully")
78
```
79
80
### Page Processing Hooks
81
82
Optional hooks for pre and post-processing pages during rendering.
83
84
```python { .api }
85
def on_pre_page_macros(env):
86
"""
87
Called before macro rendering for each page.
88
89
Args:
90
env: MacrosPlugin instance with page context available
91
92
The env.page and env.markdown properties are available.
93
Can modify env.markdown to alter page content before template rendering.
94
"""
95
96
def on_post_page_macros(env):
97
"""
98
Called after macro rendering for each page.
99
100
Args:
101
env: MacrosPlugin instance with page context available
102
103
The env.page and env.markdown properties are available.
104
Can modify env.markdown to alter final page content.
105
"""
106
```
107
108
#### Usage Examples
109
110
Adding dynamic content:
111
```python
112
def on_pre_page_macros(env):
113
"""Add dynamic header before processing"""
114
header = f"\\n## Page: {env.page.title}\\nLast built: {{{{ now() }}}}\\n"
115
env.markdown = header + env.markdown
116
117
def on_post_page_macros(env):
118
"""Add footer after processing"""
119
footer = f"\\n\\n---\\n*Generated on {env.page.file.src_path}*"
120
env.markdown += footer
121
```
122
123
Conditional processing:
124
```python
125
def on_pre_page_macros(env):
126
"""Conditional page modifications"""
127
# Only process API docs
128
if env.page.file.src_path.startswith('api/'):
129
env.markdown = "{% include 'api_header.md' %}\\n" + env.markdown
130
131
# Add warnings for draft pages
132
if 'draft' in env.page.meta:
133
warning = "!!! warning\\n This page is a draft\\n\\n"
134
env.markdown = warning + env.markdown
135
```
136
137
### Build Hook
138
139
Optional hook called after the entire site build is complete.
140
141
```python { .api }
142
def on_post_build(env):
143
"""
144
Called after the complete site build.
145
146
Args:
147
env: MacrosPlugin instance
148
149
No page context available. Used for cleanup, file generation,
150
or other post-build operations.
151
"""
152
```
153
154
#### Usage Examples
155
156
Generate additional files:
157
```python
158
def on_post_build(env):
159
"""Generate sitemap or other files"""
160
import json
161
162
# Generate metadata file
163
metadata = {
164
'build_time': str(env.variables.get('now', 'unknown')),
165
'site_name': env.conf.get('site_name'),
166
'pages': len(env.variables.get('navigation', {}).get('pages', []))
167
}
168
169
output_path = os.path.join(env.project_dir, 'site', 'metadata.json')
170
with open(output_path, 'w') as f:
171
json.dump(metadata, f, indent=2)
172
```
173
174
Cleanup operations:
175
```python
176
def on_post_build(env):
177
"""Post-build cleanup"""
178
temp_dir = os.path.join(env.project_dir, 'temp_build_files')
179
if os.path.exists(temp_dir):
180
shutil.rmtree(temp_dir)
181
```
182
183
### Legacy Module Hook
184
185
Deprecated but still supported module definition interface.
186
187
```python { .api }
188
def declare_variables(variables, macro):
189
"""
190
Legacy hook for module definition (deprecated).
191
192
Args:
193
variables: Dictionary to add variables to
194
macro: Decorator function for registering macros
195
196
Prefer define_env() for new modules.
197
"""
198
```
199
200
#### Usage Example
201
202
```python
203
def declare_variables(variables, macro):
204
"""Legacy module definition (prefer define_env)"""
205
206
variables['legacy_var'] = 'value'
207
208
@macro
209
def legacy_function():
210
return "This uses the old API"
211
```
212
213
## Module Loading
214
215
### Local Modules
216
217
Modules are loaded from the project directory:
218
219
- **File module**: `main.py` in project root
220
- **Package module**: `main/` directory with `__init__.py`
221
- **Custom name**: Set `module_name` in configuration
222
223
### Pluglet Modules
224
225
Pre-installed modules distributed via PyPI:
226
227
```yaml
228
plugins:
229
- macros:
230
modules:
231
- package_name # Import as package_name
232
- source_name:import_name # Install source_name, import as import_name
233
```
234
235
The plugin will automatically install missing packages using pip.
236
237
### Module Discovery
238
239
The plugin looks for these functions in order:
240
1. `define_env(env)` - Primary hook (required)
241
2. `on_pre_page_macros(env)` - Optional pre-processing
242
3. `on_post_page_macros(env)` - Optional post-processing
243
4. `on_post_build(env)` - Optional build completion
244
245
At least `define_env` must be present for the module to load successfully.
246
247
## Error Handling
248
249
### Module Loading Errors
250
251
```python
252
# Module not found
253
raise ImportError("Macro plugin could not find custom 'module_name' module")
254
255
# No standard functions found
256
raise NameError("None of the standard functions was found in module")
257
258
# Function registration conflicts
259
raise KeyError("Registration error: macro 'name' already exists")
260
```
261
262
### Best Practices
263
264
1. **Always implement `define_env`** - Required for module loading
265
2. **Use descriptive names** - Clear macro and filter names
266
3. **Handle errors gracefully** - Return meaningful error messages
267
4. **Document your functions** - Include docstrings for better debugging
268
5. **Use env.start_chatting()** - For debug output when verbose mode is enabled
269
6. **Test with different configurations** - Ensure compatibility with various setups