############################################################################### # Copyright: Red Hat, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. ############################################################################### from django import template register = template.Library() TemplateSyntaxError = template.TemplateSyntaxError class RecurseNode(template.Node): def __init__(self, params): self.params = params def render(self, context): try: recurse_ctx = context['_recurse_ctx'] except KeyError: raise TemplateSyntaxError("{0} found outside recursion context") args = [param.resolve(context) for param in self.params] return recurse_ctx._render(context, *args) class RecurseDefinitionNode(template.Node): def __init__(self, params, nodelist): self.params = params self.nodelist = nodelist def _render(self, context, *args): context.push() context['level'] = context.get('level', -1) + 1 context['_recurse_ctx'] = self try: if args and len(args) != len(self.params): raise IndexError for i, arg in enumerate(args): context[self.params[i]] = arg except IndexError: raise TemplateSyntaxError("Number of arguments passed to recurse " "do not match defrecurse") output = self.nodelist.render(context) context.pop() return output def render(self, context): return self._render(context) @register.tag def defrecurse(parser, token): """ Recursively render things in Django templates. Usage: {% defrecurse param... %} {% enddefrecurse %} """ params = token.split_contents() tag_name = params.pop(0) nodelist = parser.parse(('enddefrecurse',)) parser.delete_first_token() if not nodelist.get_nodes_by_type(RecurseNode): raise TemplateSyntaxError( "No recursion inside {0} block".format(tag_name)) return RecurseDefinitionNode(params, nodelist) @register.tag def recurse(parser, token): params = token.split_contents() tag_name = params.pop(0) params = [parser.compile_filter(param) for param in params] return RecurseNode(params)