Exemple #1
0
def parse_tag(token, parser):
    """Parses template tag for name, arguments and keyword arguments.

    :param token: Template token containing all the tag contents
    :type token: django.template.base.Token
    :param parser: Template parser
    :type parser: django.template.base.Parser
    :return: Tuple with tag name, arguments and keyword arguments
    :rtype: tuple

    """
    # Split the tag content into words, respecting quoted strings.
    bits = token.split_contents()

    # Pull out the tag name.
    tag_name = bits.pop(0)

    # Parse the rest of the args, and build FilterExpressions from them so that
    # we can evaluate them later.
    args = []
    kwargs = {}
    for bit in bits:
        # Is this a kwarg or an arg?
        match = kwarg_re.match(bit)
        kwarg_format = match and match.group(1)
        if kwarg_format:
            key, value = match.groups()
            kwargs[key] = FilterExpression(value, parser)
        else:
            args.append(FilterExpression(bit, parser))

    return (tag_name, args, kwargs)
Exemple #2
0
    def test_filter_parsing(self):
        c = {"article": {"section": "News"}}
        p = Parser("", builtins=[filter_library])

        def fe_test(s, val):
            self.assertEqual(FilterExpression(s, p).resolve(c), val)

        fe_test("article.section", "News")
        fe_test("article.section|upper", "NEWS")
        fe_test('"News"', "News")
        fe_test("'News'", "News")
        fe_test(r'"Some \"Good\" News"', 'Some "Good" News')
        fe_test(r'"Some \"Good\" News"', 'Some "Good" News')
        fe_test(r"'Some \'Bad\' News'", "Some 'Bad' News")

        fe = FilterExpression(r'"Some \"Good\" News"', p)
        self.assertEqual(fe.filters, [])
        self.assertEqual(fe.var, 'Some "Good" News')

        # Filtered variables should reject access of attributes beginning with
        # underscores.
        msg = (
            "Variables and attributes may not begin with underscores: 'article._hidden'"
        )
        with self.assertRaisesMessage(TemplateSyntaxError, msg):
            FilterExpression("article._hidden|upper", p)
Exemple #3
0
def do_usemacro(parser, token):
    try:
        args = token.split_contents()
        macro_name, values = args[1], args[2:]
    except IndexError:
        m = ("'%s' tag requires at least one argument (macro name)" %
             token.contents.split()[0])
        raise template.TemplateSyntaxError(m)
    try:
        macro = parser._macros[macro_name]
    except (AttributeError, KeyError):
        m = "Macro '%s' is not defined" % macro_name
        raise template.TemplateSyntaxError(m)

    fe_kwargs = {}
    fe_args = []

    for val in values:
        if "=" in val:
            # kwarg
            name, value = val.split("=")
            fe_kwargs[name] = FilterExpression(value, parser)
        else:  # arg
            # no validation, go for it ...
            fe_args.append(FilterExpression(val, parser))

    macro.parser = parser
    return UseMacroNode(macro, fe_args, fe_kwargs)
class iffchangedNode(template.Node):
    def __init__(self, parser, condition, true_value, false_value, asvar=None):
        self.condition = FilterExpression(condition, parser)

        self.true_value = FilterExpression(true_value, parser)
        self.false_value = FilterExpression(false_value, parser)
        self.asvar = asvar

    def render(self, context):
        if self not in context.render_context:
            context.render_context[self] = {'condition': self.condition.resolve(context)}
            if self.asvar:
                context[self.asvar] = self.true_value.resolve(context)
            return self.true_value.resolve(context)
        else:
            if context.render_context[self]['condition'] == self.condition.resolve(context):
                if self.asvar:
                    context[self.asvar] = self.false_value.resolve(context)
                else:
                    return self.false_value.resolve(context)
            else:
                context.render_context[self]['condition'] = self.condition.resolve(context)
                if self.asvar:
                    context[self.asvar] = self.true_value.resolve(context)
                else:
                    return self.true_value.resolve(context)

        return ''
Exemple #5
0
def interpolate(parser, tokens):
    ''' make django.template.Variable's from the input tokens '''
    args = []
    kwargs = {}
    for token in tokens:
        match = kwarg_re.match(token)
        if match:
            kwargs[match.group(1)] = FilterExpression(match.group(2), parser)
        else:
            args.append(FilterExpression(token, parser))

    return (args, kwargs)
