diff --git a/django/template/base.py b/django/template/base.py index 9e3ea0f3c6..1265040fc0 100644 --- a/django/template/base.py +++ b/django/template/base.py @@ -622,34 +622,17 @@ class FilterExpression(object): def args_check(name, func, provided): provided = list(provided) - plen = len(provided) + # First argument, filter input, is implied. + plen = len(provided) + 1 # Check to see if a decorator is providing the real function. func = getattr(func, '_decorated_function', func) args, varargs, varkw, defaults = getargspec(func) - # First argument is filter input. - args.pop(0) - if defaults: - nondefs = args[:-len(defaults)] - else: - nondefs = args - # Args without defaults must be provided. - try: - for arg in nondefs: - provided.pop(0) - except IndexError: - # Not enough + alen = len(args) + dlen = len(defaults or []) + # Not enough OR Too many + if plen < (alen - dlen) or plen > alen: raise TemplateSyntaxError("%s requires %d arguments, %d provided" % - (name, len(nondefs), plen)) - - # Defaults can be overridden. - defaults = list(defaults) if defaults else [] - try: - for parg in provided: - defaults.pop(0) - except IndexError: - # Too many. - raise TemplateSyntaxError("%s requires %d arguments, %d provided" % - (name, len(nondefs), plen)) + (name, alen - dlen, plen)) return True args_check = staticmethod(args_check) diff --git a/tests/template_tests/test_parser.py b/tests/template_tests/test_parser.py index e626bfd34f..159a240530 100644 --- a/tests/template_tests/test_parser.py +++ b/tests/template_tests/test_parser.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals from unittest import TestCase from django.template import (TokenParser, FilterExpression, Parser, Variable, - Template, TemplateSyntaxError) + Template, TemplateSyntaxError, Library) from django.test.utils import override_settings from django.utils import six @@ -94,3 +94,42 @@ class ParserTests(TestCase): with six.assertRaisesRegex(self, TemplateSyntaxError, msg) as cm: Template("{% if 1 %}{{ foo@bar }}{% endif %}") self.assertEqual(cm.exception.django_template_source[1], (10, 23)) + + 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)