コード例 #1
0
    def render(self, context):
        if 'forloop' in context:
            parentloop = context['forloop']
        else:
            parentloop = {}
        context.push()
        try:
            values = self.sequence.resolve(context, True)
        except VariableDoesNotExist:
            values = []
        if values is None:
            values = []
        if not hasattr(values, '__len__'):
            values = list(values)
        len_values = len(values)
        if len_values < 1:
            context.pop()
            return self.nodelist_empty.render(context)
        nodelist = NodeList()
        if self.is_reversed:
            values = reversed(values)
        unpack = len(self.loopvars) > 1
        # Create a forloop value in the context.  We'll update counters on each
        # iteration just below.
        loop_dict = context['forloop'] = {'parentloop': parentloop}
        for i, item in enumerate(values):
            # Shortcuts for current loop iteration number.
            loop_dict['counter0'] = i
            loop_dict['counter'] = i + 1
            # Reverse counter iteration numbers.
            loop_dict['revcounter'] = len_values - i
            loop_dict['revcounter0'] = len_values - i - 1
            # Boolean values designating first and last times through loop.
            loop_dict['first'] = (i == 0)
            loop_dict['last'] = (i == len_values - 1)

            pop_context = False
            if unpack:
                # If there are multiple loop variables, unpack the item into
                # them.
                try:
                    unpacked_vars = dict(zip(self.loopvars, item))
                except TypeError:
                    pass
                else:
                    pop_context = True
                    context.update(unpacked_vars)
            else:
                context[self.loopvars[0]] = item
            for node in self.nodelist_loop:
                nodelist.append(node.render(context))
            if pop_context:
                # The loop variables were pushed on to the context so pop them
                # off again. This is necessary because the tag lets the length
                # of loopvars differ to the length of each set of items and we
                # don't want to leave any vars from the previous loop on the
                # context.
                context.pop()
        context.pop()
        return nodelist.render(context)
コード例 #2
0
    def render(self, slots_filled=None, *args, **kwargs):
        slots_filled = slots_filled or []
        context_args_variables = getfullargspec(self.context).args[1:]
        context_args = {
            key: kwargs[key]
            for key in context_args_variables if key in kwargs
        }
        context = self.context(**context_args)
        template = get_template(self.template(context))
        slots_in_template = self.slots_in_template(template)

        if slots_in_template:
            valid_slot_names = set([slot.name for slot in slots_in_template])
            nodelist = NodeList()
            for node in template.template.nodelist:
                if (node.token.token_type == TokenType.BLOCK
                        and node.token.split_contents()[0] == "slot"):
                    if node.name in valid_slot_names and node.name in slots_filled:
                        nodelist.append(TextNode(slots_filled[node.name]))
                    else:
                        for node in node.nodelist:
                            nodelist.append(node)
                else:
                    nodelist.append(node)

            render_context = Context(context)
            with render_context.bind_template(template.template):
                return nodelist.render(render_context)

        return template.render(context)
コード例 #3
0
ファイル: defaulttags.py プロジェクト: crazyoldman/SOSpy
    def render(self, context):
        if 'forloop' in context:
            parentloop = context['forloop']
        else:
            parentloop = {}
        context.push()
        try:
            values = self.sequence.resolve(context, True)
        except VariableDoesNotExist:
            values = []
        if values is None:
            values = []
        if not hasattr(values, '__len__'):
            values = list(values)
        len_values = len(values)
        if len_values < 1:
            context.pop()
            return self.nodelist_empty.render(context)
        nodelist = NodeList()
        if self.is_reversed:
            values = reversed(values)
        unpack = len(self.loopvars) > 1
        # Create a forloop value in the context.  We'll update counters on each
        # iteration just below.
        loop_dict = context['forloop'] = {'parentloop': parentloop}
        for i, item in enumerate(values):
            # Shortcuts for current loop iteration number.
            loop_dict['counter0'] = i
            loop_dict['counter'] = i+1
            # Reverse counter iteration numbers.
            loop_dict['revcounter'] = len_values - i
            loop_dict['revcounter0'] = len_values - i - 1
            # Boolean values designating first and last times through loop.
            loop_dict['first'] = (i == 0)
            loop_dict['last'] = (i == len_values - 1)

            pop_context = False
            if unpack:
                # If there are multiple loop variables, unpack the item into
                # them.
                try:
                    unpacked_vars = dict(zip(self.loopvars, item))
                except TypeError:
                    pass
                else:
                    pop_context = True
                    context.update(unpacked_vars)
            else:
                context[self.loopvars[0]] = item
            for node in self.nodelist_loop:
                nodelist.append(node.render(context))
            if pop_context:
                # The loop variables were pushed on to the context so pop them
                # off again. This is necessary because the tag lets the length
                # of loopvars differ to the length of each set of items and we
                # don't want to leave any vars from the previous loop on the
                # context.
                context.pop()
        context.pop()
        return nodelist.render(context)
