Metadata-Version: 2.4
Name: chameleon-rs
Version: 0.1.47
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Web Environment
Classifier: Framework :: FastAPI
Classifier: Framework :: Flask
Classifier: Framework :: Litestar
Classifier: Framework :: Litestar :: 2
Classifier: Intended Audience :: Developers
Classifier: Operating System :: MacOS
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: 3.15
Classifier: Programming Language :: Python :: Free Threading :: 2 - Beta
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Rust
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Classifier: Topic :: Text Processing :: Markup :: HTML
Classifier: Topic :: Text Processing :: Markup :: XML
Classifier: Typing :: Typed
License-File: LICENSE.txt
License-File: COPYRIGHT.txt
Summary: Chameleon templates, modernized: a Rust engine that runs Chameleon templates unchanged, several times faster, with async rendering and built-in framework integrations.
Keywords: chameleon,template,templates,page templates,zpt,tal,metal,html,rust,flask,quart,fastapi,starlette,robyn,litestar
Author-email: Michael Kennedy <michael@talkpython.fm>
License-Expression: LicenseRef-Chameleon
Requires-Python: >=3.12
Description-Content-Type: text/markdown
Project-URL: Changelog, https://github.com/mikeckennedy/chameleon-rust/blob/main/CHANGELOG.md
Project-URL: Documentation, https://mkennedy.codes/docs/chameleon-rs/
Project-URL: Funding, https://github.com/sponsors/mikeckennedy
Project-URL: Homepage, https://mkennedy.codes/docs/chameleon-rs/
Project-URL: Issues, https://github.com/mikeckennedy/chameleon-rust/issues
Project-URL: Source, https://github.com/mikeckennedy/chameleon-rust

<p align="center">
  <img src="https://mkennedy-shared.nyc3.digitaloceanspaces.com/github/chameleon-rs-slogan.png" alt="chameleon-rs: Chameleon templates, Rust speed" width="850">
</p>

# chameleon-rs

**Chameleon templates, modernized. Several times faster.**

