Unleash the power of MkDocs with macros and variables
—
Methods for integrating with other MkDocs plugins and external systems, including registration hooks and debugging utilities.
Methods for other MkDocs plugins to register macros, filters, and variables with the macros plugin.
class MacrosPlugin:
def register_macros(self, items: dict):
"""
Register macros from external plugins.
Args:
items: Dictionary of {name: function} pairs
Raises:
KeyError: If macro name already exists
"""
def register_filters(self, items: dict):
"""
Register filters from external plugins.
Args:
items: Dictionary of {name: function} pairs
Raises:
KeyError: If filter name already exists
"""
def register_variables(self, items: dict):
"""
Register variables from external plugins.
Args:
items: Dictionary of {name: value} pairs
Raises:
KeyError: If variable name already exists
"""From another MkDocs plugin:
class MyPlugin(BasePlugin):
def on_config(self, config):
# Find the macros plugin
for plugin in config.plugins:
if hasattr(plugin, 'register_macros'):
# Register our functions
plugin.register_macros({
'my_custom_macro': self.my_macro_function,
'another_macro': lambda x: f"Processed: {x}"
})
plugin.register_filters({
'my_filter': self.my_filter_function
})
plugin.register_variables({
'plugin_version': '1.0.0',
'plugin_config': self.config
})
break
def my_macro_function(self, text):
return f"<span class='highlight'>{text}</span>"
def my_filter_function(self, value):
return str(value).replace(' ', '_')Standalone registration utility:
def register_with_macros(config, macros_dict, filters_dict=None, variables_dict=None):
"""Utility to register items with macros plugin"""
for plugin in config.plugins:
if hasattr(plugin, 'register_macros'):
if macros_dict:
plugin.register_macros(macros_dict)
if filters_dict:
plugin.register_filters(filters_dict)
if variables_dict:
plugin.register_variables(variables_dict)
return True
return FalseMethods for creating debug output and logging functionality within macros.
class MacrosPlugin:
def start_chatting(self, prefix: str, color: str = 'yellow') -> callable:
"""
Create a debug function for verbose output.
Args:
prefix: Module/component name for log messages
color: Color for terminal output ('yellow', 'green', 'red', etc.)
Returns:
Chatter function that logs messages when verbose mode is enabled
"""Module with debug output:
def define_env(env):
# Create debug function
chatter = env.start_chatting('MY_MODULE', 'green')
chatter("Module initialization started")
@env.macro
def debug_macro(value):
chatter(f"Processing value: {value}")
result = str(value).upper()
chatter(f"Result: {result}")
return result
chatter("Module loaded successfully")Conditional debugging:
def define_env(env):
chatter = env.start_chatting('DATA_PROCESSOR')
@env.macro
def process_data(data_list):
chatter(f"Processing {len(data_list)} items")
results = []
for i, item in enumerate(data_list):
chatter(f"Processing item {i}: {item}")
processed = item.upper().strip()
results.append(processed)
chatter(f"Completed processing, {len(results)} results")
return resultsMulti-level debugging:
def define_env(env):
# Different debug levels with colors
info = env.start_chatting('INFO', 'blue')
warn = env.start_chatting('WARN', 'yellow')
error = env.start_chatting('ERROR', 'red')
@env.macro
def safe_operation(value):
info("Starting safe operation")
if not value:
warn("Empty value provided")
return "N/A"
try:
result = complex_processing(value)
info(f"Operation successful: {result}")
return result
except Exception as e:
error(f"Operation failed: {e}")
return "Error"Methods for working with file paths and determining rendering behavior.
class MacrosPlugin:
def force_page_rendering(self, filename: str) -> bool:
"""
Check if page should be force-rendered based on configuration.
Args:
filename: Page filename relative to docs directory
Returns:
True if page matches force_render_paths patterns
"""Conditional processing in hooks:
def on_pre_page_macros(env):
"""Apply different processing based on file patterns"""
current_file = env.page.file.src_path
# Check force rendering patterns
if env.force_page_rendering(current_file):
env.variables['force_rendered'] = True
# Apply special processing for forced files
env.markdown = "<!-- Force rendered -->\n" + env.markdown
# Different processing for different file types
if current_file.endswith('.api.md'):
env.variables['is_api_doc'] = True
elif current_file.startswith('examples/'):
env.variables['is_example'] = TrueFile-based configuration:
def define_env(env):
@env.macro
def include_if_forced(filename, content):
"""Only include content if file would be force-rendered"""
if env.force_page_rendering(filename):
return content
return ""
@env.macro
def get_render_status(filename):
"""Get rendering status for debugging"""
if env.force_page_rendering(filename):
return "Force rendered"
elif env.config['render_by_default']:
return "Default rendered"
else:
return "Opt-in only"# In another plugin
class MyPlugin(BasePlugin):
def on_config(self, config):
# Store reference to macros plugin
self.macros_plugin = None
for plugin in config.plugins:
if hasattr(plugin, 'register_macros'):
self.macros_plugin = plugin
break
def on_page_markdown(self, markdown, page, config, files):
# Use macros plugin for rendering
if self.macros_plugin:
# Add custom variables for this page
self.macros_plugin.register_variables({
'current_plugin': 'MyPlugin',
'processing_time': datetime.now()
})# Shared utility functions for multiple plugins
def create_shared_macros():
"""Create macros that multiple plugins can use"""
def format_code(code, language='python'):
return f"```{language}\n{code}\n```"
def create_alert(message, type='info'):
return f"!!! {type}\n {message}"
def embed_youtube(video_id):
return f'<iframe src="https://youtube.com/embed/{video_id}"></iframe>'
return {
'format_code': format_code,
'alert': create_alert,
'youtube': embed_youtube
}
# Register from multiple plugins
def register_shared_functionality(config):
shared_macros = create_shared_macros()
for plugin in config.plugins:
if hasattr(plugin, 'register_macros'):
plugin.register_macros(shared_macros)
breakclass IntegratedPlugin(BasePlugin):
config_scheme = (
('enable_macros', Type(bool, default=True)),
('macro_prefix', Type(str, default='plugin')),
)
def on_config(self, config):
if not self.config['enable_macros']:
return
# Find and configure macros plugin
for plugin in config.plugins:
if hasattr(plugin, 'register_macros'):
prefix = self.config['macro_prefix']
plugin.register_macros({
f'{prefix}_function': self.my_function,
f'{prefix}_utility': self.utility_function
})
plugin.register_variables({
f'{prefix}_config': dict(self.config),
f'{prefix}_enabled': True
})
breakdef safe_register_macros(plugin, macros_dict):
"""Safely register macros with conflict handling"""
for name, func in macros_dict.items():
try:
plugin.register_macros({name: func})
except KeyError as e:
print(f"Warning: Macro {name} already exists: {e}")
# Try with prefixed name
prefixed_name = f"ext_{name}"
try:
plugin.register_macros({prefixed_name: func})
print(f"Registered as {prefixed_name} instead")
except KeyError:
print(f"Could not register {name} even with prefix")def find_macros_plugin(config):
"""Find macros plugin in configuration"""
for plugin in config.plugins:
if hasattr(plugin, 'register_macros'):
return plugin
return None
def ensure_macros_plugin(config):
"""Ensure macros plugin is available"""
plugin = find_macros_plugin(config)
if not plugin:
raise RuntimeError("MkDocs Macros plugin not found in configuration")
return pluginInstall with Tessl CLI
npx tessl i tessl/pypi-mkdocs-macros-plugin