コード例 #4
0
ファイル: core_admin_tags.py プロジェクト: timrc/schproj
   def render(self, context):
      permissions = [permission.resolve(context, True) for permission in self.permissions]

      #
      #  check if has permissions
      #
      from core.helper import get_request
      from core.permission import check_permissions
      status, action = check_permissions(get_request(), permissions)

      nodelist = NodeList()
      if status:
         for node in self.nodelist_permission:
            try:
               nodelist.append(node.render(context))
            except Exception as e:
               if not hasattr(e, 'django_template_source'):
                  e.django_template_source = node.source
               raise
      else:
         if self.nodelist_nopermission:
            for node in self.nodelist_nopermission:
               try:
                  nodelist.append(node.render(context))
               except Exception as e:
                  if not hasattr(e, 'django_template_source'):
                     e.django_template_source = node.source
                  raise

      return nodelist.render(context)
コード例 #5
0
 def render(self, context):
     context.push();
     kwargs = self.resolve_kwargs(context)
     items = Item.objects(type=kwargs['type']).order_by('-itemMeta.versionCreated')[:kwargs['length']]
     nodelist = NodeList()
     for item in items:
         context['item'] = item
         for node in self.nodelist:
             nodelist.append(node.render(context))
     context.pop()
     return nodelist.render(context)
コード例 #6
0
 def render(self, context):
     context.push()
     items = self.get_items(context)
     nodelist = NodeList()
     context['package'] = context['item']
     first = True
     for item in items:
         context['item'] = item
         context['first'] = first
         for node in self.nodelist:
             nodelist.append(node.render(context))
         first = False
     context.pop()
     return nodelist.render(context)
コード例 #7
0
 def render(self, context):
     context.push()
     nodelist = NodeList()
     kwargs = self.resolve_kwargs(context)
     limit = kwargs.get('limit', 55)
     start = kwargs.get('start', 0)
     order = kwargs.get('order', '-versionCreated')
     items = Item.objects(itemClass=kwargs['class'],publishedOn__ne=None).order_by(order)[start:limit]
     for item in items:
         context['item'] = item
         for node in self.nodelist:
             nodelist.append(node.render(context))
     context.pop()
     return nodelist.render(context)
コード例 #8
0
ファイル: core_admin_tags.py プロジェクト: timrc/schproj
   def render(self, context):
      from core.helper import get_request
      request = get_request()
      context['request'] = request
      context['wepo'] = request.wepo

      nodelist = NodeList()

      for node in self.nodelist_field:
         try:
            nodelist.append(node.render(context))
         except Exception as e:
            if not hasattr(e, 'django_template_source'):
               e.django_template_source = node.source
            raise

      return nodelist.render(context)
コード例 #9
0
ファイル: core_admin_tags.py プロジェクト: timrc/schproj
   def render(self, context):
      section = self.section.resolve(context, True)
      page = self.page.resolve(context, True)

      import json
      page_blocks = page.blocks if page and page.blocks else []
      page_section = page_blocks[section] if section in page_blocks else []

      nodelist = NodeList()

      context[self.output_var] = page_section

      for node in self.nodelist_field:
         try:
            nodelist.append(node.render(context))
         except Exception as e:
            if not hasattr(e, 'django_template_source'):
               e.django_template_source = node.source
            raise

      return nodelist.render(context)