def parse_tag_args(parser, parts):
    args = []
    kwargs = {}
    for part in parts:
        match = kwarg_re.match(part)
        kwarg_format = match and match.group(1)
        if kwarg_format:
            key, value = match.groups()
            kwargs[key] = FilterExpression(value, parser)
        else:
            args.append(FilterExpression(part, parser))

    return args, kwargs
def do_get_latest_objects_by_category(parser, token):
    """
    Get the latest objects by category

    {% get_latest_objects_by_category category app_name model_name set_name [date_field] [number] as [var_name] %}
    """
    proper_form = "{% get_latest_objects_by_category category app_name model_name set_name [date_field] [number] as [var_name] %}"
    bits = token.split_contents()

    if bits[-2] != 'as':
        raise TemplateSyntaxError("%s tag shoud be in the form: %s" %
                                  (bits[0], proper_form))
    if len(bits) < 7:
        raise TemplateSyntaxError("%s tag shoud be in the form: %s" %
                                  (bits[0], proper_form))
    if len(bits) > 9:
        raise TemplateSyntaxError("%s tag shoud be in the form: %s" %
                                  (bits[0], proper_form))
    category = FilterExpression(bits[1], parser)
    app_label = FilterExpression(bits[2], parser)
    model_name = FilterExpression(bits[3], parser)
    set_name = FilterExpression(bits[4], parser)
    var_name = bits[-1]
    if bits[5] != 'as':
        date_field = FilterExpression(bits[5], parser)
    else:
        date_field = FilterExpression(None, parser)
    if bits[6] != 'as':
        num = FilterExpression(bits[6], parser)
    else:
        num = FilterExpression(None, parser)
    return LatestObjectsNode(var_name, category, app_label, model_name,
                             set_name, date_field, num)
Exemple #8
0
def parse_args_kwargs(parser, bits):
    # Parse the rest of the args, and build FilterExpressions from them so that
    # we can evaluate them later.
    args = []
    kwargs = {}
    for bit in bits:
        # Is this a kwarg or an arg?
        match = kwarg_re.match(bit)
        kwarg_format = match and match.group(1)
        if kwarg_format:
            key, value = match.groups()
            kwargs[key] = FilterExpression(value, parser)
        else:
            args.append(FilterExpression(bit, parser))
    return args, kwargs
def parse_tag(token, parser):
    bits = token.split_contents()
    tag_name = bits.pop(0)
    args = []
    kwargs = {}
    for bit in bits:
        match = kwarg_re.match(bit)
        kwarg_format = match and match.group(1)
        if kwarg_format:
            key, value = match.groups()
            kwargs[key] = FilterExpression(value, parser)
        else:
            args.append(FilterExpression(bit, parser))

    return (tag_name, args, kwargs)
def recursetree(parser, token):
    """
    Iterates over the nodes in the tree, and renders the contained block for each node.
    This tag will recursively render children into the template variable {{ children }}.
    Only one database query is required (children are cached for the whole tree)

    Usage:
            <ul>
                {% recursetree nodes %}
                    <li>
                        {{ node.name }}
                        {% if not node.is_leaf_node %}
                            <ul>
                                {{ children }}
                            </ul>
                        {% endif %}
                    </li>
                {% endrecursetree %}
            </ul>
    """
    bits = token.contents.split()
    if len(bits) != 2:
        raise template.TemplateSyntaxError('%s tag requires a queryset' %
                                           bits[0])
    queryset_var = FilterExpression(bits[1], parser)

    template_nodes = parser.parse(('endrecursetree', ))
    parser.delete_first_token()

    return RecurseTreeNode(template_nodes, queryset_var)
