# Configuration

URL: /kida/docs/reference/configuration/
Section: reference
Description: All Environment configuration options

---

> For a complete page index, fetch /kida/llms.txt.

All Environment configuration options.

## Environment Constructor

```python
from kida import Environment, FileSystemLoader

env = Environment(
    loader=FileSystemLoader("templates/"),
    autoescape=True,
    auto_reload=True,
    cache_size=400,
    fragment_cache_size=1000,
    fragment_ttl=300.0,
)
```

---

## Core Options

### loader

Template source provider.

| Type | Default | Description |
|------|---------|-------------|
| `Loader \| None` | `None` | Template loader |

```python
# FileSystemLoader
loader = FileSystemLoader("templates/")

# Multiple paths
loader = FileSystemLoader(["templates/", "shared/"])

# DictLoader
loader = DictLoader({"page.html": "..."})
```

### autoescape

Escaping mode for output.

| Type | Default | Description |
|------|---------|-------------|
| `bool \| "html" \| "terminal" \| "markdown" \| Callable` | `True` | Select escaping behavior |

```python
# Always escape
autoescape=True

# Never escape
autoescape=False

# Terminal ANSI-safe escaping
autoescape="terminal"

# GitHub-flavored Markdown escaping
autoescape="markdown"

# Conditional by filename
def should_escape(name):
    if name is None:
        return True
    return name.endswith((".html", ".xml"))

autoescape=should_escape
```

Markdown mode is for generated Markdown such as GitHub step summaries, PR comments, and release notes. It escapes CommonMark/GFM formatting triggers while keeping ordinary punctuation readable. Since 0.9.0, inline hyphens, parentheses, hashes, pipes, and tildes are not escaped unless they appear where Markdown treats them as block markers. Values marked with `| safe` or `Markup(...)` bypass Markdown escaping through `__markdown__`, so only mark trusted Markdown safe.

### auto_reload

Check for template source changes.

| Type | Default | Description |
|------|---------|-------------|
| `bool` | `True` | Check on each load |

```python
# Development: check for changes
auto_reload=True

# Production: skip checks
auto_reload=False
```

---

## Cache Options

### cache_size

Maximum compiled templates to cache.

| Type | Default | Description |
|------|---------|-------------|
| `int` | `400` | LRU cache size |

```python
# Small cache (testing)
cache_size=10

# Large cache (production)
cache_size=1000
```

### fragment_cache_size

Maximum `{% cache %}` fragments to cache.

| Type | Default | Description |
|------|---------|-------------|
| `int` | `1000` | Fragment cache size |

### fragment_ttl

Fragment cache time-to-live in seconds.

| Type | Default | Description |
|------|---------|-------------|
| `float` | `300.0` | 5 minutes |

```python
# Short TTL for development
fragment_ttl=1.0

# Longer TTL for production
fragment_ttl=3600.0  # 1 hour
```

`fragment_ttl` is the default for `{% cache %}` blocks. You can override TTL per block:

```kida
{% cache "user-" ~ user.id, ttl="5m" %}
    {{ render_profile(user) }}
{% end %}
```

Supported `ttl` formats in templates:
- Numeric seconds (`ttl=30`, `ttl=0.5`)
- Duration strings (`"30s"`, `"5m"`, `"2h"`, `"1d"`)

### bytecode_cache

Persistent bytecode cache for cold-start performance.

| Type | Default | Description |
|------|---------|-------------|
| `BytecodeCache \| bool \| None` | `None` | Bytecode cache |

```python
from kida.bytecode_cache import BytecodeCache

# Auto-detect (default)
bytecode_cache=None

# Explicit disable
bytecode_cache=False

# Custom location
bytecode_cache=BytecodeCache("__pycache__/kida/")
```

Bytecode cache files are trusted application state. Store them in an
application-owned directory, not a shared or user-writable location: cache files
contain marshalled code and serialized compiler data, and the sandbox does not
treat bytecode cache contents as untrusted input.

### static_context

Static values for partial evaluation at compile time. Expressions that depend only on these values are evaluated during compilation and replaced with constants in the bytecode — enabling near-`str.format()` speed for static regions (e.g. `site.title`, `config.base_url`).

| Type | Default | Description |
|------|---------|-------------|
| `dict[str, Any] \| None` | `None` | Values known at compile time |

```python
# Site-wide config available in all templates
env = Environment(
    loader=FileSystemLoader("templates/"),
    static_context={"site": site_config, "config": app_config},
)

# from_string() accepts its own static_context kwarg (takes precedence)
template = env.from_string("{{ site.title }}", static_context={"site": {"title": "My Site"}})
```

