Open nabijaczleweli opened 4 years ago
Sorry for the late reply. I haven't checked the cause of the error but macros are meant to be defined on separate files and not really used that way, I wouldn't be surprised some weird interactions are happening there
Well, the examples only define macros in separate files, so that's a good start.
I think I've encountered the same bug. Can't quite make sense from the example so my case might be slightly different, but these probably have the same root cause; looks like the namespaces aren't tracked properly when the caller is using inheritance. My case has one more level of macros. The error message about a missing macro is pointing to a caller template, not the macro template. The macro files have only macros in them.
I've got page-a.html
that extends base.html
. In a block from the base, a macro (outer
) is used from macro_a_level0.html
. This macro calls a macro (inner
) from macro_a_level1.html
, which again calls a macro from that file (deeper
).
The self::deeper()
invocation fails because Tera is looking for deeper()
in page_a.html
.
Here's a full repository with a minimal-ish repro to help with the many files: https://github.com/sooda/bugs/tree/tera/macro-namespace-inheritance - just cargo run
to see what happens. [edit: link changed a bit]
Pasting a few relevant bits below. Output:
let rendered_a = tera.render("page_a.html", &cx);
let rendered_b = tera.render("page_b.html", &cx);
// A: Err(Error { kind: Msg("Failed to render \'page_a.html\': error while rendering macro `level1::inner`"), source: Some(Error { kind: Msg("Macro `self::deeper` not found in template `page_a.html`"), source: None }) })
println!("A: {:?}", rendered_a);
// B: Ok("\n\n\n\nhello from b\n\n\n\n\n")
println!("B: {:?}", rendered_b);
Both should work, but only the B variant does. The difference between page-a.html
and page-b.html
is that page-b.html
includes also the innermost macro file. That's an acceptable workaround for me.
$ diff -u page_a.html page_b.html
--- page_a.html 2020-08-11 22:49:06.065862910 +0300
+++ page_b.html 2020-08-11 22:49:28.049015276 +0300
@@ -1,5 +1,6 @@
{% extends "base.html" %}
-{% import "macro_a_level0.html" as level0 %}
+{% import "macro_b_level1.html" as level1 %}
+{% import "macro_b_level0.html" as level0 %}
{% block content %}
{{ level0::outer() }}
{% endblock content %}
The other files are essentially identical between a and b.
Here's another interesting repro case: multiple macro files imported from the rendered page, but no import recursion. https://github.com/sooda/bugs/tree/tera/macro-namespace-inheritance-v2
Case: page_a.html
includes macro_bar.html
as bar
and macro_a_foo.html
as foo
, calls foo::outer()
. outer()
calls self::inner()
that's in the same file. Expected: inner is found and gets rendered. Result:
Err(Error { kind: Msg("Failed to render \'page_a.html\': error while rendering macro `foo::outer`"), source: Some(Error { kind: Msg("Macro `self::inner` not found in template `page_a.html`"), source: None }) })
Changing self::
to foo::
"fixes" this: foo::inner()
is obviously found in page_a.html
because that page imports foo
.
Deleting the bar
import "fixes" this too as page_b.html
in the above repo demonstrates, but what if its contents are really needed? (macro_bar.html
needs to have some content for the bug to occur; this didn't repro with an empty file.)
Swapping the order of the foo
and bar
imports would also fix things in this particular case, but if both macro files use self::
, there is no order that would work for both simultaneously. The first trick to change self
to the name of the import does help though.
@sooda thanks for the debugging! Can you recreate those examples as testcases in a PR?
These occurred in normal use so there wasn't much debugging needed :) the actual bug still needs to be found and fixed.
See PR #548 for two commits. The first one is for the latter case and the second one is for the nested more complicated trouble. Not all of those tests obviously pass yet. I've add a few comments between the lines to explain the details.
Looks like the issue is that loading macro namespaces in Tera is currently a bit stupid. I have no recollection at all of that part of code so anyone will go as fast as me if they want to fix it. Thanks to @sooda there are tests for it in https://github.com/Keats/tera/pull/548
Note to myself: Tera v2 should have fewer restrictions for macros in general
Tested on 5dc12afc4e0e506183898fc56d3bdc9ce6a0b6d7 (current HEAD) and 1.3.1 off Crates.io.
Given the following setup:
Running this (based on the "basic" example):
Yields
which is ungood
However, removing the inheritance from
child
:Yields
as expected