Exemple #11
0
    def render(self, context):

        for i, arg in enumerate(self.macro.args):
            try:
                fe = self.fe_args[i]
                context[arg] = fe.resolve(context)
            except IndexError:
                context[arg] = ""

        for name, default in self.macro.kwargs.items():
            if name in self.fe_kwargs:
                context[name] = self.fe_kwargs[name].resolve(context)
            else:
                context[name] = FilterExpression(default,
                                                 self.macro.parser
                                                 ).resolve(context)

        # # Place output into context variable
        # context[self.macro.name] = self.macro.nodelist.render(context)
        # return '' if self.context_only else context[self.macro.name]

        string = self.macro.nodelists[0].render(context)
        for nl in self.macro.nodelists[1:]:
            # render macro body
            string += self.nodelist.render(context)
            string += nl.render(context)

        return string
Exemple #12
0
def do_usemacro(parser, token):
    try:
        args = token.split_contents()
        tag_name, macro_name, values = args[0], args[1], args[2:]
    except IndexError:
        raise (template.TemplateSyntaxError,
               "'%s' tag requires at least one argument (macro name)" %
               token.contents.split()[0])
    try:
        macro = parser._macros[macro_name]
    except (AttributeError, KeyError):
        raise (template.TemplateSyntaxError,
               "Macro '%s' is not defined" % macro_name)

    if (len(values) != len(macro.args)):
        raise (
            template.TemplateSyntaxError,
            "Macro '%s' was declared with %d parameters and used with %d parameter"
            % (macro_name, len(macro.args), len(values)))
    filter_expressions = []
    for val in values:
        if (val[0] == "'" or val[0] == '"') and (val[0] != val[-1]):
            raise (template.TemplateSyntaxError,
                   "Non-terminated string argument: %s" % val[1:])
        filter_expressions.append(FilterExpression(val, parser))
    return UseMacroNode(macro, filter_expressions)
Exemple #13
0
def pjaxr_extends(parser, token):
    bits = token.split_contents()
    if len(bits) != 4 and len(bits) != 3 and len(bits) != 2:
        raise TemplateSyntaxError("'%s' takes 1 - 3 arguments" % bits[0])

    nodelist = parser.parse()

    if nodelist.get_nodes_by_type(
            PjaxrExtendsNode) or nodelist.get_nodes_by_type(ExtendsNode):
        raise TemplateSyntaxError(
            "'pjaxr_extends' and 'extends' cannot appear more than once in the same template!"
        )

    if len(bits) > 2:
        try:
            # format DEFAULT_PJAXR_TEMPLATE string to fit into FilterExpression as token
            pjaxr_template = parser.compile_filter(
                bits[3]) if (len(bits) == 4) else FilterExpression(
                    "'{0}'".format(settings.DEFAULT_PJAXR_TEMPLATE), parser)
        except AttributeError:
            raise TemplateSyntaxError(
                "No Pjaxr template set, even no default!")
        return PjaxrExtendsNode(nodelist, parser.compile_filter(bits[1]),
                                parser.compile_filter(bits[2]), pjaxr_template)
    return ExtendsNode(nodelist, parser.compile_filter(bits[1]))
Exemple #14
0
def do_math(parser, token):
    """
    Syntax:
        {% math <argument, ..> "expression" as var_name %}

    Evaluates a math expression in the current context and saves the value into a variable with the given name.

    "$<number>" is a placeholder in the math expression. It will be replaced by the value of the argument at index <number> - 1.
    Arguments are static values or variables immediately after 'math' tag and before the expression (the third last token).

    Example usage,
        {% math a b "min($1, $2)" as result %}
        {% math a|length b|length 3 "($1 + $2) % $3" as result %}
    """
    tokens = token.split_contents()
    if len(tokens) < 5:
        raise TemplateSyntaxError("'math' tag requires at least 4 arguments")
    expr, as_, var_name = tokens[-3:]

    # strip quote if found
    if re.match(r'^(\'|")', expr):
        expr = expr.strip(expr[0])

    args = []
    for a in tokens[1:-3]:
        if a.find('|') != -1:
            args.append(FilterExpression(a, parser))
        else:
            args.append(Variable(a))
    return MathNode(var_name, expr, args)
def token_value(bits, parser):
    """Parse ``bits`` string and return string or variable (FilterExpression)."""
    if bits[0] in ('"', "'"):  # Parse a string.
        if not (bits[0] == bits[-1] or len(bits) < 2):
            raise template.TemplateSyntaxError("Malformed argument %r" % bits)
        return bits[1:-1]
    else:  # Parse a variable.
        return FilterExpression(bits, parser)
