Commit 8137027f authored by Julien Phalip's avatar Julien Phalip
Browse files

Fixed #13956 -- Enabled `*args` and `**kwargs` support for `simple_tag`,...

Fixed #13956 -- Enabled `*args` and `**kwargs` support for `simple_tag`, `inclusion_tag` and `assignment_tag`. Many thanks to Stephen Burrows for the report and initial patch, to Gregor Müllegger for the initial tests, to SamBull for the suggestions, and to Jannis Leidel for the review and PEP8 cleanup.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@16908 bcc190cf-cafb-0310-a4f2-bffc1f526a37
parent 29b8e34d
Loading
Loading
Loading
Loading
+375 −185

File changed.

Preview size limit exceeded, changes collapsed.

+1 −52
Original line number Diff line number Diff line
@@ -10,64 +10,13 @@ from django.template.base import (Node, NodeList, Template, Library,
    TemplateSyntaxError, VariableDoesNotExist, InvalidTemplateLibrary,
    BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END,
    SINGLE_BRACE_START, SINGLE_BRACE_END, COMMENT_TAG_START, COMMENT_TAG_END,
    get_library)
    get_library, token_kwargs, kwarg_re)
from django.template.smartif import IfParser, Literal
from django.template.defaultfilters import date
from django.utils.encoding import smart_str, smart_unicode
from django.utils.safestring import mark_safe

register = Library()
# Regex for token keyword arguments
kwarg_re = re.compile(r"(?:(\w+)=)?(.+)")

def token_kwargs(bits, parser, support_legacy=False):
    """
    A utility method for parsing token keyword arguments.

    :param bits: A list containing remainder of the token (split by spaces)
        that is to be checked for arguments. Valid arguments will be removed
        from this list.

    :param support_legacy: If set to true ``True``, the legacy format
        ``1 as foo`` will be accepted. Otherwise, only the standard ``foo=1``
        format is allowed.

    :returns: A dictionary of the arguments retrieved from the ``bits`` token
        list.

    There is no requirement for all remaining token ``bits`` to be keyword
    arguments, so the dictionary will be returned as soon as an invalid
    argument format is reached.
    """
    if not bits:
        return {}
    match = kwarg_re.match(bits[0])
    kwarg_format = match and match.group(1)
    if not kwarg_format:
        if not support_legacy:
            return {}
        if len(bits) < 3 or bits[1] != 'as':
            return {}

    kwargs = {}
    while bits:
        if kwarg_format:
            match = kwarg_re.match(bits[0])
            if not match or not match.group(1):
                return kwargs
            key, value = match.groups()
            del bits[:1]
        else:
            if len(bits) < 3 or bits[1] != 'as':
                return kwargs
            key, value = bits[2], bits[0]
            del bits[:3]
        kwargs[key] = parser.compile_filter(value)
        if bits and not kwarg_format:
            if bits[0] != 'and':
                return kwargs
            del bits[:1]
    return kwargs

class AutoEscapeControlNode(Node):
    """Implements the actions of the autoescape tag."""
+67 −0
Original line number Diff line number Diff line
@@ -698,6 +698,29 @@ If you need to rename your tag, you can provide a custom name for it::
    def some_function(value):
        return value - 1

.. versionadded:: 1.4

``simple_tag`` functions may accept any number of positional or keyword
arguments. For example:

.. code-block:: python

    @register.simple_tag
    def my_tag(a, b, *args, **kwargs):
        warning = kwargs['warning']
        profile = kwargs['profile']
        ...
        return ...

Then in the template any number of arguments, separated by spaces, may be
passed to the template tag. Like in Python, the values for keyword arguments
are set using the equal sign ("``=``") and must be provided after the positional
arguments. For example:

.. code-block:: html+django

    {% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile %}

.. _howto-custom-template-tags-assignment-tags:

Assignment tags
@@ -761,6 +784,27 @@ Or, using decorator syntax:
For more information on how the ``takes_context`` option works, see the section
on :ref:`inclusion tags<howto-custom-template-tags-inclusion-tags>`.

``assignment_tag`` functions may accept any number of positional or keyword
arguments. For example:

.. code-block:: python

    @register.assignment_tag
    def my_tag(a, b, *args, **kwargs):
        warning = kwargs['warning']
        profile = kwargs['profile']
        ...
        return ...

Then in the template any number of arguments, separated by spaces, may be
passed to the template tag. Like in Python, the values for keyword arguments
are set using the equal sign ("``=``") and must be provided after the positional
arguments. For example:

