diff --git a/common/jinja/value.cpp b/common/jinja/value.cpp index 5ecff52a1..0840e05b8 100644 --- a/common/jinja/value.cpp +++ b/common/jinja/value.cpp @@ -425,7 +425,12 @@ const func_builtins & global_builtins() { throw raised_exception("Jinja Exception: " + msg); }}, {"dict", toobject}, - {"namespace", toobject}, + {"namespace", [](const func_args & args) -> value { + // unlike a dict, a namespace has no methods: ns.items is the attribute, as in jinja2 + value out = toobject(args); + cast_val(out)->has_builtins = false; + return out; + }}, {"strftime_now", [](const func_args & args) -> value { args.ensure_vals(); std::string format = args.get_pos(0)->as_string().str(); diff --git a/tests/test-jinja.cpp b/tests/test-jinja.cpp index 327ab365c..99552a8f7 100644 --- a/tests/test-jinja.cpp +++ b/tests/test-jinja.cpp @@ -1299,6 +1299,18 @@ static void test_namespace(testing & t) { json::object(), "3" ); + + test_template(t, "namespace attribute named like a dict method", + "{% set ns = namespace(items=[]) %}{{ 'yes' if ns.items else 'no' }}{% set ns.items = ns.items + [1] %}{{ ns.items | length }}{{ 'yes' if ns.items else 'no' }}", + json::object(), + "no1yes" + ); + + test_template(t, "dict keeps its methods", + "{% set d = dict(items=1) %}{% for k, v in d.items() %}{{ k }}={{ v }}{% endfor %}", + json::object(), + "items=1" + ); } static void test_tests(testing & t) {