Exemple #16
0
def get_category_drilldown(parser, token):
    """
    Retrieves the specified category, its ancestors and its immediate children as an Iterable.

    The basic syntax::

        {% get_category_drilldown "category name" [using "app.Model"] as varname %}

    Example:
        Using a string for the category name::

            {% get_category_drilldown "/Grandparent/Parent" as family %}

        or using a variable for the category::

            {% get_category_drilldown category_obj as family %}

        Sets family to::

            Grandparent, Parent, Child 1, Child 2, Child n

    Args:
        parser: The Django template parser.
        token: The tag contents

    Returns:
        The recursive tree node.

    Raises:
        TemplateSyntaxError: If the tag is malformed.
    """
    bits = token.split_contents()
    error_str = ("%(tagname)s tag should be in the format {%% %(tagname)s "
                 '"category name" [using "app.Model"] as varname %%} or '
                 "{%% %(tagname)s category_obj as varname %%}.")
    varname = model = ""
    if len(bits) == 4:
        if bits[2] != "as":
            raise template.TemplateSyntaxError(error_str %
                                               {"tagname": bits[0]})
        if bits[2] == "as":
            varname = bits[3].strip("'\"")
            model = "categories.category"
    elif len(bits) == 6:
        if bits[2] not in ("using", "as") or bits[4] not in ("using", "as"):
            raise template.TemplateSyntaxError(error_str %
                                               {"tagname": bits[0]})
        if bits[2] == "as":
            varname = bits[3].strip("'\"")
            model = bits[5].strip("'\"")
        if bits[2] == "using":
            varname = bits[5].strip("'\"")
            model = bits[3].strip("'\"")
    else:
        raise template.TemplateSyntaxError(error_str % {"tagname": bits[0]})
    category = FilterExpression(bits[1], parser)
    return CategoryDrillDownNode(category, varname, model)
class varNode(template.Node):
    def __init__(self, parser, value, asvar):

        self.value = FilterExpression(value, parser)
        self.asvar = asvar

    def render(self, context):
        context[self.asvar] = self.value.resolve(context)
        return ''
Exemple #18
0
    def test_filter_args_count(self):
        p = Parser("")
        l = Library()

        @l.filter
        def no_arguments(value):
            pass

        @l.filter
        def one_argument(value, arg):
            pass

        @l.filter
        def one_opt_argument(value, arg=False):
            pass

        @l.filter
        def two_arguments(value, arg, arg2):
            pass

        @l.filter
        def two_one_opt_arg(value, arg, arg2=False):
            pass

        p.add_library(l)
        for expr in (
                '1|no_arguments:"1"',
                '1|two_arguments',
                '1|two_arguments:"1"',
                '1|two_one_opt_arg',
        ):
            with self.assertRaises(TemplateSyntaxError):
                FilterExpression(expr, p)
        for expr in (
                # Correct number of arguments
                '1|no_arguments',
                '1|one_argument:"1"',
                # One optional
                '1|one_opt_argument',
                '1|one_opt_argument:"1"',
                # Not supplying all
                '1|two_one_opt_arg:"1"',
        ):
            FilterExpression(expr, p)
Exemple #19
0
def foldy(parser, token):
    tag, args, kwargs = parse_block_tag(parser, token)

    usage = '{{% {tag} name=<str> [expanded=<bool>] [class=<str>] %}}'\
                '<foldyhead>'\
                '<foldybody>'\
            '{{% -{tag} %}}'.format(tag=tag)

    if 'name' not in kwargs.keys():
       raise template.TemplateSyntaxError("Usage: "+usage)

    nodes = parser.parse(('-'+tag,))
    parser.delete_first_token()
    return FoldyNode(
        nodes,
        name=kwargs['name'],
        expanded=kwargs.get('expanded', FilterExpression("False", parser)),
        klass=kwargs.get('class', FilterExpression("", parser))
    )