.. code-block:: html+django

    {% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile as the_result %}

.. _howto-custom-template-tags-inclusion-tags:

Inclusion tags
@@ -884,6 +928,29 @@ The ``takes_context`` parameter defaults to ``False``. When it's set to *True*,
the tag is passed the context object, as in this example. That's the only
difference between this case and the previous ``inclusion_tag`` example.

.. versionadded:: 1.4

``inclusion_tag`` functions may accept any number of positional or keyword
arguments. For example:

.. code-block:: python

    @register.inclusion_tag('my_template.html')
    def my_tag(a, b, *args, **kwargs):
        warning = kwargs['warning']
        profile = kwargs['profile']
        ...
        return ...

Then in the template any number of arguments, separated by spaces, may be
passed to the template tag. Like in Python, the values for keyword arguments
are set using the equal sign ("``=``") and must be provided after the positional
arguments. For example:

.. code-block:: html+django

    {% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile %}

Setting a variable in the context
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+25 −0
Original line number Diff line number Diff line
@@ -162,6 +162,31 @@ A new helper function,
``template.Library`` to ease the creation of template tags that store some
data in a specified context variable.

``*args`` and ``**kwargs`` support for template tag helper functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

:ref:`simple_tag<howto-custom-template-tags-simple-tags>`, :ref:`inclusion_tag
<howto-custom-template-tags-inclusion-tags>` and the newly introduced
:ref:`assignment_tag<howto-custom-template-tags-assignment-tags>` template
helper functions may now accept any number of positional or keyword arguments.
For example:

.. code-block:: python

    @register.simple_tag
    def my_tag(a, b, *args, **kwargs):
        warning = kwargs['warning']
        profile = kwargs['profile']
        ...
        return ...

Then in the template any number of arguments may be passed to the template tag.
For example:

.. code-block:: html+django

    {% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile %}

``truncatechars`` template filter
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+217 −18
Original line number Diff line number Diff line
@@ -35,6 +35,56 @@ class CustomTagTests(TestCase):
        t = template.Template('{% load custom %}{% params_and_context 37 %}')
        self.assertEqual(t.render(c), u'params_and_context - Expected result (context value: 42): 37')

        t = template.Template('{% load custom %}{% simple_two_params 37 42 %}')
        self.assertEqual(t.render(c), u'simple_two_params - Expected result: 37, 42')

        t = template.Template('{% load custom %}{% simple_one_default 37 %}')
        self.assertEqual(t.render(c), u'simple_one_default - Expected result: 37, hi')

        t = template.Template('{% load custom %}{% simple_one_default 37 two="hello" %}')
        self.assertEqual(t.render(c), u'simple_one_default - Expected result: 37, hello')

        t = template.Template('{% load custom %}{% simple_one_default one=99 two="hello" %}')
        self.assertEqual(t.render(c), u'simple_one_default - Expected result: 99, hello')

        self.assertRaisesRegexp(template.TemplateSyntaxError,
            "'simple_one_default' received unexpected keyword argument 'three'",
            template.Template, '{% load custom %}{% simple_one_default 99 two="hello" three="foo" %}')

        t = template.Template('{% load custom %}{% simple_one_default 37 42 %}')
        self.assertEqual(t.render(c), u'simple_one_default - Expected result: 37, 42')

        t = template.Template('{% load custom %}{% simple_unlimited_args 37 %}')
        self.assertEqual(t.render(c), u'simple_unlimited_args - Expected result: 37, hi')

        t = template.Template('{% load custom %}{% simple_unlimited_args 37 42 56 89 %}')
        self.assertEqual(t.render(c), u'simple_unlimited_args - Expected result: 37, 42, 56, 89')

        t = template.Template('{% load custom %}{% simple_only_unlimited_args %}')
        self.assertEqual(t.render(c), u'simple_only_unlimited_args - Expected result: ')

        t = template.Template('{% load custom %}{% simple_only_unlimited_args 37 42 56 89 %}')
        self.assertEqual(t.render(c), u'simple_only_unlimited_args - Expected result: 37, 42, 56, 89')

        self.assertRaisesRegexp(template.TemplateSyntaxError,
            "'simple_two_params' received too many positional arguments",
            template.Template, '{% load custom %}{% simple_two_params 37 42 56 %}')

        self.assertRaisesRegexp(template.TemplateSyntaxError,
            "'simple_one_default' received too many positional arguments",
            template.Template, '{% load custom %}{% simple_one_default 37 42 56 %}')

        t = template.Template('{% load custom %}{% simple_unlimited_args_kwargs 37 40|add:2 56 eggs="scrambled" four=1|add:3 %}')
        self.assertEqual(t.render(c), u'simple_unlimited_args_kwargs - Expected result: 37, 42, 56 / eggs=scrambled, four=4')

        self.assertRaisesRegexp(template.TemplateSyntaxError,
            "'simple_unlimited_args_kwargs' received some positional argument\(s\) after some keyword argument\(s\)",
            template.Template, '{% load custom %}{% simple_unlimited_args_kwargs 37 40|add:2 eggs="scrambled" 56 four=1|add:3 %}')

        self.assertRaisesRegexp(template.TemplateSyntaxError,
            "'simple_unlimited_args_kwargs' received multiple values for keyword argument 'eggs'",
            template.Template, '{% load custom %}{% simple_unlimited_args_kwargs 37 eggs="scrambled" eggs="scrambled" %}')

    def test_simple_tag_registration(self):
        # Test that the decorators preserve the decorated function's docstring, name and attributes.
        self.verify_tag(custom.no_params, 'no_params')