Applied automatically to all templates loaded via `get_template()`. Use `from_string(source, static_context={...})` to override per-call.

---

## Lexer Options

Control template syntax delimiters.

### block_start / block_end

Tag delimiters.

| Option | Default | Description |
|--------|---------|-------------|
| `block_start` | `"{%"` | Opening tag |
| `block_end` | `"%}"` | Closing tag |

### variable_start / variable_end

Output delimiters.

| Option | Default | Description |
|--------|---------|-------------|
| `variable_start` | `"{{"` | Opening output |
| `variable_end` | `"}}"` | Closing output |

### comment_start / comment_end

Comment delimiters.

| Option | Default | Description |
|--------|---------|-------------|
| `comment_start` | `"{#"` | Opening comment |
| `comment_end` | `"#}"` | Closing comment |

### Custom Delimiters

```python
# Ruby-like syntax
env = Environment(
    block_start="<%",
    block_end="%>",
    variable_start="<%=",
    variable_end="%>",
    comment_start="<%#",
    comment_end="%>",
)
```

---

## Whitespace Options

### trim_blocks

Remove newline after block tags.

| Type | Default | Description |
|------|---------|-------------|
| `bool` | `False` | Trim after `%}` |

### lstrip_blocks

Remove leading whitespace before block tags.

| Type | Default | Description |
|------|---------|-------------|
| `bool` | `False` | Strip before `{%` |

```python
env = Environment(
    trim_blocks=True,
    lstrip_blocks=True,
)
```

---

## Behavior Options

### strict_none

Strict None comparison in sorting.

| Type | Default | Description |
|------|---------|-------------|
| `bool` | `False` | Fail on None comparisons |

```python
# Lenient (default): None sorts last
strict_none=False

# Strict: raise error on None
strict_none=True
```

### strict_undefined

Strict attribute access — raise `UndefinedError` on missing attributes instead of returning the `_Undefined` sentinel.

| Type | Default | Description |
|------|---------|-------------|
| `bool` | `True` | Raise on missing attributes |

```python
# Strict (default): missing attributes raise UndefinedError immediately
strict_undefined=True

# Lenient: missing attributes return the _Undefined sentinel (renders as "")
strict_undefined=False
```

By default, `{{ user.typo }}` raises immediately instead of rendering as empty string — the same contract as undefined top-level variables. Error messages distinguish between "Undefined variable", "Undefined attribute", and "Undefined key", and include `did you mean …` suggestions.

To keep the lenient behavior on a specific `Environment` (useful when migrating templates that rely on optional attributes), pass `strict_undefined=False`. The preferred in-template idioms for optional data are:

- `{{ user.bio ?? "" }}` — null-coalescing
- `{% if user.bio is defined and user.bio %}` — existence guard
- `{{ user.bio | default("") }}` — filter with fallback

### jinja2_compat_warnings

Emit `MigrationWarning` when a nested `{% set %}` shadows a name already bound template-wide via `{% let %}` or `{% export %}` — the Jinja2 scoping trap.

| Type | Default | Description |
|------|---------|-------------|
| `bool` | `True` | Warn on nested `{% set %}` that shadows a template-scope binding |

```python
# Default — catches the Jinja2 trap
jinja2_compat_warnings=True

# Silence the warning entirely
jinja2_compat_warnings=False

# Or filter via stdlib warnings:
# warnings.filterwarnings("ignore", category=MigrationWarning)
```

When enabled, a template like `{% let x = 1 %}{% if cond %}{% set x = 2 %}{% end %}` emits a `MigrationWarning` (K-WARN-002) — in Jinja2 the author would expect `x == 2` after the block, but Kida's block-scoped `{% set %}` leaves `x == 1`. The warning names the shadowed variable and suggests `{% export %}` as the fix.

The warning is **narrowly scoped** to the actual trap pattern: fresh names used for genuine block-scoped purposes (e.g., a loop-local counter) do not trigger — Kida and Jinja2 behave identically there.

### fstring_coalescing

Enable f-string output coalescing optimization.

| Type | Default | Description |
|------|---------|-------------|
| `bool` | `True` | Coalesce consecutive outputs into f-strings |

When enabled, the compiler merges consecutive output nodes (static text and simple expressions) into single f-string appends, reducing function call overhead by ~37% in output-heavy templates.

