djc / askama

Type-safe, compiled Jinja-like templates for Rust
Apache License 2.0
3.47k stars 219 forks source link

Allow `extends`, `macro` and `import` inside an included template #915

Closed PizzasBear closed 9 months ago

PizzasBear commented 11 months ago

This makes include work more like in Jinja2 , and closes (#572). This also improves caching of included templates by storing them inside the contexts map.

djc commented 11 months ago

What is the use case? Why do you want this?

PizzasBear commented 11 months ago

This allows me to use macros fully inside an included template without having to create another macro file that is associated with the included template and importing it using the same name everywhere I include this template.

GuillaumeGomez commented 11 months ago

Nice! I'm working on something similar: allowing imports and macro declarations everywhere. So big :+1: for this from me. :)

Do you have some numbers for the impact on compilation performance by any chance?

PizzasBear commented 11 months ago

No, I didn't measure the performance impact. Though I suspect it wouldn't get much worse because this caches included templates better, but it does clone the Context struct for every include.

GuillaumeGomez commented 11 months ago

Let's see if there is a change already and if there one big enough, we can start optimizing (if @djc is ok with this feature of course).

PizzasBear commented 11 months ago

I don't know how to measure the performance impact, can you explain me?

GuillaumeGomez commented 11 months ago

Very ad-hoc way to do it (if nothing comes out from this, I think it's mostly ok): write a file with like 10 templates of different sizes. Then run cargo build --timings with and without this PR a few times and compare the median time for both.

PizzasBear commented 11 months ago

With big files full of if statements there doesn't seem to be any difference from main. And including a bunch of big files full of ifs doesn't seem to show any differences either.

GuillaumeGomez commented 11 months ago

Can you provide us the code you used to test so we can also confirm please?

PizzasBear commented 11 months ago

I used the following to generate the templates.

from datetime import datetime

def generate_big_if(num: int, level: int = 0) -> str:
    indent = "    " * level
    if num <= 1:
        return f"{indent}hello {datetime.now()}\n"
    right = num // 2
    left = num - right
    return (
        indent
        + "{% if true %}\n"
        + generate_big_if(left, level + 1)
        + indent
        + "{% else %}\n"
        + generate_big_if(right, level + 1)
        + indent
        + "{% endif %}\n"
    )

def main():
    for i in range(9):
        with open(f"templates/{i}.html", "w+") as f:
            f.write(generate_big_if(int(1.75 ** (i + 3))))

if __name__ == "__main__":
    main()

And had this file as well:

{% include "1.html" %}
{% include "2.html" %}
{% include "3.html" %}
{% include "4.html" %}
{% include "5.html" %}
{% include "6.html" %}
{% include "7.html" %}
{% include "8.html" %}
{% include "1.html" %}
{% include "2.html" %}
{% include "3.html" %}
{% include "4.html" %}
{% include "5.html" %}
{% include "6.html" %}
{% include "7.html" %}
{% include "8.html" %}

And compiled:

use askama::Template;

#[derive(Template)]
#[template(path = "0.html")]
struct T0;

#[derive(Template)]
#[template(path = "1.html")]
struct T1;

#[derive(Template)]
#[template(path = "2.html")]
struct T2;

#[derive(Template)]
#[template(path = "3.html")]
struct T3;

#[derive(Template)]
#[template(path = "4.html")]
struct T4;

#[derive(Template)]
#[template(path = "5.html")]
struct T5;

#[derive(Template)]
#[template(path = "6.html")]
struct T6;

#[derive(Template)]
#[template(path = "7.html")]
struct T7;

#[derive(Template)]
#[template(path = "8.html")]
struct T8;

#[derive(Template)]
#[template(path = "9.html")]
struct T9;

fn main() {
    println!("Hello, world!");
}
GuillaumeGomez commented 11 months ago

Thanks! Confirming the impact on my side.

GuillaumeGomez commented 11 months ago

I have 1.97s before this PR and 1.98s after. I think the impact is indeed quite small. Well, let's see what @djc thinks about this now.

GuillaumeGomez commented 10 months ago

Does it look good to you @djc?

GuillaumeGomez commented 10 months ago

Ping @djc.

djc commented 9 months ago

Please squash that change into the originating commit.

PizzasBear commented 9 months ago

I squashed it

GuillaumeGomez commented 9 months ago

:tada:

MeirKriheli commented 6 months ago

Thanks for this!

Is it going into a release soon? It's very useful with partials for htmx.

djc commented 6 months ago

I wrote up some thoughts on release planning in #1035.