CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-mkdocs-macros-plugin

Unleash the power of MkDocs with macros and variables

Pending
Overview
Eval results
Files

external-integration.mddocs/

External Integration

Methods for integrating with other MkDocs plugins and external systems, including registration hooks and debugging utilities.

Capabilities

Plugin Registration Hooks

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
        """

Usage Examples

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 False

Debug and Logging Utilities

Methods 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
        """

Usage Examples

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 results

Multi-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"

Path and File Utilities

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
        """

Usage Examples

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'] = True

File-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"

Integration Patterns

Plugin Communication

# 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 Functionality

# 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)
            break

Configuration Integration

class 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
                })
                break

Error Handling

Registration Conflicts

def 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")

Plugin Detection

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 plugin

Best Practices

  1. Check plugin availability before attempting registration
  2. Handle registration conflicts gracefully with prefixes or warnings
  3. Use descriptive prefixes for external functions to avoid naming conflicts
  4. Document integration requirements in your plugin documentation
  5. Test with different plugin load orders to ensure compatibility
  6. Use debug output to track registration and execution
  7. Provide fallbacks when macros plugin is not available

Install with Tessl CLI

npx tessl i tessl/pypi-mkdocs-macros-plugin

docs

built-in-context.md

built-in-functions.md

external-integration.md

index.md

module-system.md

plugin-configuration.md

template-environment.md

tile.json