```python
# Enabled (default) — recommended for production
fstring_coalescing=True

# Disable for debugging compiled output
fstring_coalescing=False
```

See [[docs/advanced/compiler|Compiler Internals]] for details on how coalescing works.

### pure_filters

Additional filters the compiler can treat as side-effect-free.

| Type | Default | Description |
|------|---------|-------------|
| `set[str]` | `set()` | Custom pure filter names |

Filters in this set are assumed to have no side effects and can be coalesced into f-strings alongside built-in pure filters (`escape`, `upper`, `lower`, `trim`, `default`, etc.). When `static_context` is provided, pure filters with fully static inputs are evaluated at compile time and replaced with constants.

```python
# Manual registration via configuration
pure_filters={"markdown", "highlight", "currency"}
```

The preferred approach is the `@pure` decorator, which auto-registers filters as pure when added via `add_filter()`:

```python
from kida import Environment, pure

@pure
def currency(value, symbol="$"):
    return f"{symbol}{value:,.2f}"

env = Environment()
env.add_filter("currency", currency)
# "currency" is automatically added to pure_filters
```

### validate_calls

Enable compile-time call-site validation for `{% def %}` functions.

| Type | Default | Description |
|------|---------|-------------|
| `bool` | `False` | Validate call sites against `{% def %}` signatures |

When enabled, the compiler checks every `{{ func(...) }}` call against the matching `{% def %}` signature and emits `UserWarning` for unknown parameters, missing required arguments, and other mismatches. Duplicate keyword arguments are rejected by the parser before validation runs.

```python
# Enable call validation (recommended for development/CI)
validate_calls=True

# Disabled (default) — no validation overhead
validate_calls=False
```

See [[docs/advanced/analysis|Static Analysis — Call-Site Validation]] for programmatic API details.

### preserve_ast

Preserve AST for template introspection.

| Type | Default | Description |
|------|---------|-------------|
| `bool` | `True` | Keep AST after compile |

```python
# Enable introspection (default)
preserve_ast=True

# Disable to save memory
preserve_ast=False
```

### max_extends_depth / max_include_depth

Resource limits for DoS protection. Configurable for deployments with deep template hierarchies.

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `max_extends_depth` | `int` | `50` | Maximum `{% extends %}` chain depth |
| `max_include_depth` | `int` | `50` | Maximum `{% include %}` / `{% embed %}` depth |

```python
# Deeper inheritance for complex sites
env = Environment(
    max_extends_depth=100,
    max_include_depth=75,
)
```

Exceeding these limits raises `TemplateRuntimeError`. See [[docs/advanced/security|Security Hardening]] for details.

### enable_htmx_helpers

Register HTMX helper globals (`hx_request`, `hx_target`, `hx_trigger`, `hx_boosted`, `csrf_token`).

| Type | Default | Description |
|------|---------|-------------|
| `bool` | `True` | Enable HTMX helpers |

When enabled, templates can use `hx_request()`, `hx_target()`, etc. Frameworks must set metadata via `render_context().set_meta()` before rendering. See [[docs/extending/custom-globals|Custom Globals]] for HTMX helper details.

---

## Development vs Production

### Development

```python
env = Environment(
    loader=FileSystemLoader("templates/"),
    autoescape=True,
    auto_reload=True,          # Check for changes
    cache_size=50,             # Small cache
    fragment_ttl=1.0,          # Short TTL
)
```

### Production

```python
env = Environment(
    loader=FileSystemLoader("templates/"),
    autoescape=True,
    auto_reload=False,         # No reload checks
    cache_size=1000,           # Large cache
    fragment_ttl=3600.0,       # 1 hour TTL
)
```

---

## Cache Methods

### cache_info()

Get cache statistics.

```python
info = env.cache_info()
print(info["template"])
# {'size': 5, 'max_size': 400, 'hits': 100, 'misses': 5, 'hit_rate': 0.95}
```

### clear_cache(include_bytecode=False)

Clear all caches.

```python
env.clear_cache()                    # Memory only
env.clear_cache(include_bytecode=True)  # Include disk
```

### clear_template_cache(names=None)

Clear specific templates.

```python
env.clear_template_cache()           # All
env.clear_template_cache(["base.html", "page.html"])  # Specific
```

### clear_fragment_cache()

Clear fragment cache only.

```python
env.clear_fragment_cache()
```

## See Also

- [[docs/reference/api|API Reference]] — Environment methods
- [[docs/usage/loading-templates|Loading Templates]] — Loader configuration
- [[docs/about/performance|Performance]] — Optimization tips