コード例 #10
0
class ForNode(Node):
    child_nodelists = ('nodelist_loop', 'nodelist_empty')

    def __init__(self,
                 loopvars,
                 sequence,
                 is_reversed,
                 nodelist_loop,
                 nodelist_empty=None):
        self.loopvars, self.sequence = loopvars, sequence
        self.is_reversed = is_reversed
        self.nodelist_loop = nodelist_loop
        if nodelist_empty is None:
            self.nodelist_empty = NodeList()
        else:
            self.nodelist_empty = nodelist_empty

    def __repr__(self):
        reversed_text = self.is_reversed and ' reversed' or ''
        return "<For Node: for %s in %s, tail_len: %d%s>" % \
            (', '.join(self.loopvars), self.sequence, len(self.nodelist_loop),
             reversed_text)

    def __iter__(self):
        for node in self.nodelist_loop:
            yield node
        for node in self.nodelist_empty:
            yield node

    def render(self, context):
        if 'forloop' in context:
            parentloop = context['forloop']
        else:
            parentloop = {}
        context.push()
        try:
            values = self.sequence.resolve(context, True)
        except VariableDoesNotExist:
            values = []
        if values is None:
            values = []
        if not hasattr(values, '__len__'):
            values = list(values)
        len_values = len(values)
        if len_values < 1:
            context.pop()
            return self.nodelist_empty.render(context)
        nodelist = NodeList()
        if self.is_reversed:
            values = reversed(values)
        unpack = len(self.loopvars) > 1
        # Create a forloop value in the context.  We'll update counters on each
        # iteration just below.
        loop_dict = context['forloop'] = {'parentloop': parentloop}
        for i, item in enumerate(values):
            # Shortcuts for current loop iteration number.
            loop_dict['counter0'] = i
            loop_dict['counter'] = i + 1
            # Reverse counter iteration numbers.
            loop_dict['revcounter'] = len_values - i
            loop_dict['revcounter0'] = len_values - i - 1
            # Boolean values designating first and last times through loop.
            loop_dict['first'] = (i == 0)
            loop_dict['last'] = (i == len_values - 1)

            pop_context = False
            if unpack:
                # If there are multiple loop variables, unpack the item into
                # them.
                try:
                    unpacked_vars = dict(zip(self.loopvars, item))
                except TypeError:
                    pass
                else:
                    pop_context = True
                    context.update(unpacked_vars)
            else:
                context[self.loopvars[0]] = item
            # In TEMPLATE_DEBUG mode provide source of the node which
            # actually raised the exception
            if settings.TEMPLATE_DEBUG:
                for node in self.nodelist_loop:
                    try:
                        nodelist.append(node.render(context))
                    except Exception as e:
                        if not hasattr(e, 'django_template_source'):
                            e.django_template_source = node.source
                        raise
            else:
                for node in self.nodelist_loop:
                    nodelist.append(node.render(context))
            if pop_context:
                # The loop variables were pushed on to the context so pop them
                # off again. This is necessary because the tag lets the length
                # of loopvars differ to the length of each set of items and we
                # don't want to leave any vars from the previous loop on the
                # context.
                context.pop()
        context.pop()
        return nodelist.render(context)
