Closed cefn closed 6 years ago
Dynamic include names:
{% include {{inc_name}} %}
were implemented some time ago: https://github.com/pfalcon/utemplate/commit/24c5ceb827707db2a69d053327ec42c6520c7e79 .
That's the amount in which I can support dynamic include names, I apologize if it doesn't cover all the usecases above. I intend to close this ticket. If you really think that there're some important points not covered, and you'd like to keep it open, feel free to reopen.
Thanks for that work, @pfalcon. I think it will make a big difference when/if I get back to working with these templated pages.
Hi again.
Been using your templating framework with great success, but have hit a brick wall on a specific feature which is the last I need to resolve to control memory overhead for our application.
Hopefully the feature I'm proposing makes some sense. I get very lost in the utemplate code so I'm not optimistic being able to translate the feature request into a patch without some help.
I think it cashes out as needing the names of included templates to be able to be computed themselves by runtime values. It's not the full power of 'eval' (which would be incompatible with the design intent of utemplate), but it would allow runtime state to parameterise the call to the template resolver, e.g. when iterating over lists or dicts.
This is not eval, every
include
would still end up backed by a named template from the resolver, with the potential that it hits a cache (e.g. a pre-compiled frozen module)). However, my workaround DOES effectively use eval, and is horrible.Roughly speaking, instead of being limited to just...
{% include 'subpage' %}
...you could have extra expressive power by having computable template names...
{% for key in keys %}{% include 'page{{key}}' %}{% endfor %}
BACKGROUND
Our templating strategy is illustrated by this line from https://github.com/cefn/avatap/blob/master/python/milecastles.py which defines arguments available to all render functions
signature = "engine, story, box, node, card, sack"
This signature provides access to the text adventure engine, the whole story, the box the player has tapped on (these are RFID-sensing boxes in a museum), the node the player is at in the story, the card information of the player, and the player's sack (an inventory 'dict' loaded from their RFID)...
REQUIREMENT
The application requirement is illustrated by the (working) templates in the code-fragment under EVIL NESTED EVAL CODE below. To provide template authors with the needed features for our text adventure game, I have been forced to 'step outside' the template logic, and call the templating engine directly from within the render function. This implies a bit of an explosion of memory overhead, since it means eval is being called within eval! It also means that the embedded template is always evaled based on a runtime string and therefore can't be cached.
The motivation for the case is that authors write readable text used to render each choice you can make in the adventure (of one-to-many choices named by uid). However, these texts in turn use templating features, so they can't merely be static text, they should be loaded through the resolver/compiler with
include
.Typically these parameterised template names will depend on a key from a Jinja2-style iteration. Our issue is that while the resolver can be augmented to handle special names like 'choices[choiceUid]' or 'choices[2]' or just 'choices_7' and can grab different backing templates in each parameterised case, there's no way to pass these values through
include
currently. Consequently I am using a reference to the templating engine directly within the template, and I am simply passing the template text (which can be accessed via Jinja2) to the engine from within the render function!EVIL NESTED EVAL CODE
This is so wrong, but it's ended up unavoidably unpacking this way. I wonder whether parameterised includes might offer important extra power without surrendering the efficiency/low overhead of cached generators. This would certainly fix our immediate problem.
DESIGN ISSUE
Of course, all of this implies that the resolver plays a part in the runtime execution of a render() function. I don't know if this is currently the case, or whether all includes are actually expanded by simply inlining at compile time. If all includes are always inlined, could it make sense to reverse this design decision, and keep the resolver in the loop even at runtime to get this extra power? It would also minimise duplication from embedding the same templates inline in multiple places.