Chameleon Template Engine

repository·master·Indexed 41 days ago

https://github.com/malthe/chameleon

A 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.

Tokens
12.6K
Snippets
44
Records
61
Agent score
52%

What's inside Chameleon

  1. Overview of Chameleon template engine

    master
    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.
  2. Overview of TAL (Template Attribute Language) statements

    master

    The 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:

    1. tal:define (Variables are set first for use in subsequent statements)
    2. tal:switch (Switch conditions)
    3. tal:condition (Conditional inclusion)
    4. tal:repeat (Repetition)
    5. tal:case (Switch cases)
    6. tal:content or tal:replace (Content substitution)
    7. tal:omit-tag (Implied with tal:replace)
    8. tal:attributes (Attribute manipulation)

    Statement Reference

    StatementDescription
    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:content and tal:replace cannot be used on the same element.

  3. How the Macro Expansion Language (METAL) works

    master

    The 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 &mdash; ${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>
  4. Extend the expression engine with custom compilers

    master

    You can extend Chameleon's language by writing custom expression compilers. You can register these globally by modifying PageTemplate.expression_types or locally by subclassing a template class and overriding its expression_types attribute.

    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" />
  5. Use Chameleon language extensions

    master

    Chameleon 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:define and tal:repeat support 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 an AttributeError, Chameleon automatically attempts a dictionary lookup (obj['name']).
    • Switch Statements: Use tal:switch on a parent element and tal:case on 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>
  6. How the Template Attribute Language (TAL) works

    master

    The 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>
  7. How text insertion and escaping work in Chameleon

    master

    Chameleon 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 Markup utility 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>
  8. Declare and use namespace prefixes in templates

    master

    Chameleon 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:

    • tal
    • metal
    • i18n

    This allows you to use them immediately without boilerplate declaration.

    Data Attributes

    If the enable_data_attributes option is enabled, you can also use the data- 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>
  9. Understand the syntax of the Chameleon attribute language

    master

    Chameleon 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>
  10. Understand TALES expression syntax

    master

    TALES (Template Attribute Language Expression Syntax) is used to supply data to TAL and METAL attributes. An expression consists of an optional type_prefix followed by a colon and a string. If no prefix is provided, the default expression type is python.

    Syntax: Expression ::= [type_prefix ':'] String type_prefix ::= Name

    Examples:

    • 1 + 2 (Python expression)
    • string:Hello, ${view.user_name} (String expression)
    • import:os.path (Import expression)
    Expression ::= [type_prefix ':'] String
  11. Control markup comment processing

    master

    You 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 -->
  12. Understand differences between Chameleon and Zope Page Templates (ZPT)

    master

    If 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 render or call method are inserted directly into the template namespace. In ZPT, they are only available via the options dictionary.
      • ZPT style: <div tal:content="options/title" />
      • Chameleon style: <div tal:content="title" />
    • Special Symbols: The CONTEXTS symbol is not available in Chameleon.

    Note: For full ZPT compatibility, use the z3c.pt package.