コード例 #11
0
ファイル: defaulttags.py プロジェクト: Bogh/django
class ForNode(Node):
    child_nodelists = ('nodelist_loop', 'nodelist_empty')

    def __init__(self, loopvars, sequence, is_reversed, nodelist_loop, nodelist_empty=None):
        self.loopvars, self.sequence = loopvars, sequence
        self.is_reversed = is_reversed
        self.nodelist_loop = nodelist_loop
        if nodelist_empty is None:
            self.nodelist_empty = NodeList()
        else:
            self.nodelist_empty = nodelist_empty

    def __repr__(self):
        reversed_text = ' reversed' if self.is_reversed else ''
        return "<For Node: for %s in %s, tail_len: %d%s>" % \
            (', '.join(self.loopvars), self.sequence, len(self.nodelist_loop),
             reversed_text)

    def __iter__(self):
        for node in self.nodelist_loop:
            yield node
        for node in self.nodelist_empty:
            yield node

    def render(self, context):
        if 'forloop' in context:
            parentloop = context['forloop']
        else:
            parentloop = {}
        with context.push():
            try:
                values = self.sequence.resolve(context, True)
            except VariableDoesNotExist:
                values = []
            if values is None:
                values = []
            if not hasattr(values, '__len__'):
                values = list(values)
            len_values = len(values)
            if len_values < 1:
                return self.nodelist_empty.render(context)
            nodelist = []
            if self.is_reversed:
                values = reversed(values)
            unpack = len(self.loopvars) > 1
            # Create a forloop value in the context.  We'll update counters on each
            # iteration just below.
            loop_dict = context['forloop'] = {'parentloop': parentloop}
            for i, item in enumerate(values):
                # Shortcuts for current loop iteration number.
                loop_dict['counter0'] = i
                loop_dict['counter'] = i + 1
                # Reverse counter iteration numbers.
                loop_dict['revcounter'] = len_values - i
                loop_dict['revcounter0'] = len_values - i - 1
                # Boolean values designating first and last times through loop.
                loop_dict['first'] = (i == 0)
                loop_dict['last'] = (i == len_values - 1)

                pop_context = False
                if unpack:
                    # If there are multiple loop variables, unpack the item into
                    # them.
                    try:
                        unpacked_vars = dict(zip(self.loopvars, item))
                    except TypeError:
                        pass
                    else:
                        pop_context = True
                        context.update(unpacked_vars)
                else:
                    context[self.loopvars[0]] = item
                # In TEMPLATE_DEBUG mode provide source of the node which
                # actually raised the exception
                if settings.TEMPLATE_DEBUG:
                    for node in self.nodelist_loop:
                        try:
                            nodelist.append(node.render(context))
                        except Exception as e:
                            if not hasattr(e, 'django_template_source'):
                                e.django_template_source = node.source
                            raise
                else:
                    for node in self.nodelist_loop:
                        nodelist.append(node.render(context))
                if pop_context:
                    # The loop variables were pushed on to the context so pop them
                    # off again. This is necessary because the tag lets the length
                    # of loopvars differ to the length of each set of items and we
                    # don't want to leave any vars from the previous loop on the
                    # context.
                    context.pop()
        return mark_safe(''.join(force_text(n) for n in nodelist))