Exemple #20
0
def foldybody(parser, token):
    tag, args, kwargs = parse_block_tag(parser, token)

    usage = '{{% {tag} [class=<str>] %}}'\
                '<content>'\
            '{{% -{tag} %}}'.format(tag=tag)

    nodes = parser.parse(('-'+tag,))
    parser.delete_first_token()
    return FoldybodyNode(
        nodes,
        klass=kwargs.get('class', FilterExpression("", parser))
    )
Exemple #21
0
def url_timeslot(parser, token):
    """
    Wrapper around url templatetag which inserts a timeslot argument if a timeslot is present.

    :param parser:
    :param token:
    :return:
    """
    node = url(parser, token)  # default url parser
    ts = get_timeslot()
    if ts:
        node.args.append(FilterExpression(token=str(ts.pk), parser=parser))
    return node
Exemple #22
0
def do_get_latest_objects_by_category(parser, token):
    """
    Get the latest objects by category.

    The basic syntax is::

        {% get_latest_objects_by_category category app_name model_name set_name [date_field] [number] as [var_name] %}


    Args:
        parser: The Django template parser.
        token: The tag contents

    Returns:
        The latet objects node.

    Raises:
        TemplateSyntaxError: If the tag is malformed
    """
    proper_form = (
        "{% get_latest_objects_by_category category app_name model_name set_name "
        "[date_field] [number] as [var_name] %}")
    bits = token.split_contents()

    if bits[-2] != "as":
        raise TemplateSyntaxError("%s tag shoud be in the form: %s" %
                                  (bits[0], proper_form))
    if len(bits) < 7:
        raise TemplateSyntaxError("%s tag shoud be in the form: %s" %
                                  (bits[0], proper_form))
    if len(bits) > 9:
        raise TemplateSyntaxError("%s tag shoud be in the form: %s" %
                                  (bits[0], proper_form))
    category = FilterExpression(bits[1], parser)
    app_label = FilterExpression(bits[2], parser)
    model_name = FilterExpression(bits[3], parser)
    set_name = FilterExpression(bits[4], parser)
    var_name = bits[-1]
    if bits[5] != "as":
        date_field = FilterExpression(bits[5], parser)
    else:
        date_field = FilterExpression(None, parser)
    if bits[6] != "as":
        num = FilterExpression(bits[6], parser)
    else:
        num = FilterExpression(None, parser)
    return LatestObjectsNode(var_name, category, app_label, model_name,
                             set_name, date_field, num)
Exemple #23
0
def parse_tag(token, parser):
    """
    Generic template tag parser.

    Returns a three-tuple: (tag_name, args, kwargs)

    tag_name is a string, the name of the tag.

    args is a list of FilterExpressions, from all the arguments that didn't look like kwargs,
    in the order they occurred, including any that were mingled amongst kwargs.

    kwargs is a dictionary mapping kwarg names to FilterExpressions, for all the arguments that
    looked like kwargs, including any that were mingled amongst args.

    (At rendering time, a FilterExpression f can be evaluated by calling f.resolve(context).)
    """
    # Split the tag content into words, respecting quoted strings.
    bits = token.split_contents()

    # Pull out the tag name.
    tag_name = bits.pop(0)

    # Parse the rest of the args, and build FilterExpressions from them so that
    # we can evaluate them later.
    args = []
    kwargs = {}
    for bit in bits:
        bit = bit.replace('&quot;', '"')
        # Is this a kwarg or an arg?
        match = kwarg_re.match(bit)
        kwarg_format = match and match.group(1)
        if kwarg_format:
            key, value = match.groups()
            kwargs[key] = FilterExpression(value, parser)
        else:
            args.append(FilterExpression(bit, parser))

    return tag_name, args, kwargs
Exemple #24
0
 def render(self, context):
     for i, arg in enumerate(self.macro.args):
         try:
             fe = self.fe_args[i]
             context[arg] = fe.resolve(context)
         except IndexError:
             context[arg] = ""
     for name, default in iter(self.macro.kwargs.items()):
         if name in self.fe_kwargs:
             context[name] = self.fe_kwargs[name].resolve(context)
         else:
             context[name] = FilterExpression(
                 default, self.macro.parser).resolve(context)
     return self.macro.nodelist.render(context)