@@ -42,16 +92,14 @@ class CustomTagTests(TestCase):
        self.verify_tag(custom.explicit_no_context, 'explicit_no_context')
        self.verify_tag(custom.no_params_with_context, 'no_params_with_context')
        self.verify_tag(custom.params_and_context, 'params_and_context')
        self.verify_tag(custom.simple_unlimited_args_kwargs, 'simple_unlimited_args_kwargs')
        self.verify_tag(custom.simple_tag_without_context_parameter, 'simple_tag_without_context_parameter')

    def test_simple_tag_missing_context(self):
        # That the 'context' parameter must be present when takes_context is True
        def a_simple_tag_without_parameters(arg):
            """Expected __doc__"""
            return "Expected result"

        register = template.Library()
        decorator = register.simple_tag(takes_context=True)
        self.assertRaises(template.TemplateSyntaxError, decorator, a_simple_tag_without_parameters)
        # The 'context' parameter must be present when takes_context is True
        self.assertRaisesRegexp(template.TemplateSyntaxError,
            "'simple_tag_without_context_parameter' is decorated with takes_context=True so it must have a first argument of 'context'",
            template.Template, '{% load custom %}{% simple_tag_without_context_parameter 123 %}')

    def test_inclusion_tags(self):
        c = template.Context({'value': 42})