コード例 #12
0
class TableNode(Node):
    child_nodelists = ('nodelist_headers', 'nodelist_loop', 'nodelist_empty')

    def __init__(self, loopvars, sequence, is_reversed, nodelist_headers, nodelist_loop, nodelist_empty=None):        
        self.loopvars, self.sequence = loopvars, sequence
        self.is_reversed = is_reversed
        self.nodelist_headers, self.nodelist_loop = nodelist_headers, nodelist_loop
        if nodelist_empty is None:
            self.nodelist_empty = NodeList()
        else:
            self.nodelist_empty = nodelist_empty


    def __repr__(self):
        reversed_text = ' reversed' if self.is_reversed else ''
        return "<Table Node: ui_table %s in %s, tail_len: %d%s>" % \
            (', '.join(self.loopvars), self.sequence, len(self.nodelist_loop),
             reversed_text)

    def __iter__(self):        
        for node in self.nodelist_loop:
            yield node
        for node in self.nodelist_empty:
            yield node

    def render(self, context):        
        self.nodelist_headers = [node for node in self.nodelist_headers if not isinstance(node, TextNode)]
        self.nodelist_loop = [node for node in self.nodelist_loop if not isinstance(node, TextNode)]

        len_nodelist_headers = len(self.nodelist_headers)
        len_nodelist_loop = len(self.nodelist_loop)

        if len_nodelist_headers != len_nodelist_loop:
            raise ValueError(
                "There are {} headers and {} column values, need the same length"
                .format(len_nodelist_headers, len_nodelist_loop),
            )

        if 'forloop' in context:
            parentloop = context['forloop']
        else:
            parentloop = {}
        with context.push():
            try:
                values = self.sequence.resolve(context, True)
            except VariableDoesNotExist:
                values = []
            if values is None:
                values = []
            if not hasattr(values, '__len__'):
                values = list(values)
            len_values = len(values)            
            if len_values < 1:
                return self.nodelist_empty.render(context)
            
            if self.is_reversed:
                values = reversed(values)
            num_loopvars = len(self.loopvars)
            unpack = num_loopvars > 1
            # Create a forloop value in the context.  We'll update counters on each
            # iteration just below.
            loop_dict = context['forloop'] = {'parentloop': parentloop}            

            rows = []
            for i, item in enumerate(values):
                # Shortcuts for current loop iteration number.                
                loop_dict['counter'] = i + 1
                # Reverse counter iteration numbers.
                loop_dict['revcounter'] = len_values - i                
                # Boolean values designating first and last times through loop.
                loop_dict['first'] = (i == 0)
                loop_dict['last'] = (i == len_values - 1)

                pop_context = False
                if unpack:
                    # If there are multiple loop variables, unpack the item into
                    # them.
                    try:
                        len_item = len(item)
                    except TypeError:  # not an iterable
                        len_item = 1
                    # Check loop variable count before unpacking
                    if num_loopvars != len_item:
                        raise ValueError(
                            "Need {} values to unpack in for loop; got {}. "
                            .format(num_loopvars, len_item),
                        )
                    unpacked_vars = dict(zip(self.loopvars, item))
                    pop_context = True
                    context.update(unpacked_vars)
                else:                    
                    context[self.loopvars[0]] = item                                

                row = []
                for node in self.nodelist_loop:                    
                    result = node.render_annotated(context)                    
                    t = context.template.engine.get_template('ui_components/table/td.html')
                    td = t.render(Context({'td': result}, autoescape=context.autoescape))                                        
                    row.append(td)

                t = context.template.engine.get_template('ui_components/table/tr.html')
                tr = t.render(Context({'row': row}, autoescape=context.autoescape))
                rows.append(tr)

                if pop_context:
                    # The loop variables were pushed on to the context so pop them
                    # off again. This is necessary because the tag lets the length
                    # of loopvars differ to the length of each set of items and we
                    # don't want to leave any vars from the previous loop on the
                    # context.
                    context.pop()
            
            headers = []
            for node in self.nodelist_headers:
                header = node.render_annotated(context)
                headers.append(header)            

            t = context.template.engine.get_template('ui_components/table/table.html')
            table = t.render(Context({ 'headers':headers, 'rows': rows}, autoescape=context.autoescape))            
        
        return mark_safe(table)
