UniCoreFW

template()

Process a template string with a context of variables.

Implementation

The template format supports variable interpolation with `<%= variable %>` and conditional statements with `<% if condition %>` and `<% endif %>`. Args: template_str: The template string to process context: Dictionary of variables to use in the template Returns: The processed template Raises: ValueError: If the template contains invalid syntax SecurityError: If potentially dangerous patterns are detected

Example

template("Hello, <%= name %>!", {"name": "John"})

Expected output: "Hello, John!"

Source Code

def template(template_str: str, context: Dict[str, Any]) -> str: # Validate inputs for security template_str = sanitize_string(template_str, max_length=10000) if not isinstance(context, dict): raise TypeError("Context must be a dictionary") # Validate context values for key, value in context.items(): if not isinstance(key, str): raise TypeError(f"Context key '{key}' must be a string") if callable(value): from .security import validate_callable validate_callable(value, f"context['{key}']") # Check for dangerous patterns dangerous_patterns = ( r"<%=.*?.__(class|bases|subclasses|globals|dict|code|builtins|module)__.*?%>" ) if re.search(dangerous_patterns, template_str): raise SecurityError("Potentially dangerous template pattern detected") # Define the token pattern token_pattern = r"(<%=?[^%]*?%>)" # Tokenize the template def tokenize(template_text: str) -> List[str]: """Split template into tokens.""" tokens = re.split(token_pattern, template_text) return tokens # Evaluate expressions in the template def evaluate_expression(expr: str, ctx: Dict[str, Any]) -> Any: """ Evaluate a template expression. Args: expr: The expression to evaluate ctx: The context dictionary Returns: The result of the expression Raises: ValueError: If the expression is invalid NameError: If a variable is not defined AttributeError: If an attribute is not found """ # Allow simple variable access and method calls with a strict pattern pattern = r"^([a-zA-Z_][a-zA-Z0-9_]*)(\.[a-zA-Z_][a-zA-Z0-9_]*(\(\))?)*$" if not re.match(pattern, expr): raise ValueError(f"Invalid expression: '{expr}'") parts = expr.split(".") value = ctx.get(parts[0], None) if value is None: raise NameError(f"Name '{parts[0]}' is not defined.") for part in parts[1:]: if part.endswith("()"): method_name = part[:-2] value = call_safe_method(value, method_name) else: if hasattr(value, part): value = getattr(value, part, None) else: raise AttributeError(f"Attribute '{part}' not found.") return value # Evaluate conditions in the template def evaluate_condition(condition: str, ctx: Dict[str, Any]) -> bool: """ Evaluate a template condition. Args: condition: The condition to evaluate ctx: The context dictionary Returns: True if the condition is truthy, False otherwise Raises: ValueError: If the condition is invalid """ # Allow simple variable truthiness checks pattern = r"^([a-zA-Z_][a-zA-Z0-9_]*)$" if not re.match(pattern, condition): raise ValueError(f"Invalid condition: '{condition}'") value = ctx.get(condition, None) return bool(value) # Call safe methods on objects def call_safe_method(obj: Any, method_name: str) -> Any: """ Call a safe method on an object. Args: obj: The object to call the method on method_name: The name of the method to call Returns: The result of the method call Raises: ValueError: If the method is not allowed """ # Only allow safe methods on strings safe_methods = {"upper", "lower", "title", "capitalize"} if isinstance(obj, str) and method_name in safe_methods: method = getattr(obj, method_name, None) if method is not None: return method() else: raise ValueError( f"Method '{method_name}' is not allowed on object of type '{type(obj).__name__}'." ) # Process the template tokens = tokenize(template_str) output = "" skip_stack = [] # Track conditional blocks idx = 0 while idx < len(tokens): token = tokens[idx] # Handle variable interpolation if token.startswith("<%=") and token.endswith("%>"): if not any(skip_stack): # Only process if not in a skipped block expr = token[3:-2].strip() value = evaluate_expression(expr, context) output += str(value) # Handle control statements elif token.startswith("<%") and token.endswith("%>"): tag_content = token[2:-2].strip() # if statement if tag_content.startswith("if "): condition = tag_content[3:].rstrip(":").strip() result = evaluate_condition(condition, context) skip_stack.append(not result) # endif statement elif tag_content == "endif": if skip_stack: skip_stack.pop() else: raise ValueError("Unmatched 'endif' found.") # unknown tag else: raise ValueError(f"Unknown tag '{tag_content}'.") # Regular text else: if not any(skip_stack): # Only add if not in a skipped block output += token idx += 1 # Check for unclosed conditional blocks if skip_stack: raise ValueError("Unclosed 'if' statement detected.") return output