Exemple #25
0
 def test_repr(self):
     token = Token(TokenType.BLOCK, 'some text')
     self.assertEqual(repr(token), '<Block token: "some text...">')
     parser = Parser([token], builtins=[filter_library])
     self.assertEqual(
         repr(parser),
         '<Parser tokens=[<Block token: "some text...">]>',
     )
     filter_expression = FilterExpression('news|upper', parser)
     self.assertEqual(repr(filter_expression), "<FilterExpression 'news|upper'>")
     lexer = Lexer('{% for i in 1 %}{{ a }}\n{% endfor %}')
     self.assertEqual(
         repr(lexer),
         '<Lexer template_string="{% for i in 1 %}{{ a...", verbatim=False>',
     )
Exemple #26
0
def token_value(bits, parser):
    """Parse ``bits`` string and return string or variable (FilterExpression).

    >>> from django.template.base import Parser
    >>> parser = Parser('')
    >>> from django_genericfilters.templatetags.updateurl import token_value
    >>> token_value('"A"', parser)
    'A'
    >>> token_value('a', parser).var
    <Variable: 'a'>

    """
    if bits[0] in ('"', "'"):  # Parse a string.
        if not (bits[0] == bits[-1] or len(bits) < 2):
            raise template.TemplateSyntaxError("Malformed argument %r" % bits)
        return bits[1:-1]
    else:  # Parse a variable.
        return FilterExpression(bits, parser)
Exemple #27
0
def do_usemacro(parser, token):
    try:
        args = token.split_contents()
        macro_name, values = args[1], args[2:]
    except IndexError:
        m = ("'%s' tag requires at least one argument (macro name)" %
             token.contents.split()[0])
        raise template.TemplateSyntaxError(m)
    try:
        macro = parser._macros[macro_name]
    except (AttributeError, KeyError):
        m = "Macro '%s' is not defined" % macro_name
        raise template.TemplateSyntaxError(m)

    fe_args, fe_kwargs = parse_token_args(
        values, lambda value: FilterExpression(value, parser))
    macro.parser = parser
    return UseMacroNode(macro, fe_args, fe_kwargs)
def get_category_drilldown(parser, token):
    """
    Retrieves the specified category, its ancestors and its immediate children
    as an iterable.

    Syntax::

        {% get_category_drilldown "category name" [using "app.Model"] as varname %}

    Example::

        {% get_category_drilldown "/Grandparent/Parent" [using "app.Model"] as family %}

    or ::

        {% get_category_drilldown category_obj as family %}

    Sets family to::

        Grandparent, Parent, Child 1, Child 2, Child n
    """
    bits = token.split_contents()
    error_str = '%(tagname)s tag should be in the format {%% %(tagname)s ' \
                '"category name" [using "app.Model"] as varname %%} or ' \
                '{%% %(tagname)s category_obj as varname %%}.'
    if len(bits) == 4:
        if bits[2] != 'as':
            raise template.TemplateSyntaxError(error_str %
                                               {'tagname': bits[0]})
        if bits[2] == 'as':
            varname = bits[3].strip("'\"")
            model = "categories.category"
    if len(bits) == 6:
        if bits[2] not in ('using', 'as') or bits[4] not in ('using', 'as'):
            raise template.TemplateSyntaxError(error_str %
                                               {'tagname': bits[0]})
        if bits[2] == 'as':
            varname = bits[3].strip("'\"")
            model = bits[5].strip("'\"")
        if bits[2] == 'using':
            varname = bits[5].strip("'\"")
            model = bits[3].strip("'\"")
    category = FilterExpression(bits[1], parser)
    return CategoryDrillDownNode(category, varname, model)