コード例 #13
0
ファイル: defaulttags.py プロジェクト: zizaipai/django
class ForNode(Node):
    child_nodelists = ('nodelist_loop', 'nodelist_empty')

    def __init__(self,
                 loopvars,
                 sequence,
                 is_reversed,
                 nodelist_loop,
                 nodelist_empty=None):
        self.loopvars, self.sequence = loopvars, sequence
        self.is_reversed = is_reversed
        self.nodelist_loop = nodelist_loop
        if nodelist_empty is None:
            self.nodelist_empty = NodeList()
        else:
            self.nodelist_empty = nodelist_empty

    def __repr__(self):
        reversed_text = ' reversed' if self.is_reversed else ''
        return "<For Node: for %s in %s, tail_len: %d%s>" % \
            (', '.join(self.loopvars), self.sequence, len(self.nodelist_loop),
             reversed_text)

    def __iter__(self):
        for node in self.nodelist_loop:
            yield node
        for node in self.nodelist_empty:
            yield node

    def render(self, context):
        if 'forloop' in context:
            parentloop = context['forloop']
        else:
            parentloop = {}
        with context.push():
            try:
                values = self.sequence.resolve(context, True)
            except VariableDoesNotExist:
                values = []
            if values is None:
                values = []
            if not hasattr(values, '__len__'):
                values = list(values)
            len_values = len(values)
            if len_values < 1:
                return self.nodelist_empty.render(context)
            nodelist = []
            if self.is_reversed:
                values = reversed(values)
            num_loopvars = len(self.loopvars)
            unpack = num_loopvars > 1
            # Create a forloop value in the context.  We'll update counters on each
            # iteration just below.
            loop_dict = context['forloop'] = {'parentloop': parentloop}
            for i, item in enumerate(values):
                # Shortcuts for current loop iteration number.
                loop_dict['counter0'] = i
                loop_dict['counter'] = i + 1
                # Reverse counter iteration numbers.
                loop_dict['revcounter'] = len_values - i
                loop_dict['revcounter0'] = len_values - i - 1
                # Boolean values designating first and last times through loop.
                loop_dict['first'] = (i == 0)
                loop_dict['last'] = (i == len_values - 1)

                pop_context = False
                if unpack:
                    # If there are multiple loop variables, unpack the item into
                    # them.

                    # To complete this deprecation, remove from here to the
                    # try/except block as well as the try/except itself,
                    # leaving `unpacked_vars = ...` and the "else" statements.
                    if not isinstance(item, (list, tuple)):
                        len_item = 1
                    else:
                        len_item = len(item)
                    # Check loop variable count before unpacking
                    if num_loopvars != len_item:
                        warnings.warn(
                            "Need {0} values to unpack in for loop; got {1}. "
                            "This will raise an exception in Django 2.0.".
                            format(num_loopvars,
                                   len_item), RemovedInDjango20Warning)
                    try:
                        unpacked_vars = dict(zip(self.loopvars, item))
                    except TypeError:
                        pass
                    else:
                        pop_context = True
                        context.update(unpacked_vars)
                else:
                    context[self.loopvars[0]] = item
                # In TEMPLATE_DEBUG mode provide source of the node which
                # actually raised the exception
                if settings.TEMPLATE_DEBUG:
                    for node in self.nodelist_loop:
                        try:
                            nodelist.append(node.render(context))
                        except Exception as e:
                            if not hasattr(e, 'django_template_source'):
                                e.django_template_source = node.source
                            raise
                else:
                    for node in self.nodelist_loop:
                        nodelist.append(node.render(context))
                if pop_context:
                    # The loop variables were pushed on to the context so pop them
                    # off again. This is necessary because the tag lets the length
                    # of loopvars differ to the length of each set of items and we
                    # don't want to leave any vars from the previous loop on the
                    # context.
                    context.pop()
        return mark_safe(''.join(force_text(n) for n in nodelist))
