Chameleon Template Engine
repository·master·Indexed 41 days ago
https://github.com/malthe/chameleonA high-performance HTML/XML template engine for Python that implements the Page Templates language. It features the Template Attribute Language (TAL) for document flow and repetition, the Macro Expansion Language (METAL) for generic templates with slots, and an Internationalization Language (I18N) based on gettext. Chameleon provides integration packages for frameworks including Pyramid, Django, Zope 2, Plone, Zope Toolkit (ZTK), and Grok.
What's inside Chameleon
- Chameleon is an HTML/XML template engine for Python that utilizes the page templates language. It is designed to be compatible with most Python applications and supports Python 3.9+ as well as PyPy 3.
Overview of TAL (Template Attribute Language) statements
masterThe Template Attribute Language (TAL) is used to create dynamic XML-like content by allowing elements to be replaced, repeated, or omitted.
Execution Order
When an element contains multiple TAL statements, they are executed in a specific order regardless of their written order in the tag:
tal:define(Variables are set first for use in subsequent statements)tal:switch(Switch conditions)tal:condition(Conditional inclusion)tal:repeat(Repetition)tal:case(Switch cases)tal:contentortal:replace(Content substitution)tal:omit-tag(Implied withtal:replace)tal:attributes(Attribute manipulation)
Statement Reference
Statement Description tal:defineDefine variables tal:switchDefines a switch condition tal:conditionInclude element only if expression is true tal:repeatRepeat an element tal:caseIncludes element only if expression is equal to parent switch tal:contentSubstitute the content of an element tal:replaceReplace the element with dynamic content tal:omit-tagOmit the element tags, leaving only the inner content tal:attributesDynamically change or insert element attributes tal:on-errorSubstitute the content of an element if processing fails Note:
tal:contentandtal:replacecannot be used on the same element.How the Macro Expansion Language (METAL) works
masterThe Macro Expansion Language (METAL) allows you to create generic templates with slots that can be filled in by other templates.
Key components:
metal:main: Defines a macro template.use-macro: Used by a calling template to load a macro (e.g.,use-macro="load: main.pt").metal:fill-slot: Used in the calling template to provide content for a specific slot.metal:content: Used in the macro template to define where the incoming content should be placed (e.g.,define-slot="content").
Note: The
load:expression type retrieves a template from the file system relative to the calling template.<!-- Macro Template --> <html xmlns="http://www.w3.org/1999/xhtml"> <metal:main use-macro="load: main.pt"> <head> <title>Example — ${document.title}</title> </head> <body> <h1>${document.title}</h1> <div id="content"> <metal:content define-slot="content" /> </div> </body> </metal:main> </html> <!-- Calling Template --> <metal:main use-macro="load: main.pt"> <head> <p metal:fill-slot="content">${structure: document.body}</p> </head> </metal:main>Extend the expression engine with custom compilers
masterYou can extend Chameleon's language by writing custom expression compilers. You can register these globally by modifying
PageTemplate.expression_typesor locally by subclassing a template class and overriding itsexpression_typesattribute.Custom compilers are typically implemented as closures that return a function accepting
(target, engine)and returning a list of AST nodes.import ast from chameleon import PageTemplate, PageTemplateFile from chameleon.tales import PythonExpr # 1. Define the compiler as a closure def uppercase_expression(string): def compiler(target, engine): uppercased = string.upper() value = ast.Constant(uppercased) return [ast.Assign(targets=[target], value=value)] return compiler # 2. Option A: Register globally PageTemplate.expression_types['upper'] = uppercase_expression # 3. Option B: Register via subclassing (preferred for isolation) class MyPageTemplateFile(PageTemplateFile): expression_types = { 'python': PythonExpr, 'upper': uppercase_expression } # Usage in template: <div tal:content="upper: hello, world" />Use Chameleon language extensions
masterChameleon provides several expression types and features to extend standard template languages:
New Expression Types
structure: <expr>: Wraps the result as structure (inserts raw HTML without escaping).import: <module>: Imports module globals.load: <template>: Loads templates relative to the current template.
Language Features
- Tuple Unpacking:
tal:defineandtal:repeatsupport tuple unpacking (e.g.,tal:define="(a, b, c) [1, 2, 3]"). Note that asterisk-based iterable unpacking is not supported. - Fallback Dictionary Lookup: If attribute lookup (
obj.name) raises anAttributeError, Chameleon automatically attempts a dictionary lookup (obj['name']). - Switch Statements: Use
tal:switchon a parent element andtal:caseon children to implement conditional logic. - Inline String Substitution: Use
${...}inside element attributes or text/tail content.
<!-- Structure expression --> <div>${structure: body.text}</div> <!-- Import expression --> <div tal:define="compile import: re.compile">...</div> <!-- Load expression --> <div tal:define="compile load: main.pt">...</div> <!-- Tuple unpacking --> <div tal:define="(a, b, c) [1, 2, 3]"></div> <!-- Switch statement --> <div tal:switch="status"> <div tal:case="'active'">Active</div> <div tal:case="'inactive'">Inactive</div> </div>How the Template Attribute Language (TAL) works
masterThe Template Attribute Language (TAL) allows you to control document flow, element repetition, and text replacement using special attributes within your HTML/XML structure.
Common constructs include:
tal:repeat: Used for element repetition (loops).tal:define: Used to define variables within the template.
Example of repetition:
<tr tal:repeat="row 'apple', 'banana', 'pineapple'"> <td tal:repeat="col 'juice', 'muffin', 'pie'"> ${row.capitalize()} ${col} </td> </tr><html> <body> <h1>Hello, ${'world'}!</h1> <table> <tr tal:repeat="row 'apple', 'banana', 'pineapple'"> <td tal:repeat="col 'juice', 'muffin', 'pie'"> ${row.capitalize()} ${col} </td> </tr> </table> </body> </html>How text insertion and escaping work in Chameleon
masterChameleon uses the
${...}notation for text insertion. The Python expression inside the braces is evaluated and the result is included in the output.By default, the string is escaped before insertion to prevent XSS. To bypass escaping and insert raw HTML (often called 'structure'), you can use the
structure:prefix or return an object that implements the__html__()method.To use the built-in
Markuputility for safe raw HTML insertion:from chameleon.utils import Markup username = Markup("<tt>%s</tt>" % username)<html> <body> <h1>Hello, ${'world'}!</h1> <div>${structure: <p>Raw HTML</p>}</div> </body> </html>Declare and use namespace prefixes in templates
masterChameleon uses XML namespaces to distinguish template instructions from standard HTML/XML attributes. While you can declare them explicitly at the top of your template, Chameleon provides sane defaults for common prefixes.
Explicit Declaration
You can declare namespaces manually using
xmlns:<html xmlns="http://www.w3.org/1999/xhtml" xmlns:tal="http://xml.zope.org/namespaces/tal" xmlns:metal="http://xml.zope.org/namespaces/metal" xmlns:i18n="http://xml.zope.org/namespaces/i18n"> </html>Default Prefixes
Chameleon automatically sets up default prefixes for:
talmetali18n
This allows you to use them immediately without boilerplate declaration.
Data Attributes
If the
enable_data_attributesoption is enabled, you can also use thedata-prefix for template commands (e.g.,data-tal-content="...").<html xmlns="http://www.w3.org/1999/xhtml"> <body> <p tal:content="text"> ... </p> </body> </html>Understand the syntax of the Chameleon attribute language
masterChameleon is an attribute language used to render XML or HTML documents. Instead of using separate script tags, statements are embedded directly into document tags as special attributes.
An attribute statement follows this pattern:
<tag namespace-prefix:command="argument"> ... </tag>- The attribute (
namespace-prefix:command="argument") is the actual instruction/statement. - The element (the entire tag) is the portion of the document the statement operates on.
Input must be a well-formed XML/HTML document. The output is typically XML-like but does not strictly have to be well-formed.
<p tal:content="text"> ... </p>- The attribute (
Understand TALES expression syntax
masterTALES (Template Attribute Language Expression Syntax) is used to supply data to TAL and METAL attributes. An expression consists of an optional
type_prefixfollowed by a colon and a string. If no prefix is provided, the default expression type ispython.Syntax:
Expression ::= [type_prefix ':'] Stringtype_prefix ::= NameExamples:
1 + 2(Python expression)string:Hello, ${view.user_name}(String expression)import:os.path(Import expression)
Expression ::= [type_prefix ':'] StringControl markup comment processing
masterYou can modify how HTML comments are handled using
!and?modifiers:- Drop (
<!--! ... -->): The comment is removed from the final output. - Verbatim (
<!--? ... -->): The comment is included in the output exactly as written, and${...}expression evaluation is disabled inside the comment.
<!--! This comment will be dropped from output --> <!--? This comment will be included verbatim -->- Drop (
Understand differences between Chameleon and Zope Page Templates (ZPT)
masterIf you are migrating from Zope Page Templates (ZPT), note these key differences in Chameleon:
- Default Expression Type: In Chameleon, the default expression type is Python.
- Template Arguments: Arguments passed via keyword to the
renderorcallmethod are inserted directly into the template namespace. In ZPT, they are only available via theoptionsdictionary.- ZPT style:
<div tal:content="options/title" /> - Chameleon style:
<div tal:content="title" />
- ZPT style:
- Special Symbols: The
CONTEXTSsymbol is not available in Chameleon.
Note: For full ZPT compatibility, use the
z3c.ptpackage.