Exemple #29
0
def recursetree(parser, token):
    """
    Iterates over the nodes in the tree, and renders the contained block for each node.

    This tag will recursively render children into the template variable {{ children }}.
    Only one database query is required (children are cached for the whole tree)

    Example:
        Basic usage example::

            <ul>
                {% recursetree nodes %}
                    <li>
                        {{ node.name }}
                        {% if not node.is_leaf_node %}
                            <ul>
                                {{ children }}
                            </ul>
                        {% endif %}
                    </li>
                {% endrecursetree %}
            </ul>

    Args:
        parser: The Django template parser.
        token: The tag contents

    Returns:
        The recursive tree node.

    Raises:
        TemplateSyntaxError: If a queryset isn't provided.
    """
    bits = token.contents.split()
    if len(bits) != 2:
        raise template.TemplateSyntaxError("%s tag requires a queryset" %
                                           bits[0])
    queryset_var = FilterExpression(bits[1], parser)

    template_nodes = parser.parse(("endrecursetree", ))
    parser.delete_first_token()

    return RecurseTreeNode(template_nodes, queryset_var)
    def test_filter_parsing(self):
        c = {"article": {"section": "News"}}
        p = Parser("")

        def fe_test(s, val):
            self.assertEqual(FilterExpression(s, p).resolve(c), val)

        fe_test("article.section", "News")
        fe_test("article.section|upper", "NEWS")
        fe_test('"News"', "News")
        fe_test("'News'", "News")
        fe_test(r'"Some \"Good\" News"', 'Some "Good" News')
        fe_test(r'"Some \"Good\" News"', 'Some "Good" News')
        fe_test(r"'Some \'Bad\' News'", "Some 'Bad' News")

        fe = FilterExpression(r'"Some \"Good\" News"', p)
        self.assertEqual(fe.filters, [])
        self.assertEqual(fe.var, 'Some "Good" News')

        # Filtered variables should reject access of attributes beginning with
        # underscores.
        self.assertRaises(TemplateSyntaxError, FilterExpression, "article._hidden|upper", p)
def do_tumbleposts(parser, token):
    '''
    Iterates given block over tumblelog posts.
    {% tumbleposts as post tumblelog_id=1 **kwargs %}
    {{ post.title }}
    {% endtumbleposts %}
    '''
    bits = token.contents.split()
    bits.reverse()
    bits = [bit.strip(',') for bit in bits]
    tag_name = bits.pop()
    as_name = bits.pop()
    var_name = bits.pop()
    order_by = None
    kwargs_list = [bit for bit in bits if '=' in bit]
    kwargs = {}

    #coerce kwargs to expected types
    for kwarg in kwargs_list:
        key, val = kwarg.split('=')
        try:
            val = int(val)
        except ValueError:
            pass
        if val in ['True', 'False']:
            val = bool(val)
        kwargs[key] = val

    # preflight kwargs before running any queries
    if not var_name or '=' in var_name or not kwargs.get('tumblelog_id'):
        raise template.TemplateSyntaxError("'%s' tag requires at a minimum a post variable name and tumblelog_id." % tag_name)
    order_by = None
    limit = None
    if 'order_by' in kwargs:
        order_by = kwargs['order_by']
        del kwargs['order_by']
    if 'limit' in kwargs:
        limit = kwargs['limit']
        del kwargs['limit']
    object_list = models.TumblelogPost.objects.filter(**kwargs)
    if order_by:
        object_list = object_list.order_by(order_by)
    if limit:
        object_list = object_list[:limit]

    # apply custom data
    for obj in object_list:
        # set permalink
        permalink = reverse('tumble_post', urlconf='tumbledore.urls', args=[obj.tumblelog.mount_on, obj.slug])
        obj.__dict__.update(permalink=permalink)
        # override any custom variables
        if isinstance(obj.custom_data, dict):
            obj.__dict__.update(**obj.custom_data)

    # set up FilterExpression from object_list
    sequence = FilterExpression('', parser)
    sequence.filters = []
    sequence.var = object_list

    # gather node lists out of the template parser
    nodelist_loop = parser.parse(('empty', 'endtumbleposts'))
    token = parser.next_token()
    if token.contents == 'empty':
        nodelist_empty = parser.parse(('endtumbleposts',))
        parser.delete_first_token()
    else:
        nodelist_empty = None

    return ForNode([var_name], sequence, False, nodelist_loop, nodelist_empty)
    def __init__(self, parser, value, asvar):

        self.value = FilterExpression(value, parser)
        self.asvar = asvar
    def __init__(self, parser, condition, true_value, false_value, asvar=None):
        self.condition = FilterExpression(condition, parser)

        self.true_value = FilterExpression(true_value, parser)
        self.false_value = FilterExpression(false_value, parser)
        self.asvar = asvar