Repository navigation
fix(templates): collect each template's filters once per set of overridden blocks - #282
Merged
Merged
Conversation
…ycles and successive analyses These tests check, in order, which filters a template's render reaches, where they stand, the template applying each, and the names reaching their value and argument. They cover projects analysed one after the other, a template reached directly and through a child overriding its block, and a template reached inside a cycle of includes and from outside it. The expected results are those of main, which the next commit must keep.
…idden blocks project_templates followed every path through includes and extends to collect the filters a render reaches. Two includes of one template at each of n levels made it follow 2^n paths: 20 levels took 31 s (#279). Each template is now walked once for each set of blocks the templates extending it override. Its first occurrences are kept in order, then mapped through each reference, which gives the same filters in the same order. The results last for one analysis, and a walk cut short by a cycle of includes is not kept. 30 levels now take a few milliseconds.
…uple _reaching returned the filters it found and whether a cycle cut its walk short as a bare pair. It now returns a _Walk naming both, which the cache keeps whole. No behavior changes.
…er of the walk A walk of a template is cut where it reaches a template already on the way. The cache kept a walk that no cycle cut, and returned it to any later walk of that template with the same blocks overridden, even where a template it had gone through was then on the way. The filters then depended on the template walked first: a child extending a parent that includes it back, in the block the child overrides, got its own filter reached through the parent's include, which a walk path by path cuts. Each walk now keeps the templates it went through, and is reused only where none of them is on the way. Elsewhere the walk would be cut, and is walked again.
…h by path 1,500 generated projects of four templates extend and include one another, inside and outside overridden blocks, with cycles among them; each include binds the variable to a name of its own. The filters of every template must be those of the walk of main, written in the test, which follows every path: which filters, where, the template applying each, the names reaching them, and their order.
…ed again A walk cut short by a cycle of includes is not kept, so templates in such a cycle are walked again wherever the cycle cuts their walk: with two includes of one template at each level, the cost there stays exponential, as it was.
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #279.
To collect the filters a render reaches,
project_templatesfollowed every path through includes and extends (_reaching). Two includes of one template at each ofnlevels made it follow 2^n paths, and every directory check runs that walk.Each template is now walked once for each set of blocks the templates extending it override:
project_templatescreates, so projects analysed one after the other in one process do not share them.Limit. Templates in a cycle of includes are still walked again wherever the cycle cuts their walk. With two includes of one template at each level of such a cycle, the cost stays exponential, as on
main.test(templates): pin the filters reached through overridden blocks, cycles and successive analysesmainfix(templates): collect each template's filters once per set of overridden blocksdocs(changelog): announce the linear walk of template filtersCHANGELOG.mdrefactor(templates): return a template's walk of filters as a named tuple_Walk, no behavior changefix(templates): make the filters of a template independent of the order of the walktest(templates): compare the walk of template filters with a walk path by pathmaindocs(changelog): state that templates in a cycle of includes are walked againReview
A walk that no cycle cut was reused even where a template it had gone through was on the way. There, a walk path by path is cut. In the case of the review:
a.htmlextendsb.htmland overrides itsbody;b.htmlincludesa.htmlwithv=otherin that block.Walked from
b.html,a.htmlstops atb.html. Whena.htmlwas walked first, its kept walk was reused insideb.html, which then gotstriptagsreached withother. Whenb.htmlwas walked first, it did not. The walk now keeps the templates it went through, and is reused only where none of them is on the way.Results
Time.
level0.htmlholds{{ name|upper }}, andlevel{n}.htmlincludeslevel{n-1}.htmltwice:mainThe same, with
level0.htmlalso including the top level, which closes a cycle through every level:mainSame filters.
project_templates(root).filtersis identical, order included, on 50 projects: the local corpus (14 Django packages, Django 5.1.4, the regression projects) and the reproductions and controls of templates: constructs left open, and template tags inside comments, hide cross-site scripting; unclosed ones make the inspection quadratic #272 and templates: a block or include inside a parent's script inserts unchecked markup there #273.Tests
The new file
tests/test_template_filter_walk.pyholds 6 tests, 7 cases. No existing test changes.include … withamong them), and their order.mainis written in the test. On 1,500 generated projects of four templates extending and including one another, inside and outside overridden blocks, with cycles among them, the filters are those of that walk. Each include binds the variable to a name of its own, so that what reaches a filter tells the paths apart.Before the fix.
main: the test of time does not end within 60 s. The three tests of the first commit pass: they pin the results ofmain, which the fix keeps.Mutations. Each of these makes the test written for it fail:
Verification
Python 3.12, on
mainat0da4cc8:ruff check .andmypy: clean on each commit;This change and the fix of #278 touch different parts of
taint/templates.py. Merged in either order, they give the same tree, on whichruffandmypyare clean, the unit suite passes (2,649) and the regression too (19 of 19).