@@ -71,6 +119,70 @@ class CustomTagTests(TestCase):
        t = template.Template('{% load custom %}{% inclusion_params_and_context 37 %}')
        self.assertEqual(t.render(c), u'inclusion_params_and_context - Expected result (context value: 42): 37\n')

        t = template.Template('{% load custom %}{% inclusion_two_params 37 42 %}')
        self.assertEqual(t.render(c), u'inclusion_two_params - Expected result: 37, 42\n')

        t = template.Template('{% load custom %}{% inclusion_one_default 37 %}')
        self.assertEqual(t.render(c), u'inclusion_one_default - Expected result: 37, hi\n')

        t = template.Template('{% load custom %}{% inclusion_one_default 37 two="hello" %}')
        self.assertEqual(t.render(c), u'inclusion_one_default - Expected result: 37, hello\n')

        t = template.Template('{% load custom %}{% inclusion_one_default one=99 two="hello" %}')
        self.assertEqual(t.render(c), u'inclusion_one_default - Expected result: 99, hello\n')

        self.assertRaisesRegexp(template.TemplateSyntaxError,
            "'inclusion_one_default' received unexpected keyword argument 'three'",
            template.Template, '{% load custom %}{% inclusion_one_default 99 two="hello" three="foo" %}')

        t = template.Template('{% load custom %}{% inclusion_one_default 37 42 %}')
        self.assertEqual(t.render(c), u'inclusion_one_default - Expected result: 37, 42\n')

        t = template.Template('{% load custom %}{% inclusion_unlimited_args 37 %}')
        self.assertEqual(t.render(c), u'inclusion_unlimited_args - Expected result: 37, hi\n')

        t = template.Template('{% load custom %}{% inclusion_unlimited_args 37 42 56 89 %}')
        self.assertEqual(t.render(c), u'inclusion_unlimited_args - Expected result: 37, 42, 56, 89\n')

        t = template.Template('{% load custom %}{% inclusion_only_unlimited_args %}')
        self.assertEqual(t.render(c), u'inclusion_only_unlimited_args - Expected result: \n')

        t = template.Template('{% load custom %}{% inclusion_only_unlimited_args 37 42 56 89 %}')
        self.assertEqual(t.render(c), u'inclusion_only_unlimited_args - Expected result: 37, 42, 56, 89\n')

        self.assertRaisesRegexp(template.TemplateSyntaxError,
            "'inclusion_two_params' received too many positional arguments",
            template.Template, '{% load custom %}{% inclusion_two_params 37 42 56 %}')

        self.assertRaisesRegexp(template.TemplateSyntaxError,
            "'inclusion_one_default' received too many positional arguments",
            template.Template, '{% load custom %}{% inclusion_one_default 37 42 56 %}')

        self.assertRaisesRegexp(template.TemplateSyntaxError,
            "'inclusion_one_default' did not receive value\(s\) for the argument\(s\): 'one'",
            template.Template, '{% load custom %}{% inclusion_one_default %}')

        self.assertRaisesRegexp(template.TemplateSyntaxError,
            "'inclusion_unlimited_args' did not receive value\(s\) for the argument\(s\): 'one'",
            template.Template, '{% load custom %}{% inclusion_unlimited_args %}')

        t = template.Template('{% load custom %}{% inclusion_unlimited_args_kwargs 37 40|add:2 56 eggs="scrambled" four=1|add:3 %}')
        self.assertEqual(t.render(c), u'inclusion_unlimited_args_kwargs - Expected result: 37, 42, 56 / eggs=scrambled, four=4\n')

        self.assertRaisesRegexp(template.TemplateSyntaxError,
            "'inclusion_unlimited_args_kwargs' received some positional argument\(s\) after some keyword argument\(s\)",
            template.Template, '{% load custom %}{% inclusion_unlimited_args_kwargs 37 40|add:2 eggs="scrambled" 56 four=1|add:3 %}')

        self.assertRaisesRegexp(template.TemplateSyntaxError,
            "'inclusion_unlimited_args_kwargs' received multiple values for keyword argument 'eggs'",
            template.Template, '{% load custom %}{% inclusion_unlimited_args_kwargs 37 eggs="scrambled" eggs="scrambled" %}')

    def test_include_tag_missing_context(self):
        # The 'context' parameter must be present when takes_context is True
        self.assertRaisesRegexp(template.TemplateSyntaxError,
            "'inclusion_tag_without_context_parameter' is decorated with takes_context=True so it must have a first argument of 'context'",
            template.Template, '{% load custom %}{% inclusion_tag_without_context_parameter 123 %}')

    def test_inclusion_tags_from_template(self):
        c = template.Context({'value': 42})

@@ -89,6 +201,27 @@ class CustomTagTests(TestCase):
        t = template.Template('{% load custom %}{% inclusion_params_and_context_from_template 37 %}')
        self.assertEqual(t.render(c), u'inclusion_params_and_context_from_template - Expected result (context value: 42): 37\n')

        t = template.Template('{% load custom %}{% inclusion_two_params_from_template 37 42 %}')
        self.assertEqual(t.render(c), u'inclusion_two_params_from_template - Expected result: 37, 42\n')

        t = template.Template('{% load custom %}{% inclusion_one_default_from_template 37 %}')
        self.assertEqual(t.render(c), u'inclusion_one_default_from_template - Expected result: 37, hi\n')

        t = template.Template('{% load custom %}{% inclusion_one_default_from_template 37 42 %}')
        self.assertEqual(t.render(c), u'inclusion_one_default_from_template - Expected result: 37, 42\n')

        t = template.Template('{% load custom %}{% inclusion_unlimited_args_from_template 37 %}')
        self.assertEqual(t.render(c), u'inclusion_unlimited_args_from_template - Expected result: 37, hi\n')

        t = template.Template('{% load custom %}{% inclusion_unlimited_args_from_template 37 42 56 89 %}')
        self.assertEqual(t.render(c), u'inclusion_unlimited_args_from_template - Expected result: 37, 42, 56, 89\n')

        t = template.Template('{% load custom %}{% inclusion_only_unlimited_args_from_template %}')
        self.assertEqual(t.render(c), u'inclusion_only_unlimited_args_from_template - Expected result: \n')

        t = template.Template('{% load custom %}{% inclusion_only_unlimited_args_from_template 37 42 56 89 %}')
        self.assertEqual(t.render(c), u'inclusion_only_unlimited_args_from_template - Expected result: 37, 42, 56, 89\n')

    def test_inclusion_tag_registration(self):
        # Test that the decorators preserve the decorated function's docstring, name and attributes.
        self.verify_tag(custom.inclusion_no_params, 'inclusion_no_params')
