Skip to content

fix: keep the parent block's filters when a child renders block.super - #187

Merged
SizzleUnrlsd merged 5 commits into
mainfrom
fix/block-super
Sep 29, 2026
Merged

SizzleUnrlsd merged 5 commits into
mainfrom
fix/block-super

Conversation

@SizzleUnrlsd

Copy link
Copy Markdown
Contributor

Closes #185.

Summary

  • A template extending another passed every block it defined up the chain as replaced, so the parent's filter applications in an overridden block dropped out of the render context flow even when the child rendered {{ block.super }}, which Django executes. The reachable finding at the parent's line was kept; the exploitable flow was lost.
  • _read_scopes now records the blocks whose body renders block.super (_Template.extended), and _reaching passes up as replaced only the blocks a template defines without it (_Template.replaced), level by level: a child with block.super keeps its parent's content; a parent that itself replaced its own parent's block still cuts the chain above it; a replacement at the end cuts the whole chain. Other overridden blocks stay excluded, and each application is yielded once, with the parent template as through and the filter's line as sink_line.
  • block.super counts however the block renders it: filtered, conditional or twice.
  • Documented in the usage guide and the changelog.

Verification

  • RED: 8 failures for the expected reason (the parent flow missing, or the child's own flow alone).
  • tests/test_block_super.py: 10 passed; the context-flow, filter-call and docs tests pass. Full suite in the worktree (regression excluded): all passed. ruff check and mypy pass.
  • Mutations, all killed: passing every block up as replaced; never recording block.super; treating nothing as replaced (fails the existing overridden-twice case).

…ocessor

A TEMPLATES bound in a function, called or not, or in a class configures nothing; a function reading, mutating or rebinding the module's TEMPLATES leaves the activation uncertain; a local TEMPLATES beside module settings changes nothing.
request_processor walked every node of every module and counted any assignment to TEMPLATES, so a function-local or class-level assignment activated the request context processor. It now walks the modules with their scope tables: an assignment counts only at module scope, and a mention counts only when the name resolves to the module's binding, or to nothing, as a star import gives. ScopeTable.scope_for accepts a lambda, whose scope the builder already records.
…e flow

A block rendering {{ block.super }} extends the parent's block: its filters stay fed by the render context, with the parent template as the place of the call, however block.super is rendered; a block without it replaces the parent's whole block; other overridden blocks stay excluded; a chain of templates keeps exactly the parents each level renders, once.
A template extending another replaced every block it defined in the parent, so the parent's filter applications dropped out of the render context flow even when the child rendered {{ block.super }}, which Django executes. A template now records the blocks that render block.super, and only the blocks defined without it are passed up as replaced, level by level.
@SizzleUnrlsd SizzleUnrlsd added the bug Something isn't working label Sep 28, 2026
@SizzleUnrlsd SizzleUnrlsd self-assigned this Sep 28, 2026
@SizzleUnrlsd
SizzleUnrlsd merged commit 2605fab into main Sep 29, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[P2] Preserve parent template flows rendered through block.super

1 participant