A Python-Markdown extension which provides an 'include' function
npx @tessl/cli install tessl/pypi-markdown-include@0.8.0A Python-Markdown extension which provides an 'include' function, similar to that found in LaTeX (and also the C pre-processor and Fortran). This extension enables recursive file inclusion using the syntax {!filename!} and supports advanced features like line range selection, heading depth inheritance, configurable base paths, and custom file encoding.
pip install markdown-includeimport markdown
from markdown_include.include import MarkdownIncludeFor factory function usage:
from markdown_include.include import makeExtensionimport markdown
# Simple usage with default settings
html = markdown.markdown(source, extensions=['markdown_include.include'])import markdown
from markdown_include.include import MarkdownInclude
# Create extension with custom configuration
markdown_include = MarkdownInclude(
configs={
'base_path': '/srv/content/',
'encoding': 'iso-8859-1',
'inheritHeadingDepth': True,
'headingOffset': 1,
'throwException': False
}
)
html = markdown.markdown(source, extensions=[markdown_include])import markdown
from markdown_include.include import makeExtension
extension = makeExtension(base_path='/srv/content/', encoding='utf-8')
html = markdown.markdown(source, extensions=[extension])markdown_extensions:
- markdown_include.include:
base_path: docs
inheritHeadingDepth: true{!filename.md!}Includes the entire content of filename.md. The extension works recursively, so any include statements within the included file will also be processed.
{!filename.md!lines=1 3 8-10 2}Includes specific lines and ranges from the file:
1, 3, 28-10 (includes lines 8, 9, and 10)The main extension class that integrates with Python-Markdown.
class MarkdownInclude(Extension):
def __init__(self, configs={}):
"""
Initialize the markdown-include extension.
Parameters:
- configs (dict): Configuration dictionary with extension settings
"""
def extendMarkdown(self, md):
"""
Register the include preprocessor with the Markdown instance.
Parameters:
- md: Markdown instance to extend
"""The core processor that handles file inclusion syntax.
class IncludePreprocessor(Preprocessor):
def __init__(self, md, config):
"""
Initialize the include preprocessor.
Parameters:
- md: Markdown instance
- config (dict): Configuration dictionary from MarkdownInclude
"""
def run(self, lines):
"""
Process markdown lines to replace include statements with file contents.
Parameters:
- lines (list): List of markdown lines to process
Returns:
list: Processed lines with include statements replaced
"""Creates a MarkdownInclude extension instance.
def makeExtension(*args, **kwargs):
"""
Factory function to create MarkdownInclude extension instance.
Parameters:
- *args: Variable arguments (unused)
- **kwargs: Keyword arguments passed as configs to MarkdownInclude
Returns:
MarkdownInclude: Extension instance ready for use with Python-Markdown
"""All configuration options can be specified when initializing the extension:
# Configuration parameters for MarkdownInclude
configs = {
'base_path': str, # Default: "." - Base directory for relative paths
'encoding': str, # Default: "utf-8" - File encoding for included files
'inheritHeadingDepth': bool, # Default: False - Inherit heading depth from context
'headingOffset': int, # Default: 0 - Additional heading depth offset
'throwException': bool # Default: False - Whether to throw exceptions on file not found
}# Example with heading depth inheritance
markdown_include = MarkdownInclude(
configs={'inheritHeadingDepth': True, 'headingOffset': 1}
)Source document:
# Main Heading
{!included.md!}
## Sub Heading
{!included.md!}If included.md contains:
# Included Heading
Content here.Result with inheritHeadingDepth=True:
# Main Heading
## Included Heading
Content here.
## Sub Heading
### Included Heading
Content here.The extension supports flexible line selection:
{!large_file.md!lines=1-5 10 15-20}1-5 includes lines 1, 2, 3, 4, and 5# Exception mode - raises FileNotFoundError for missing files
strict_include = MarkdownInclude(configs={'throwException': True})
try:
html = markdown.markdown(source, extensions=[strict_include])
except FileNotFoundError as e:
print(f"Include file not found: {e}")
# Warning mode (default) - prints warnings and continues
normal_include = MarkdownInclude(configs={'throwException': False})
html = markdown.markdown(source, extensions=[normal_include])# Import types from markdown library
from markdown.extensions import Extension
from markdown.preprocessors import Preprocessor
# Regular expression patterns used internally
import re
INC_SYNTAX = re.compile(r"{!\s*(.+?)\s*!((\blines\b)=([0-9 -]+))?}")
HEADING_SYNTAX = re.compile("^#+")