Files
markitect-main/markitect/packaging/transclusion/directives.py
tegwick ec09fdd0bd
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
feat: complete Issue #150 - Advanced Packaging Features (.mdz, .mdt)
Implement comprehensive advanced packaging system using complete TDD8 methodology:

## Core Features Delivered
- **MDZ Format**: Self-contained ZIP packages with embedded assets and metadata
- **Transclusion Engine**: Dynamic content inclusion with variables and conditionals
- **Asset Management**: Automated discovery, integrity validation, and path rewriting
- **Variant Integration**: Seamless integration with existing explode-implode system

## Technical Implementation
- **53 comprehensive tests** with 100% coverage for new functionality
- **Circular import resolution** using lazy loading pattern in variant factory
- **Cross-platform compatibility** with proper path handling
- **Robust error handling** with specialized exception hierarchy

## Quality Assurance
-  All 1798 tests passing (100% system compatibility maintained)
-  Complete documentation (user guide + API reference)
-  Working demonstration script showcasing all features
-  Zero breaking changes to existing functionality

## Files Added/Modified
- **Core Implementation**: 17 new files (4,149+ lines)
- **Documentation**: Complete user and API documentation
- **Tests**: 53 new tests across 3 test modules
- **Integration**: Enhanced variant factory with MDZ support

Built on solid foundation from Issues #148-149. Production-ready with
comprehensive test coverage and full backward compatibility.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-13 23:09:18 +02:00

176 lines
5.2 KiB
Python

"""
Transclusion directive parsing.
Provides parsers and handlers for various transclusion directives
including file inclusion, variable substitution, and conditional content.
"""
import re
from typing import Dict, Any, Optional, Tuple, List
from dataclasses import dataclass
@dataclass
class Directive:
"""Represents a parsed transclusion directive."""
type: str
args: Dict[str, Any]
content: Optional[str] = None
start_pos: int = 0
end_pos: int = 0
class DirectiveParser:
"""
Parser for transclusion directives in markdown content.
Supports various directive types including file inclusion,
variable substitution, and conditional content processing.
"""
# Directive patterns
INCLUDE_PATTERN = re.compile(r'\{\{\s*include\s+"([^"]+)"\s*\}\}', re.IGNORECASE)
INCLUDE_WITH_ARGS_PATTERN = re.compile(
r'\{\{\s*include\s+"([^"]+)"\s+(.+?)\s*\}\}', re.IGNORECASE
)
VARIABLE_PATTERN = re.compile(r'\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}')
CONDITIONAL_BLOCK_PATTERN = re.compile(
r'\{\{\s*if\s+([^}]+)\s*\}\}(.*?)\{\{\s*endif\s*\}\}',
re.DOTALL | re.IGNORECASE
)
@classmethod
def parse_directives(cls, content: str) -> List[Directive]:
"""
Parse all directives from content.
Args:
content: Content to parse
Returns:
List of parsed directives
"""
directives = []
# Parse include directives with arguments
for match in cls.INCLUDE_WITH_ARGS_PATTERN.finditer(content):
file_path = match.group(1)
args_str = match.group(2)
args = cls._parse_directive_args(args_str)
args['file'] = file_path
directives.append(Directive(
type='include',
args=args,
start_pos=match.start(),
end_pos=match.end()
))
# Parse simple include directives
for match in cls.INCLUDE_PATTERN.finditer(content):
# Skip if already parsed as include with args
if any(d.start_pos <= match.start() < d.end_pos for d in directives):
continue
file_path = match.group(1)
directives.append(Directive(
type='include',
args={'file': file_path},
start_pos=match.start(),
end_pos=match.end()
))
# Parse variable references
for match in cls.VARIABLE_PATTERN.finditer(content):
# Skip if inside other directives
if any(d.start_pos <= match.start() < d.end_pos for d in directives):
continue
var_name = match.group(1)
directives.append(Directive(
type='variable',
args={'name': var_name},
start_pos=match.start(),
end_pos=match.end()
))
# Parse conditional blocks
for match in cls.CONDITIONAL_BLOCK_PATTERN.finditer(content):
condition = match.group(1)
block_content = match.group(2)
directives.append(Directive(
type='conditional',
args={'condition': condition},
content=block_content,
start_pos=match.start(),
end_pos=match.end()
))
# Sort by position to process in order
directives.sort(key=lambda d: d.start_pos)
return directives
@classmethod
def _parse_directive_args(cls, args_str: str) -> Dict[str, Any]:
"""
Parse directive arguments string.
Args:
args_str: Arguments string to parse
Returns:
Dictionary of parsed arguments
"""
args = {}
# Simple key=value parsing
for part in args_str.split():
if '=' in part:
key, value = part.split('=', 1)
# Remove quotes if present
if value.startswith('"') and value.endswith('"'):
value = value[1:-1]
elif value.startswith("'") and value.endswith("'"):
value = value[1:-1]
# Try to convert to appropriate type
if value.lower() in ('true', 'false'):
value = value.lower() == 'true'
elif value.isdigit():
value = int(value)
else:
try:
value = float(value)
except ValueError:
pass # Keep as string
args[key] = value
return args
@classmethod
def extract_file_includes(cls, content: str) -> List[str]:
"""
Extract all file paths from include directives.
Args:
content: Content to analyze
Returns:
List of file paths referenced in include directives
"""
files = []
# Extract from simple includes
for match in cls.INCLUDE_PATTERN.finditer(content):
files.append(match.group(1))
# Extract from includes with args
for match in cls.INCLUDE_WITH_ARGS_PATTERN.finditer(content):
files.append(match.group(1))
return files