@@ -96,6 +229,14 @@ class CustomTagTests(TestCase):
        self.verify_tag(custom.inclusion_explicit_no_context, 'inclusion_explicit_no_context')
        self.verify_tag(custom.inclusion_no_params_with_context, 'inclusion_no_params_with_context')
        self.verify_tag(custom.inclusion_params_and_context, 'inclusion_params_and_context')
        self.verify_tag(custom.inclusion_two_params, 'inclusion_two_params')
        self.verify_tag(custom.inclusion_one_default, 'inclusion_one_default')
        self.verify_tag(custom.inclusion_unlimited_args, 'inclusion_unlimited_args')
        self.verify_tag(custom.inclusion_only_unlimited_args, 'inclusion_only_unlimited_args')
        self.verify_tag(custom.inclusion_tag_without_context_parameter, 'inclusion_tag_without_context_parameter')
        self.verify_tag(custom.inclusion_tag_use_l10n, 'inclusion_tag_use_l10n')
        self.verify_tag(custom.inclusion_tag_current_app, 'inclusion_tag_current_app')
        self.verify_tag(custom.inclusion_unlimited_args_kwargs, 'inclusion_unlimited_args_kwargs')

    def test_15070_current_app(self):
        """
@@ -139,6 +280,37 @@ class CustomTagTests(TestCase):
        t = template.Template('{% load custom %}{% assignment_params_and_context 37 as var %}The result is: {{ var }}')
        self.assertEqual(t.render(c), u'The result is: assignment_params_and_context - Expected result (context value: 42): 37')

        t = template.Template('{% load custom %}{% assignment_two_params 37 42 as var %}The result is: {{ var }}')
        self.assertEqual(t.render(c), u'The result is: assignment_two_params - Expected result: 37, 42')

        t = template.Template('{% load custom %}{% assignment_one_default 37 as var %}The result is: {{ var }}')
        self.assertEqual(t.render(c), u'The result is: assignment_one_default - Expected result: 37, hi')

        t = template.Template('{% load custom %}{% assignment_one_default 37 two="hello" as var %}The result is: {{ var }}')
        self.assertEqual(t.render(c), u'The result is: assignment_one_default - Expected result: 37, hello')

        t = template.Template('{% load custom %}{% assignment_one_default one=99 two="hello" as var %}The result is: {{ var }}')
        self.assertEqual(t.render(c), u'The result is: assignment_one_default - Expected result: 99, hello')

        self.assertRaisesRegexp(template.TemplateSyntaxError,
            "'assignment_one_default' received unexpected keyword argument 'three'",
            template.Template, '{% load custom %}{% assignment_one_default 99 two="hello" three="foo" as var %}')

        t = template.Template('{% load custom %}{% assignment_one_default 37 42 as var %}The result is: {{ var }}')
        self.assertEqual(t.render(c), u'The result is: assignment_one_default - Expected result: 37, 42')

        t = template.Template('{% load custom %}{% assignment_unlimited_args 37 as var %}The result is: {{ var }}')
        self.assertEqual(t.render(c), u'The result is: assignment_unlimited_args - Expected result: 37, hi')

        t = template.Template('{% load custom %}{% assignment_unlimited_args 37 42 56 89 as var %}The result is: {{ var }}')
        self.assertEqual(t.render(c), u'The result is: assignment_unlimited_args - Expected result: 37, 42, 56, 89')

        t = template.Template('{% load custom %}{% assignment_only_unlimited_args as var %}The result is: {{ var }}')
        self.assertEqual(t.render(c), u'The result is: assignment_only_unlimited_args - Expected result: ')

        t = template.Template('{% load custom %}{% assignment_only_unlimited_args 37 42 56 89 as var %}The result is: {{ var }}')
        self.assertEqual(t.render(c), u'The result is: assignment_only_unlimited_args - Expected result: 37, 42, 56, 89')

        self.assertRaisesRegexp(template.TemplateSyntaxError,
            "'assignment_one_param' tag takes at least 2 arguments and the second last argument must be 'as'",
            template.Template, '{% load custom %}{% assignment_one_param 37 %}The result is: {{ var }}')
@@ -151,6 +323,33 @@ class CustomTagTests(TestCase):
            "'assignment_one_param' tag takes at least 2 arguments and the second last argument must be 'as'",
            template.Template, '{% load custom %}{% assignment_one_param 37 ass var %}The result is: {{ var }}')

        self.assertRaisesRegexp(template.TemplateSyntaxError,
            "'assignment_two_params' received too many positional arguments",
            template.Template, '{% load custom %}{% assignment_two_params 37 42 56 as var %}The result is: {{ var }}')

        self.assertRaisesRegexp(template.TemplateSyntaxError,
            "'assignment_one_default' received too many positional arguments",
            template.Template, '{% load custom %}{% assignment_one_default 37 42 56 as var %}The result is: {{ var }}')

        self.assertRaisesRegexp(template.TemplateSyntaxError,
            "'assignment_one_default' did not receive value\(s\) for the argument\(s\): 'one'",
            template.Template, '{% load custom %}{% assignment_one_default as var %}The result is: {{ var }}')

        self.assertRaisesRegexp(template.TemplateSyntaxError,
            "'assignment_unlimited_args' did not receive value\(s\) for the argument\(s\): 'one'",
            template.Template, '{% load custom %}{% assignment_unlimited_args as var %}The result is: {{ var }}')

        t = template.Template('{% load custom %}{% assignment_unlimited_args_kwargs 37 40|add:2 56 eggs="scrambled" four=1|add:3 as var %}The result is: {{ var }}')
        self.assertEqual(t.render(c), u'The result is: assignment_unlimited_args_kwargs - Expected result: 37, 42, 56 / eggs=scrambled, four=4')

        self.assertRaisesRegexp(template.TemplateSyntaxError,
            "'assignment_unlimited_args_kwargs' received some positional argument\(s\) after some keyword argument\(s\)",
            template.Template, '{% load custom %}{% assignment_unlimited_args_kwargs 37 40|add:2 eggs="scrambled" 56 four=1|add:3 as var %}The result is: {{ var }}')

        self.assertRaisesRegexp(template.TemplateSyntaxError,
            "'assignment_unlimited_args_kwargs' received multiple values for keyword argument 'eggs'",
            template.Template, '{% load custom %}{% assignment_unlimited_args_kwargs 37 eggs="scrambled" eggs="scrambled" as var %}The result is: {{ var }}')

    def test_assignment_tag_registration(self):
        # Test that the decorators preserve the decorated function's docstring, name and attributes.
        self.verify_tag(custom.assignment_no_params, 'assignment_no_params')
@@ -158,16 +357,16 @@ class CustomTagTests(TestCase):
        self.verify_tag(custom.assignment_explicit_no_context, 'assignment_explicit_no_context')
        self.verify_tag(custom.assignment_no_params_with_context, 'assignment_no_params_with_context')
        self.verify_tag(custom.assignment_params_and_context, 'assignment_params_and_context')
        self.verify_tag(custom.assignment_one_default, 'assignment_one_default')
        self.verify_tag(custom.assignment_two_params, 'assignment_two_params')
        self.verify_tag(custom.assignment_unlimited_args, 'assignment_unlimited_args')
        self.verify_tag(custom.assignment_only_unlimited_args, 'assignment_only_unlimited_args')
        self.verify_tag(custom.assignment_unlimited_args, 'assignment_unlimited_args')
        self.verify_tag(custom.assignment_unlimited_args_kwargs, 'assignment_unlimited_args_kwargs')
        self.verify_tag(custom.assignment_tag_without_context_parameter, 'assignment_tag_without_context_parameter')

    def test_assignment_tag_missing_context(self):
        # That the 'context' parameter must be present when takes_context is True
        def an_assignment_tag_without_parameters(arg):
            """Expected __doc__"""
            return "Expected result"

        register = template.Library()
        decorator = register.assignment_tag(takes_context=True)

        # The 'context' parameter must be present when takes_context is True
        self.assertRaisesRegexp(template.TemplateSyntaxError,
            "Any tag function decorated with takes_context=True must have a first argument of 'context'",
            decorator, an_assignment_tag_without_parameters)
            "'assignment_tag_without_context_parameter' is decorated with takes_context=True so it must have a first argument of 'context'",
            template.Template, '{% load custom %}{% assignment_tag_without_context_parameter 123 as var %}')
Loading