# purity

URL: /kida/api/analysis/purity/
Section: analysis
Description: Purity analysis for template introspection.

Determines if expressions and blocks are pure (deterministic).
Pure blocks produce the same output for the same inputs.

---

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

Open LLM text
(/kida/api/analysis/purity/index.txt)

Share with AI

Ask Claude
(https://claude.ai/new?q=Please%20help%20me%20understand%20this%20documentation%3A%20%2Fkida%2Fapi%2Fanalysis%2Fpurity%2Findex.txt)

Ask ChatGPT
(https://chatgpt.com/?q=Please%20help%20me%20understand%20this%20documentation%3A%20%2Fkida%2Fapi%2Fanalysis%2Fpurity%2Findex.txt)

Ask Gemini
(https://gemini.google.com/app?q=Please%20help%20me%20understand%20this%20documentation%3A%20%2Fkida%2Fapi%2Fanalysis%2Fpurity%2Findex.txt)

Ask Copilot
(https://copilot.microsoft.com/?q=Please%20help%20me%20understand%20this%20documentation%3A%20%2Fkida%2Fapi%2Fanalysis%2Fpurity%2Findex.txt)

Module

#
`analysis.purity`

Purity analysis for template introspection.

Determines if expressions and blocks are pure (deterministic).
Pure blocks produce the same output for the same inputs.

1Class1Function

## Classes

`PurityAnalyzer`

47

▼

Determine if an expression or block is pure (deterministic).

Pure expressions produce the same out…

Determine if an expression or block is pure (deterministic).

Pure expressions produce the same output for the same inputs.
This is used to determine cache safety.

Conservative: defaults to "unknown" when uncertain.

Purity Rules:

- Constants, variables, attribute access: pure

- Binary/unary operations, comparisons: pure

- Known pure filters (upper, lower, sort, etc.): pure

- Known impure filters (random, shuffle): impure

- Unknown filters/functions: unknown

#### Methods

`analyze`

1

`PurityLevel`

▼

Analyze a node and return its purity level.

`def analyze(self, node: Node) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

AST node to analyze.

##### Returns

`PurityLevel`

"pure", "impure", or "unknown"

Internal Methods
46

▼

`__init__`

3

▼

Initialize analyzer with optional extensions.

`def __init__(self, extra_pure_functions: frozenset[str] | None = None, extra_impure_filters: frozenset[str] | None = None, template_resolver: Callable[[str], Any] | None = None) -> None`

##### Parameters

Name
Type
Description

`extra_pure_functions`
`—`

Additional functions to treat as pure.

Default:`None`

`extra_impure_filters`
`—`

Additional filters to treat as impure.

Default:`None`

`template_resolver`
`—`

Optional callback(name: str) -> Template | None to resolve included templates. If None, includes return "unknown".

Default:`None`

`_visit`

1

`PurityLevel`

▼

Visit a node and determine purity.

`def _visit(self, node: Node | None) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_children`

1

`PurityLevel`

▼

Visit children and combine purity.

`def _visit_children(self, node: Node) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_const`

1

`PurityLevel`

▼

Constants are pure.

`def _visit_const(self, node: Const) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_name`

1

`PurityLevel`

▼

Variable access is pure (reading doesn't mutate).

`def _visit_name(self, node: Name) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_getattr`

1

`PurityLevel`

▼

Attribute access is pure.

`def _visit_getattr(self, node: Getattr) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_optionalgetattr`

1

`PurityLevel`

▼

Optional attribute access is pure.

`def _visit_optionalgetattr(self, node: OptionalGetattr) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_getitem`

1

`PurityLevel`

▼

Subscript access is pure.

`def _visit_getitem(self, node: Getitem) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_optionalgetitem`

1

`PurityLevel`

▼

Optional subscript access is pure.

`def _visit_optionalgetitem(self, node: OptionalGetitem) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_binop`

1

`PurityLevel`

▼

Binary operations are pure.

`def _visit_binop(self, node: BinOp) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_unaryop`

1

`PurityLevel`

▼

Unary operations are pure.

`def _visit_unaryop(self, node: UnaryOp) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_compare`

1

`PurityLevel`

▼

Comparisons are pure.

`def _visit_compare(self, node: Compare) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_boolop`

1

`PurityLevel`

▼

Boolean operations are pure.

`def _visit_boolop(self, node: BoolOp) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_condexpr`

1

`PurityLevel`

▼

Conditional expressions are pure if all parts are pure.

`def _visit_condexpr(self, node: CondExpr) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_nullcoalesce`

1

`PurityLevel`

▼

Null coalescing is pure.

`def _visit_nullcoalesce(self, node: NullCoalesce) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_concat`

1

`PurityLevel`

▼

String concatenation is pure.

`def _visit_concat(self, node: Concat) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_range`

1

`PurityLevel`

▼

Range literals are pure.

`def _visit_range(self, node: Range) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_slice`

1

`PurityLevel`

▼

Slice expressions are pure.

`def _visit_slice(self, node: Slice) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_list`

1

`PurityLevel`

▼

List literals are pure if all items are pure.

`def _visit_list(self, node: List) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_listcomp`

1

`PurityLevel`

▼

List comprehensions are pure if iter, elt, and ifs are all pure.

`def _visit_listcomp(self, node: ListComp) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_tuple`

1

`PurityLevel`

▼

Tuple literals are pure if all items are pure.

`def _visit_tuple(self, node: Tuple) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_dict`

1

`PurityLevel`

▼

Dict literals are pure if all keys and values are pure.

`def _visit_dict(self, node: Dict) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_filter`

1

`PurityLevel`

▼

Filter purity depends on the filter.

`def _visit_filter(self, node: Filter) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_pipeline`

1

`PurityLevel`

▼

Pipeline purity depends on all filters in the chain.

`def _visit_pipeline(self, node: Pipeline) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_funccall`

1

`PurityLevel`

▼

Function call purity depends on the function.

`def _visit_funccall(self, node: FuncCall) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_test`

1

`PurityLevel`

▼

Tests are pure (they're just predicates).

`def _visit_test(self, node: Test) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_for`

1

`PurityLevel`

▼

For loops are pure if body is pure.

`def _visit_for(self, node: For) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_if`

1

`PurityLevel`

▼

Conditionals are pure if all branches are pure.

`def _visit_if(self, node: If) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_match`

1

`PurityLevel`

▼

Match statements are pure if all branches are pure.

`def _visit_match(self, node: Match) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_output`

1

`PurityLevel`

▼

Output is pure if expression is pure.

`def _visit_output(self, node: Output) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_data`

1

`PurityLevel`

▼

Static data is pure.

`def _visit_data(self, node: Data) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_cache`

1

`PurityLevel`

▼

Cache blocks: the body is evaluated, but result is cached.

The block itself is…

`def _visit_cache(self, node: Cache) -> PurityLevel`

Cache blocks: the body is evaluated, but result is cached.

The block itself is pure if the body is pure.

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_block`

1

`PurityLevel`

▼

Block is pure if body is pure.

`def _visit_block(self, node: Block) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_with`

1

`PurityLevel`

▼

With blocks are pure if bindings and body are pure.

`def _visit_with(self, node: With) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_withconditional`

1

`PurityLevel`

▼

Conditional with is pure if expr and body are pure.

`def _visit_withconditional(self, node: WithConditional) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_set`

1

`PurityLevel`

▼

Set is pure if value is pure.

`def _visit_set(self, node: Set) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_let`

1

`PurityLevel`

▼

Let is pure if value is pure.

`def _visit_let(self, node: Let) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_capture`

1

`PurityLevel`

▼

Capture is pure if body is pure.

`def _visit_capture(self, node: Capture) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_include`

1

`PurityLevel`

▼

Include purity depends on included template.

If template_resolver is provided …

`def _visit_include(self, node: Include) -> PurityLevel`

Include purity depends on included template.

If template_resolver is provided and template name is a constant,
resolves and analyzes the included template. Otherwise returns "unknown".

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_extends`

1

`PurityLevel`

▼

Extends is unknown (depends on parent template).

`def _visit_extends(self, node: Extends) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_def`

1

`PurityLevel`

▼

Function definition is pure if body is pure.

`def _visit_def(self, node: Def) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_macro`

1

`PurityLevel`

▼

Macro definition (same as def).

`def _visit_macro(self, node: Def) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_inlinedfilter`

1

`PurityLevel`

▼

Inlined filter is pure (only pure filters are inlined).

`def _visit_inlinedfilter(self, node: InlinedFilter) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_marksafe`

1

`PurityLevel`

▼

Mark safe is pure.

`def _visit_marksafe(self, node: MarkSafe) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_await`

1

`PurityLevel`

▼

Await is unknown (async operations may have side effects).

`def _visit_await(self, node: Await) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

`_visit_trans`

1

`PurityLevel`

▼

Trans blocks are impure — gettext/ngettext depend on locale state.

`def _visit_trans(self, node: Trans) -> PurityLevel`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`PurityLevel`

## Functions

`_combine_purity`

2

`PurityLevel`

▼

Combine two purity levels (take worst case).

`def _combine_purity(a: PurityLevel, b: PurityLevel) -> PurityLevel`

##### Parameters

Name
Type
Description

`a`
`PurityLevel`

`b`
`PurityLevel`

##### Returns

`PurityLevel`