chameleon-rs is [Chameleon](https://github.com/malthe/chameleon)'s template language on a new
engine written in Rust. Your existing templates and code run unchanged, with byte-identical
output, several times faster. It also adds what a modern Python web app expects: async
rendering that never blocks the event loop, built-in integrations for Flask/Quart, FastAPI,
Starlette, Robyn and Litestar, partials, Markdown, and support for free-threaded Python. No
runtime dependencies.

**Documentation: <https://mkennedy.codes/docs/chameleon-rs/>** ·
[Changelog](https://github.com/mikeckennedy/chameleon-rust/blob/main/CHANGELOG.md) ·
[Compatibility](https://mkennedy.codes/docs/chameleon-rs/user-guide/compatibility.html) ·
[Performance](https://mkennedy.codes/docs/chameleon-rs/user-guide/performance.html)

## Install

```sh
pip install chameleon-rs        # or: uv pip install chameleon-rs
```

CPython 3.12 to 3.15, including the free-threaded builds (3.14t, 3.15t). Wheels for Linux
(glibc and musl), macOS and Windows contain the compiled engine, so no Rust toolchain is
needed. Stock Chameleon need not be installed.

## Already on Chameleon?

Switching an application is one line at startup, before anything imports Chameleon:

```python
import chameleon_rs.compat

chameleon_rs.compat.install()
```

From then on `import chameleon` gives chameleon-rs, and so do `chameleon_partials`,
`chameleon_flask`, `fastapi_chameleon` and `chameleon_robyn`. Your templates and the code
around them stay as they are.

- **Same output.** Chameleon's own test suite, vendored unmodified, passes: 227 of the 227
  tests that pass on Chameleon. All 122 of its fixture renders are byte-identical, and every
  page of two production Flask/Quart sites renders byte-identical HTML.
  [Known differences](https://mkennedy.codes/docs/chameleon-rs/user-guide/compatibility.html).
- **Check your own app first.** Shadow mode (`chameleon_rs.shadow.install()`) renders a sample
  of real traffic with both engines and reports any difference, while Chameleon keeps serving.
  [Switching from Chameleon](https://mkennedy.codes/docs/chameleon-rs/user-guide/switching-from-chameleon.html).

## Starting fresh?

Templates are Chameleon's: HTML with `tal:` attributes and `${...}` expressions, so a template
is still valid HTML that an editor or browser can open.

```python
from chameleon_rs import PageTemplate

page = PageTemplate('<p tal:repeat="name names">Hello, ${name}!</p>')
print(page(names=['World', 'Rust']))
```

In a web app, point chameleon-rs at a template folder and decorate your views. With FastAPI:

```python
import fastapi
import chameleon_rs.fastapi as fastapi_chameleon

app = fastapi.FastAPI()
fastapi_chameleon.configure(template_folder='templates', app=app)

@app.get('/episodes/{number}')                      # the route decorator first...
@fastapi_chameleon.template('episodes/detail.pt')   # ...then @template
async def detail(number: int):
    episode = await db.episode(number)
    if episode is None:
        fastapi_chameleon.not_found()
    return {'episode': episode}
```

The view keeps its signature, so path and query parameters, `Depends`, validation and the
OpenAPI docs work as without `@template`.
[Getting started](https://mkennedy.codes/docs/chameleon-rs/user-guide/getting-started.html).

## What's new compared with Chameleon

- **Async rendering.** `await template.render_async(**model)` awaits awaitable model values
  where the template uses them, without blocking the event loop. Macros, slots, partials and
  `tal:on-error` work across the wait, and with no awaitables the output is exactly
  `render()`'s. Opt-in concurrency: `@chameleon_rs.concurrent` helpers and
  `concurrent=True` partials run at the same time as the rest of the page.
  [Async rendering](https://mkennedy.codes/docs/chameleon-rs/user-guide/async.html).
- **Framework integrations.** Built in, with nothing extra to install (table below), and
  Pyramid's pyramid_chameleon runs unchanged. Async
  views render with `render_async`, and a `@template` written above its route decorator raises
  an error naming the fix instead of quietly returning JSON.
- **Partials.** Any template can call `${render_partial('shared/card.pt', item=item)}`; the
  partial renders natively inside the page, 10.9x faster than chameleon_partials.
  [Partials](https://mkennedy.codes/docs/chameleon-rs/user-guide/partials.html).
- **Markdown.** `${markdown: episode.notes}` converts Markdown to HTML while the page renders:
  CommonMark plus the GitHub extensions (tables, task lists, footnotes, ...), converted in
  Rust, with raw HTML escaped by default. It's about 26x faster than mistune, a pure-Python
  converter, on a 5 KB document, and repeat conversions come from a cache in well under a
  microsecond. Opt-in with `chameleon_rs.configure(markdown=True)`;
  named profiles (`md-comment:`) give different settings to different content.
  [Markdown](https://mkennedy.codes/docs/chameleon-rs/user-guide/markdown.html).
- **One place for settings.** `chameleon_rs.configure(...)` sets the template folder,
  `auto_reload`, partial lookup and Markdown for the whole process, thread-safely.
  [Configuration](https://mkennedy.codes/docs/chameleon-rs/user-guide/configuration.html).
- **Free-threaded Python.** Wheels for 3.14t and 3.15t that keep the GIL off.
  `render_async(..., offload=True)` (experimental) renders in a worker thread, in parallel
  with the event loop on a free-threaded build.
- **Typed.** PEP 561 type information for the whole API, including the native module.
- **No runtime dependencies.** The engine, the Markdown converter and the integrations are in
  the wheel; each integration imports its framework only when you import it.

## Frameworks

| Framework | Module | Replaces | Docs |
|---|---|---|---|
| Flask, Quart | `chameleon_rs.flask` | chameleon-flask | [Flask and Quart](https://mkennedy.codes/docs/chameleon-rs/user-guide/flask.html) |
| FastAPI | `chameleon_rs.fastapi` | fastapi-chameleon | [FastAPI](https://mkennedy.codes/docs/chameleon-rs/user-guide/fastapi.html) |
| Starlette | `chameleon_rs.starlette` | Starlette's `Jinja2Templates` (`Templates` is a drop-in) | [Starlette](https://mkennedy.codes/docs/chameleon-rs/user-guide/starlette.html) |
| Robyn | `chameleon_rs.robyn` | chameleon-robyn | [Robyn](https://mkennedy.codes/docs/chameleon-rs/user-guide/robyn.html) |
| Litestar | `chameleon_rs.litestar` | Litestar's `TemplateConfig` engine (`ChameleonEngine`); litestar-chameleon | [Litestar](https://mkennedy.codes/docs/chameleon-rs/user-guide/litestar.html) |
| Pyramid | pyramid_chameleon, unchanged, through `compat.install()` | — | [Pyramid](https://mkennedy.codes/docs/chameleon-rs/user-guide/pyramid.html) |

[Web frameworks](https://mkennedy.codes/docs/chameleon-rs/user-guide/frameworks.html) shows the
same page in each one. Each module keeps the API of the library it replaces and adds the same
decorator API (`configure`, `@template`, `render`, `response`, `not_found`, `error`,
`render_async`, `response_async`, `check_views`).

**Template names come from your code.** The decorator modules and `render_partial()` look names
up as the original libraries do (`..` and absolute paths work), so never build one from
request data. Starlette's `Templates` and Litestar's `ChameleonEngine` refuse names that reach
outside their folder, as Jinja does.

## Performance

In synthetic benchmarks (release build, same process, interleaved with Chameleon): listings
6.6x faster per item, macro calls 6.9x, an app-shaped page 4.3x, native partials 10.9x, and
templates compile about 70x faster. On a production Quart site, template rendering was 2.2x
faster in total and whole requests 1.3x faster; under load, throughput rose 34%. The released
wheels are profile-guided, about 10% faster again.
[How it's measured](https://mkennedy.codes/docs/chameleon-rs/user-guide/performance.html).

## Development

```sh
make dev     # set up venv with the reference Chameleon and build the extension
make test    # Rust tests, Python tests, Chameleon's own suite on both engines
```

Licensed under Chameleon's license (see `LICENSE.txt` and `COPYRIGHT.txt`); this project
ports Chameleon's behavior and reuses its test suite.

