Skills
A skill is a package of structured files that teaches an AI coding agent how to work with a specific tool or framework. The skill below was generated by Great Docs from this project’s documentation. Install it in your agent and it will be able to run commands, edit configuration, write content, and troubleshoot problems without step-by-step guidance from you.
Any agent — install with npx:
npx skills add https://mkennedy.codes/docs/chameleon-partials/Codex / OpenCode
Tell the agent:
Fetch the skill file at https://mkennedy.codes/docs/chameleon-partials/skill.md and follow the instructions.Manual — download the skill file:
curl -O https://mkennedy.codes/docs/chameleon-partials/skill.mdOr browse the SKILL.md file.
SKILL.md
--- name: chameleon-partials description: > Simple reuse of partial HTML page templates in the Chameleon template language for Python web frameworks. Use when writing Python code that uses the chameleon_partials package. license: MIT compatibility: Requires Python >=3.9. --- # Chameleon Partials Simple reuse of partial HTML page templates in the Chameleon template language for Python web frameworks. ## Installation ```bash pip install chameleon-partials ``` ## When to use what | Need | Use | |------|-----| | Insert a reusable HTML fragment inside a Chameleon template | `${ render_partial('shared/partials/fragment.pt', key=value) }` | | Make render_partial available in every Pyramid template | `BeforeRender subscriber: event['render_partial'] = chameleon_partials.render_partial` | | Make render_partial available in other frameworks (FastAPI, plain WSGI, ...) | `return chameleon_partials.extend_model(model) from the view` | | Get a rendered fragment as a plain string from Python (tests, HTMX, email) | `render_partial(template_file, **model).html_text` | | Point tests or a second setup call at a different template folder | `register_extensions(folder, cache_init=False), or reset has_registered_extensions first` | ## API overview ### Setup Register chameleon_partials with Chameleon. Call this once at application startup. - `register_extensions`: Register chameleon_partials with Chameleon so partials can be rendered ### Rendering partials Render partial templates and expose render_partial to your view models. - `render_partial`: Render a partial template to an HTML fragment - `extend_model`: Add `render_partial` to a view model so templates can call it ### Exceptions Errors raised by the library. - `PartialsException`: Raised when chameleon_partials is configured or used incorrectly ## Gotchas 1. register_extensions() is one-shot by default: with cache_init=True, later calls after one successful registration are silent no-ops. Pass cache_init=False or reset chameleon_partials.has_registered_extensions to re-point it. 2. render_partial() paths are always relative to the registered template folder — never to the calling template — even when called from inside another partial. 3. PartialsException covers misuse only (rendering before registration, empty/non-directory folder, non-dict model). A missing template file raises Chameleon's ValueError, unwrapped. 4. render_partial() returns an HTML wrapper, not a str — Chameleon inserts it unescaped via __html__(); read .html_text for the raw markup. The HTML class is not exported in __all__. 5. The model name 'encoding' is reserved (render_partial passes encoding='utf-8' internally); 'translate', 'target_language', and 'repeat' have special meaning to Chameleon. 6. extend_model() unconditionally replaces any existing 'render_partial' key in the model; render_partial() itself only injects the key when it is missing. 7. Registration is process-wide module state and is not lock-guarded — call register_extensions() from single-threaded startup code, not from concurrent request handlers. ## Best practices - Call register_extensions(template_folder, auto_reload=dev_mode) exactly once at application startup, before any template renders; leave auto_reload=False in production. - In Pyramid, wire render_partial globally with a BeforeRender subscriber; in other frameworks, return chameleon_partials.extend_model(model) from each view. - Keep partials as bare HTML fragments under shared/partials/ and prefix METAL layouts with an underscore (shared/_layout.pt). - Build the folder path with (Path(__file__).parent / 'templates').as_posix() so it is package-relative and uses forward slashes on every platform. - In test fixtures, reset chameleon_partials.has_registered_extensions = False in teardown so the next test can register its own template folder. ## End-to-end wiring Register the template folder once at startup, then call `render_partial(...)` from inside any Chameleon template. Template paths are always relative to the registered folder — even when called from inside another partial. ```python # At application startup, before any template renders from pathlib import Path import chameleon_partials folder = (Path(__file__).parent / 'templates').as_posix() chameleon_partials.register_extensions(folder, auto_reload=True) # auto_reload for dev only ``` ```html <!-- templates/shared/partials/greeting.pt (a partial is a bare HTML fragment) --> <div class="greeting"> <h2>Hello, ${ name }!</h2> </div> ``` ```html <!-- Any page template: inline the fragment, passing its model as keyword arguments --> ${ render_partial('shared/partials/greeting.pt', name='Michael') } ``` The template needs `render_partial` in its model. Pyramid apps wire it globally with a `BeforeRender` subscriber; other frameworks (FastAPI, plain WSGI, ...) call `extend_model(model)` on the view-model dict before rendering: ```python # Pyramid: discovered by config.scan(); every template gets render_partial automatically from pyramid.events import subscriber, BeforeRender import chameleon_partials @subscriber(BeforeRender) def add_global(event): event['render_partial'] = chameleon_partials.render_partial ``` ```python # Any other framework: extend the view model per view model = dict(name='Michael') return chameleon_partials.extend_model(model) ``` Inside a partial, `render_partial` is injected into the model automatically, so partials can nest further partials with no extra wiring — nesting depth is unlimited. ## Chameleon template syntax (this is TAL, not Jinja) Chameleon templates are valid HTML/XML where directives live in `tal:` and `metal:` attributes. There is no `{% ... %}` or `{ ... }` — do not use Jinja/Django syntax. Interpolation uses `${ ... }` and may contain arbitrary Python expressions. ```html <h1>${ video.title }</h1> <div class="views">${ "{:,}".format(video.views) } views</div> <!-- Loop: render a partial per item --> <div class="video" tal:repeat="v videos"> ${ render_partial('shared/partials/video_square.pt', video=v) } </div> <!-- Condition (element omitted entirely when falsy) --> <img tal:condition="show_avatar" src="${ user.avatar_url }" /> ``` ## Shared layouts with METAL macros Partials compose with Chameleon's METAL layout inheritance — pages fill a layout's slot and call partials inside it: ```html <!-- templates/shared/_layout.pt --> <!DOCTYPE html metal:define-macro="layout"> <html lang="en"> <body> <div metal:define-slot="content">No content</div> </body> </html> ``` ```html <!-- templates/home/index.pt --> <div metal:use-macro="load: ../shared/_layout.pt"> <div metal:fill-slot="content"> <div tal:repeat="v videos"> ${ render_partial('shared/partials/video_square.pt', video=v) } </div> </div> </div> ``` Note the asymmetry: `metal:use-macro="load: ..."` paths are relative to the *current template file*, while `render_partial(...)` paths are always relative to the *registered template folder*. ## Project layout conventions ``` your_app/ ├── __init__.py # register_extensions() here, at startup ├── views/ │ └── partials_middleware.py # BeforeRender subscriber (Pyramid) └── templates/ # the registered folder ├── home/index.pt # page templates └── shared/ ├── _layout.pt # METAL layout (underscore prefix by convention) └── partials/ # reusable fragments live here └── video_square.pt ``` ## Registration semantics & testing Registration is process-wide module state. With the default `cache_init=True`, calling `register_extensions()` again after a previous successful registration is a silent no-op — safe for repeated startup calls, but a test pointing at a different folder must pass `cache_init=False` or reset the flag. A registration that *raised* does not mark the module as registered (since 0.2.0), so it can simply be retried. ```python @pytest.fixture def registered_extension(): folder = (Path(__file__).parent / 'test_templates').as_posix() chameleon_partials.register_extensions(folder, auto_reload=True) yield chameleon_partials.has_registered_extensions = False # reset for the next test ``` `render_partial` returns an `HTML` wrapper (its `__html__()` makes engines insert the markup unescaped); in tests or HTMX/email code, read the raw string from `.html_text`. ## Fetching these docs as Markdown Every page on the documentation site has a plain-Markdown twin: swap the `.html` extension for `.md` to get token-efficient source without the site chrome. For example https://mkennedy.codes/docs/chameleon-partials/reference/render_partial.html is also available at https://mkennedy.codes/docs/chameleon-partials/reference/render_partial.md. Prefer the `.md` form when reading these docs programmatically. ## Resources - [Full documentation](https://mkennedy.codes/docs/chameleon-partials/) - [llms.txt](llms.txt) — Indexed API reference for LLMs - [llms-full.txt](llms-full.txt) — Comprehensive documentation for LLMs - [Source code](https://github.com/mikeckennedy/chameleon_partials)