コード例 #14
0
    def render(self, context):
        if "forloop" in context:
            parentloop = context["forloop"]
        else:
            parentloop = {}
        context.push()
        try:
            values = self.sequence.resolve(context, True)
        except VariableDoesNotExist:
            values = []
        if values is None:
            values = []
        if not hasattr(values, "__len__"):
            values = list(values)
        len_values = len(values)
        if len_values < 1:
            context.pop()
            return self.nodelist_empty.render(context)
        nodelist = NodeList()
        if self.is_reversed:
            values = reversed(values)
        unpack = len(self.loopvars) > 1
        # Create a forloop value in the context.  We'll update counters on each
        # iteration just below.
        loop_dict = context["forloop"] = {"parentloop": parentloop}
        for i, item in enumerate(values):
            # Shortcuts for current loop iteration number.
            loop_dict["counter0"] = i
            loop_dict["counter"] = i + 1
            # Reverse counter iteration numbers.
            loop_dict["revcounter"] = len_values - i
            loop_dict["revcounter0"] = len_values - i - 1
            # Boolean values designating first and last times through loop.
            loop_dict["first"] = i == 0
            loop_dict["last"] = i == len_values - 1

            pop_context = False
            if unpack:
                # If there are multiple loop variables, unpack the item into
                # them.
                try:
                    unpacked_vars = dict(zip(self.loopvars, item))
                except TypeError:
                    pass
                else:
                    pop_context = True
                    context.update(unpacked_vars)
            else:
                context[self.loopvars[0]] = item
            # In TEMPLATE_DEBUG mode provide source of the node which
            # actually raised the exception
            if settings.TEMPLATE_DEBUG:
                for node in self.nodelist_loop:
                    try:
                        nodelist.append(node.render(context))
                    except Exception as e:
                        if not hasattr(e, "django_template_source"):
                            e.django_template_source = node.source
                        raise
            else:
                for node in self.nodelist_loop:
                    nodelist.append(node.render(context))
            if pop_context:
                # The loop variables were pushed on to the context so pop them
                # off again. This is necessary because the tag lets the length
                # of loopvars differ to the length of each set of items and we
                # don't want to leave any vars from the previous loop on the
                # context.
                context.pop()
        context.pop()
        return nodelist.render(context)
コード例 #15
0
ファイル: defaulttags.py プロジェクト: pbs-education/django
class ForNode(Node):
    child_nodelists = ("nodelist_loop", "nodelist_empty")

    def __init__(self, loopvars, sequence, is_reversed, nodelist_loop, nodelist_empty=None):
        self.loopvars, self.sequence = loopvars, sequence
        self.is_reversed = is_reversed
        self.nodelist_loop = nodelist_loop
        if nodelist_empty is None:
            self.nodelist_empty = NodeList()
        else:
            self.nodelist_empty = nodelist_empty

    def __repr__(self):
        reversed_text = self.is_reversed and " reversed" or ""
        return "<For Node: for %s in %s, tail_len: %d%s>" % (
            ", ".join(self.loopvars),
            self.sequence,
            len(self.nodelist_loop),
            reversed_text,
        )

    def __iter__(self):
        for node in self.nodelist_loop:
            yield node
        for node in self.nodelist_empty:
            yield node

    def render(self, context):
        if "forloop" in context:
            parentloop = context["forloop"]
        else:
            parentloop = {}
        context.push()
        try:
            values = self.sequence.resolve(context, True)
        except VariableDoesNotExist:
            values = []
        if values is None:
            values = []
        if not hasattr(values, "__len__"):
            values = list(values)
        len_values = len(values)
        if len_values < 1:
            context.pop()
            return self.nodelist_empty.render(context)
        nodelist = NodeList()
        if self.is_reversed:
            values = reversed(values)
        unpack = len(self.loopvars) > 1
        # Create a forloop value in the context.  We'll update counters on each
        # iteration just below.
        loop_dict = context["forloop"] = {"parentloop": parentloop}
        for i, item in enumerate(values):
            # Shortcuts for current loop iteration number.
            loop_dict["counter0"] = i
            loop_dict["counter"] = i + 1
            # Reverse counter iteration numbers.
            loop_dict["revcounter"] = len_values - i
            loop_dict["revcounter0"] = len_values - i - 1
            # Boolean values designating first and last times through loop.
            loop_dict["first"] = i == 0
            loop_dict["last"] = i == len_values - 1

            pop_context = False
            if unpack:
                # If there are multiple loop variables, unpack the item into
                # them.
                try:
                    unpacked_vars = dict(zip(self.loopvars, item))
                except TypeError:
                    pass
                else:
                    pop_context = True
                    context.update(unpacked_vars)
            else:
                context[self.loopvars[0]] = item
            for node in self.nodelist_loop:
                nodelist.append(node.render(context))
            if pop_context:
                # The loop variables were pushed on to the context so pop them
                # off again. This is necessary because the tag lets the length
                # of loopvars differ to the length of each set of items and we
                # don't want to leave any vars from the previous loop on the
                # context.
                context.pop()
        context.pop()
        return nodelist.render(context)