Ticket #13956: 13956.ttag_helpers_args_kwargs_support.diff
File 13956.ttag_helpers_args_kwargs_support.diff, 50.0 KB (added by , 13 years ago) |
---|
-
django/template/base.py
diff --git a/django/template/base.py b/django/template/base.py index c94eeb5..1accc49 100644
a b class VariableNode(Node): 797 797 return '' 798 798 return _render_value_in_context(output, context) 799 799 800 def generic_tag_compiler(params, defaults, name, node_class, parser, token): 801 "Returns a template.Node subclass." 802 bits = token.split_contents()[1:] 803 bmax = len(params) 804 def_len = defaults and len(defaults) or 0 805 bmin = bmax - def_len 806 if(len(bits) < bmin or len(bits) > bmax): 807 if bmin == bmax: 808 message = "%s takes %s arguments" % (name, bmin) 800 # Regex for token keyword arguments 801 kwarg_re = re.compile(r"(?:(\w+)=)?(.+)") 802 803 def token_kwargs(bits, parser, support_legacy=False): 804 """ 805 A utility method for parsing token keyword arguments. 806 807 :param bits: A list containing remainder of the token (split by spaces) 808 that is to be checked for arguments. Valid arguments will be removed 809 from this list. 810 811 :param support_legacy: If set to true ``True``, the legacy format 812 ``1 as foo`` will be accepted. Otherwise, only the standard ``foo=1`` 813 format is allowed. 814 815 :returns: A dictionary of the arguments retrieved from the ``bits`` token 816 list. 817 818 There is no requirement for all remaining token ``bits`` to be keyword 819 arguments, so the dictionary will be returned as soon as an invalid 820 argument format is reached. 821 """ 822 if not bits: 823 return {} 824 match = kwarg_re.match(bits[0]) 825 kwarg_format = match and match.group(1) 826 if not kwarg_format: 827 if not support_legacy: 828 return {} 829 if len(bits) < 3 or bits[1] != 'as': 830 return {} 831 832 kwargs = {} 833 while bits: 834 if kwarg_format: 835 match = kwarg_re.match(bits[0]) 836 if not match or not match.group(1): 837 return kwargs 838 key, value = match.groups() 839 del bits[:1] 840 else: 841 if len(bits) < 3 or bits[1] != 'as': 842 return kwargs 843 key, value = bits[2], bits[0] 844 del bits[:3] 845 kwargs[key] = parser.compile_filter(value) 846 if bits and not kwarg_format: 847 if bits[0] != 'and': 848 return kwargs 849 del bits[:1] 850 return kwargs 851 852 def parse_bits(parser, bits, params, varargs, varkw, defaults, 853 takes_context, name): 854 """ Parses bits for template tag helpers (simple_tag, include_tag and 855 assignment_tag), in particular by detecting syntax errors and by 856 extracting positional and keyword arguments. 857 """ 858 if takes_context: 859 if params[0] == 'context': 860 params = params[1:] 861 else: 862 raise TemplateSyntaxError( 863 "'%s' is decorated with takes_context=True so it must " 864 "have a first argument of 'context'" % name) 865 args = [] 866 kwargs = {} 867 unhandled_params = list(params) 868 for bit in bits: 869 # First we try to extract a potential kwarg from the bit 870 kwarg = token_kwargs([bit], parser) 871 if kwarg: 872 # The kwarg was successfully extracted 873 param, value = kwarg.items()[0] 874 if param not in params and varkw is None: 875 # An unexpected keyword argument was supplied 876 raise TemplateSyntaxError( 877 "'%s' received unexpected keyword argument '%s'" % 878 (name, param)) 879 elif param in kwargs: 880 # The keyword argument has already been supplied once 881 raise TemplateSyntaxError( 882 "'%s' received multiple values for keyword argument '%s'" % 883 (name, param)) 884 else: 885 # All good, record the keyword argument 886 kwargs[str(param)] = value 887 if param in unhandled_params: 888 # If using the keyword syntax for a positional arg, then 889 # consume it. 890 unhandled_params.remove(param) 809 891 else: 810 message = "%s takes between %s and %s arguments" % (name, bmin, bmax) 811 raise TemplateSyntaxError(message) 812 return node_class(bits) 892 if kwargs: 893 raise TemplateSyntaxError( 894 "'%s' received some positional argument(s) after some " 895 "keyword argument(s)" % name) 896 else: 897 # Record the positional argument 898 args.append(parser.compile_filter(bit)) 899 try: 900 # Consume from the list of expected positional arguments 901 unhandled_params.pop(0) 902 except IndexError: 903 if varargs is None: 904 raise TemplateSyntaxError( 905 "'%s' received too many positional arguments" % 906 name) 907 if defaults is not None: 908 # Consider the last n params handled, where n is the 909 # number of defaults. 910 unhandled_params = unhandled_params[:-len(defaults)] 911 if unhandled_params: 912 # Some positional arguments were not supplied 913 raise TemplateSyntaxError( 914 u"'%s' did not receive value(s) for the argument(s): %s" % ( 915 name, 916 u", ".join([u"'%s'" % p for p in unhandled_params]) 917 )) 918 return args, kwargs 919 920 def generic_tag_compiler(parser, token, params, varargs, varkw, defaults, 921 name, takes_context, node_class): 922 """Returns a template.Node subclass.""" 923 bits = token.split_contents()[1:] 924 args, kwargs = parse_bits(parser, bits, params, varargs, varkw, 925 defaults, takes_context, name) 926 return node_class(takes_context, args, kwargs) 927 928 class TagHelperNode(Node): 929 """ 930 Base class for tag helper nodes such as SimpleNode, InclusionNode and 931 AssignmentNode. Manages the positional and keyword arguments to be passed 932 to the decorated function. 933 """ 934 935 def __init__(self, takes_context, args, kwargs): 936 self.takes_context = takes_context 937 self.args = args 938 self.kwargs = kwargs 939 940 def get_resolved_arguments(self, context): 941 resolved_args = [var.resolve(context) for var in self.args] 942 if self.takes_context: 943 resolved_args = [context] + resolved_args 944 resolved_kwargs = dict( 945 [(k, v.resolve(context)) for k, v in self.kwargs.items()]) 946 return resolved_args, resolved_kwargs 813 947 814 948 class Library(object): 815 949 def __init__(self): … … class Library(object): 817 951 self.tags = {} 818 952 819 953 def tag(self, name=None, compile_function=None): 820 if name == None and compile_function ==None:954 if name is None and compile_function is None: 821 955 # @register.tag() 822 956 return self.tag_function 823 elif name != None and compile_function ==None:957 elif name is not None and compile_function is None: 824 958 if callable(name): 825 959 # @register.tag 826 960 return self.tag_function(name) … … class Library(object): 829 963 def dec(func): 830 964 return self.tag(name, func) 831 965 return dec 832 elif name != None and compile_function !=None:966 elif name is not None and compile_function is not None: 833 967 # register.tag('somename', somefunc) 834 968 self.tags[name] = compile_function 835 969 return compile_function … … class Library(object): 841 975 return func 842 976 843 977 def filter(self, name=None, filter_func=None): 844 if name == None and filter_func ==None:978 if name is None and filter_func is None: 845 979 # @register.filter() 846 980 return self.filter_function 847 elif filter_func ==None:981 elif filter_func is None: 848 982 if callable(name): 849 983 # @register.filter 850 984 return self.filter_function(name) … … class Library(object): 853 987 def dec(func): 854 988 return self.filter(name, func) 855 989 return dec 856 elif name != None and filter_func !=None:990 elif name is not None and filter_func is not None: 857 991 # register.filter('somename', somefunc) 858 992 self.filters[name] = filter_func 859 993 return filter_func … … class Library(object): 866 1000 867 1001 def simple_tag(self, func=None, takes_context=None, name=None): 868 1002 def dec(func): 869 params, xx, xxx, defaults = getargspec(func) 870 if takes_context: 871 if params[0] == 'context': 872 params = params[1:] 873 else: 874 raise TemplateSyntaxError("Any tag function decorated with takes_context=True must have a first argument of 'context'") 1003 params, varargs, varkw, defaults = getargspec(func) 875 1004 876 class SimpleNode(Node): 877 def __init__(self, vars_to_resolve): 878 self.vars_to_resolve = map(Variable, vars_to_resolve) 1005 class SimpleNode(TagHelperNode): 879 1006 880 1007 def render(self, context): 881 resolved_vars = [var.resolve(context) for var in self.vars_to_resolve] 882 if takes_context: 883 func_args = [context] + resolved_vars 884 else: 885 func_args = resolved_vars 886 return func(*func_args) 1008 resolved_args, resolved_kwargs = self.get_resolved_arguments(context) 1009 return func(*resolved_args, **resolved_kwargs) 887 1010 888 1011 function_name = name or getattr(func, '_decorated_function', func).__name__ 889 compile_func = partial(generic_tag_compiler, params , defaults, function_name,SimpleNode)1012 compile_func = partial(generic_tag_compiler, params=params, varargs=varargs, varkw=varkw, defaults=defaults, name=function_name, takes_context=takes_context, node_class=SimpleNode) 890 1013 compile_func.__doc__ = func.__doc__ 891 1014 self.tag(function_name, compile_func) 892 1015 return func … … class Library(object): 902 1025 903 1026 def assignment_tag(self, func=None, takes_context=None, name=None): 904 1027 def dec(func): 905 params, xx, xxx, defaults = getargspec(func) 906 if takes_context: 907 if params[0] == 'context': 908 params = params[1:] 909 else: 910 raise TemplateSyntaxError("Any tag function decorated with takes_context=True must have a first argument of 'context'") 1028 params, varargs, varkw, defaults = getargspec(func) 911 1029 912 class AssignmentNode( Node):913 def __init__(self, params_vars, target_var):914 s elf.params_vars = map(Variable, params_vars)1030 class AssignmentNode(TagHelperNode): 1031 def __init__(self, takes_context, args, kwargs, target_var): 1032 super(AssignmentNode, self).__init__(takes_context, args, kwargs) 915 1033 self.target_var = target_var 916 1034 917 1035 def render(self, context): 918 resolved_vars = [var.resolve(context) for var in self.params_vars] 919 if takes_context: 920 func_args = [context] + resolved_vars 921 else: 922 func_args = resolved_vars 923 context[self.target_var] = func(*func_args) 1036 resolved_args, resolved_kwargs = self.get_resolved_arguments(context) 1037 context[self.target_var] = func(*resolved_args, **resolved_kwargs) 924 1038 return '' 925 1039 1040 function_name = name or getattr(func, '_decorated_function', func).__name__ 1041 926 1042 def compile_func(parser, token): 927 bits = token.split_contents() 928 tag_name = bits[0] 929 bits = bits[1:] 930 params_max = len(params) 931 defaults_length = defaults and len(defaults) or 0 932 params_min = params_max - defaults_length 933 if (len(bits) < 2 or bits[-2] != 'as'): 1043 bits = token.split_contents()[1:] 1044 if len(bits) < 2 or bits[-2] != 'as': 934 1045 raise TemplateSyntaxError( 935 1046 "'%s' tag takes at least 2 arguments and the " 936 "second last argument must be 'as'" % tag_name) 937 params_vars = bits[:-2] 1047 "second last argument must be 'as'" % function_name) 938 1048 target_var = bits[-1] 939 if (len(params_vars) < params_min or 940 len(params_vars) > params_max): 941 if params_min == params_max: 942 raise TemplateSyntaxError( 943 "%s takes %s arguments" % (tag_name, params_min)) 944 else: 945 raise TemplateSyntaxError( 946 "%s takes between %s and %s arguments" 947 % (tag_name, params_min, params_max)) 948 return AssignmentNode(params_vars, target_var) 1049 bits = bits[:-2] 1050 args, kwargs = parse_bits(parser, bits, params, varargs, varkw, defaults, takes_context, function_name) 1051 return AssignmentNode(takes_context, args, kwargs, target_var) 949 1052 950 function_name = name or getattr(func, '_decorated_function', func).__name__951 1053 compile_func.__doc__ = func.__doc__ 952 1054 self.tag(function_name, compile_func) 953 1055 return func … … class Library(object): 963 1065 964 1066 def inclusion_tag(self, file_name, context_class=Context, takes_context=False, name=None): 965 1067 def dec(func): 966 params, xx, xxx, defaults = getargspec(func) 967 if takes_context: 968 if params[0] == 'context': 969 params = params[1:] 970 else: 971 raise TemplateSyntaxError("Any tag function decorated with takes_context=True must have a first argument of 'context'") 1068 params, varargs, varkw, defaults = getargspec(func) 972 1069 973 class InclusionNode(Node): 974 def __init__(self, vars_to_resolve): 975 self.vars_to_resolve = map(Variable, vars_to_resolve) 1070 class InclusionNode(TagHelperNode): 976 1071 977 1072 def render(self, context): 978 resolved_vars = [var.resolve(context) for var in self.vars_to_resolve] 979 if takes_context: 980 args = [context] + resolved_vars 981 else: 982 args = resolved_vars 983 984 dict = func(*args) 1073 resolved_args, resolved_kwargs = self.get_resolved_arguments(context) 1074 _dict = func(*resolved_args, **resolved_kwargs) 985 1075 986 1076 if not getattr(self, 'nodelist', False): 987 1077 from django.template.loader import get_template, select_template … … class Library(object): 992 1082 else: 993 1083 t = get_template(file_name) 994 1084 self.nodelist = t.nodelist 995 new_context = context_class( dict, **{1085 new_context = context_class(_dict, **{ 996 1086 'autoescape': context.autoescape, 997 1087 'current_app': context.current_app, 998 1088 'use_l10n': context.use_l10n, … … class Library(object): 1006 1096 return self.nodelist.render(new_context) 1007 1097 1008 1098 function_name = name or getattr(func, '_decorated_function', func).__name__ 1009 compile_func = partial(generic_tag_compiler, params , defaults, function_name,InclusionNode)1099 compile_func = partial(generic_tag_compiler, params=params, varargs=varargs, varkw=varkw, defaults=defaults, name=function_name, takes_context=takes_context, node_class=InclusionNode) 1010 1100 compile_func.__doc__ = func.__doc__ 1011 1101 self.tag(function_name, compile_func) 1012 1102 return func -
django/template/defaulttags.py
diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py index 06838cf..f2bf133 100644
a b from django.template.base import (Node, NodeList, Template, Library, 10 10 TemplateSyntaxError, VariableDoesNotExist, InvalidTemplateLibrary, 11 11 BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, 12 12 SINGLE_BRACE_START, SINGLE_BRACE_END, COMMENT_TAG_START, COMMENT_TAG_END, 13 get_library )13 get_library, token_kwargs, kwarg_re) 14 14 from django.template.smartif import IfParser, Literal 15 15 from django.template.defaultfilters import date 16 16 from django.utils.encoding import smart_str, smart_unicode 17 17 from django.utils.safestring import mark_safe 18 18 19 19 register = Library() 20 # Regex for token keyword arguments21 kwarg_re = re.compile(r"(?:(\w+)=)?(.+)")22 23 def token_kwargs(bits, parser, support_legacy=False):24 """25 A utility method for parsing token keyword arguments.26 27 :param bits: A list containing remainder of the token (split by spaces)28 that is to be checked for arguments. Valid arguments will be removed29 from this list.30 31 :param support_legacy: If set to true ``True``, the legacy format32 ``1 as foo`` will be accepted. Otherwise, only the standard ``foo=1``33 format is allowed.34 35 :returns: A dictionary of the arguments retrieved from the ``bits`` token36 list.37 38 There is no requirement for all remaining token ``bits`` to be keyword39 arguments, so the dictionary will be returned as soon as an invalid40 argument format is reached.41 """42 if not bits:43 return {}44 match = kwarg_re.match(bits[0])45 kwarg_format = match and match.group(1)46 if not kwarg_format:47 if not support_legacy:48 return {}49 if len(bits) < 3 or bits[1] != 'as':50 return {}51 52 kwargs = {}53 while bits:54 if kwarg_format:55 match = kwarg_re.match(bits[0])56 if not match or not match.group(1):57 return kwargs58 key, value = match.groups()59 del bits[:1]60 else:61 if len(bits) < 3 or bits[1] != 'as':62 return kwargs63 key, value = bits[2], bits[0]64 del bits[:3]65 kwargs[key] = parser.compile_filter(value)66 if bits and not kwarg_format:67 if bits[0] != 'and':68 return kwargs69 del bits[:1]70 return kwargs71 20 72 21 class AutoEscapeControlNode(Node): 73 22 """Implements the actions of the autoescape tag.""" -
docs/releases/1.4.txt
diff --git a/docs/releases/1.4.txt b/docs/releases/1.4.txt index 90c5e24..54efc42 100644
a b Django 1.4 also includes several smaller improvements worth noting: 275 275 276 276 * Customizable names for :meth:`~django.template.Library.simple_tag`. 277 277 278 * Added ``*args`` and ``*kwargs`` support to 279 :meth:`~django.template.Library.simple_tag`, 280 :meth:`~django.template.Library.inclusion_tag` and the newly introduced 281 :meth:`~django.template.Library.assignment_tag`. 282 278 283 * In the documentation, a helpful :doc:`security overview </topics/security>` 279 284 page. 280 285 -
tests/regressiontests/templates/custom.py
diff --git a/tests/regressiontests/templates/custom.py b/tests/regressiontests/templates/custom.py index d781874..c05229e 100644
a b class CustomTagTests(TestCase): 35 35 t = template.Template('{% load custom %}{% params_and_context 37 %}') 36 36 self.assertEqual(t.render(c), u'params_and_context - Expected result (context value: 42): 37') 37 37 38 t = template.Template('{% load custom %}{% simple_two_params 37 42 %}') 39 self.assertEqual(t.render(c), u'simple_two_params - Expected result: 37, 42') 40 41 t = template.Template('{% load custom %}{% simple_one_default 37 %}') 42 self.assertEqual(t.render(c), u'simple_one_default - Expected result: 37, hi') 43 44 t = template.Template('{% load custom %}{% simple_one_default 37 two="hello" %}') 45 self.assertEqual(t.render(c), u'simple_one_default - Expected result: 37, hello') 46 47 t = template.Template('{% load custom %}{% simple_one_default one=99 two="hello" %}') 48 self.assertEqual(t.render(c), u'simple_one_default - Expected result: 99, hello') 49 50 self.assertRaisesRegexp(template.TemplateSyntaxError, 51 "'simple_one_default' received unexpected keyword argument 'three'", 52 template.Template, '{% load custom %}{% simple_one_default 99 two="hello" three="foo" %}') 53 54 t = template.Template('{% load custom %}{% simple_one_default 37 42 %}') 55 self.assertEqual(t.render(c), u'simple_one_default - Expected result: 37, 42') 56 57 t = template.Template('{% load custom %}{% simple_unlimited_args 37 %}') 58 self.assertEqual(t.render(c), u'simple_unlimited_args - Expected result: 37, hi') 59 60 t = template.Template('{% load custom %}{% simple_unlimited_args 37 42 56 89 %}') 61 self.assertEqual(t.render(c), u'simple_unlimited_args - Expected result: 37, 42, 56, 89') 62 63 t = template.Template('{% load custom %}{% simple_only_unlimited_args %}') 64 self.assertEqual(t.render(c), u'simple_only_unlimited_args - Expected result: ') 65 66 t = template.Template('{% load custom %}{% simple_only_unlimited_args 37 42 56 89 %}') 67 self.assertEqual(t.render(c), u'simple_only_unlimited_args - Expected result: 37, 42, 56, 89') 68 69 self.assertRaisesRegexp(template.TemplateSyntaxError, 70 "'simple_two_params' received too many positional arguments", 71 template.Template, '{% load custom %}{% simple_two_params 37 42 56 %}') 72 73 self.assertRaisesRegexp(template.TemplateSyntaxError, 74 "'simple_one_default' received too many positional arguments", 75 template.Template, '{% load custom %}{% simple_one_default 37 42 56 %}') 76 77 t = template.Template('{% load custom %}{% simple_unlimited_args_kwargs 37 40|add:2 56 eggs="scrambled" four=1|add:3 %}') 78 self.assertEqual(t.render(c), u'simple_unlimited_args_kwargs - Expected result: 37, 42, 56 / eggs=scrambled, four=4') 79 80 self.assertRaisesRegexp(template.TemplateSyntaxError, 81 "'simple_unlimited_args_kwargs' received some positional argument\(s\) after some keyword argument\(s\)", 82 template.Template, '{% load custom %}{% simple_unlimited_args_kwargs 37 40|add:2 eggs="scrambled" 56 four=1|add:3 %}') 83 84 self.assertRaisesRegexp(template.TemplateSyntaxError, 85 "'simple_unlimited_args_kwargs' received multiple values for keyword argument 'eggs'", 86 template.Template, '{% load custom %}{% simple_unlimited_args_kwargs 37 eggs="scrambled" eggs="scrambled" %}') 87 38 88 def test_simple_tag_registration(self): 39 89 # Test that the decorators preserve the decorated function's docstring, name and attributes. 40 90 self.verify_tag(custom.no_params, 'no_params') … … class CustomTagTests(TestCase): 42 92 self.verify_tag(custom.explicit_no_context, 'explicit_no_context') 43 93 self.verify_tag(custom.no_params_with_context, 'no_params_with_context') 44 94 self.verify_tag(custom.params_and_context, 'params_and_context') 95 self.verify_tag(custom.simple_unlimited_args_kwargs, 'simple_unlimited_args_kwargs') 96 self.verify_tag(custom.simple_tag_without_context_parameter, 'simple_tag_without_context_parameter') 45 97 46 98 def test_simple_tag_missing_context(self): 47 # That the 'context' parameter must be present when takes_context is True 48 def a_simple_tag_without_parameters(arg): 49 """Expected __doc__""" 50 return "Expected result" 51 52 register = template.Library() 53 decorator = register.simple_tag(takes_context=True) 54 self.assertRaises(template.TemplateSyntaxError, decorator, a_simple_tag_without_parameters) 99 # The 'context' parameter must be present when takes_context is True 100 self.assertRaisesRegexp(template.TemplateSyntaxError, 101 "'simple_tag_without_context_parameter' is decorated with takes_context=True so it must have a first argument of 'context'", 102 template.Template, '{% load custom %}{% simple_tag_without_context_parameter 123 %}') 55 103 56 104 def test_inclusion_tags(self): 57 105 c = template.Context({'value': 42}) … … class CustomTagTests(TestCase): 71 119 t = template.Template('{% load custom %}{% inclusion_params_and_context 37 %}') 72 120 self.assertEqual(t.render(c), u'inclusion_params_and_context - Expected result (context value: 42): 37\n') 73 121 122 t = template.Template('{% load custom %}{% inclusion_two_params 37 42 %}') 123 self.assertEqual(t.render(c), u'inclusion_two_params - Expected result: 37, 42\n') 124 125 t = template.Template('{% load custom %}{% inclusion_one_default 37 %}') 126 self.assertEqual(t.render(c), u'inclusion_one_default - Expected result: 37, hi\n') 127 128 t = template.Template('{% load custom %}{% inclusion_one_default 37 two="hello" %}') 129 self.assertEqual(t.render(c), u'inclusion_one_default - Expected result: 37, hello\n') 130 131 t = template.Template('{% load custom %}{% inclusion_one_default one=99 two="hello" %}') 132 self.assertEqual(t.render(c), u'inclusion_one_default - Expected result: 99, hello\n') 133 134 self.assertRaisesRegexp(template.TemplateSyntaxError, 135 "'inclusion_one_default' received unexpected keyword argument 'three'", 136 template.Template, '{% load custom %}{% inclusion_one_default 99 two="hello" three="foo" %}') 137 138 t = template.Template('{% load custom %}{% inclusion_one_default 37 42 %}') 139 self.assertEqual(t.render(c), u'inclusion_one_default - Expected result: 37, 42\n') 140 141 t = template.Template('{% load custom %}{% inclusion_unlimited_args 37 %}') 142 self.assertEqual(t.render(c), u'inclusion_unlimited_args - Expected result: 37, hi\n') 143 144 t = template.Template('{% load custom %}{% inclusion_unlimited_args 37 42 56 89 %}') 145 self.assertEqual(t.render(c), u'inclusion_unlimited_args - Expected result: 37, 42, 56, 89\n') 146 147 t = template.Template('{% load custom %}{% inclusion_only_unlimited_args %}') 148 self.assertEqual(t.render(c), u'inclusion_only_unlimited_args - Expected result: \n') 149 150 t = template.Template('{% load custom %}{% inclusion_only_unlimited_args 37 42 56 89 %}') 151 self.assertEqual(t.render(c), u'inclusion_only_unlimited_args - Expected result: 37, 42, 56, 89\n') 152 153 self.assertRaisesRegexp(template.TemplateSyntaxError, 154 "'inclusion_two_params' received too many positional arguments", 155 template.Template, '{% load custom %}{% inclusion_two_params 37 42 56 %}') 156 157 self.assertRaisesRegexp(template.TemplateSyntaxError, 158 "'inclusion_one_default' received too many positional arguments", 159 template.Template, '{% load custom %}{% inclusion_one_default 37 42 56 %}') 160 161 self.assertRaisesRegexp(template.TemplateSyntaxError, 162 "'inclusion_one_default' did not receive value\(s\) for the argument\(s\): 'one'", 163 template.Template, '{% load custom %}{% inclusion_one_default %}') 164 165 self.assertRaisesRegexp(template.TemplateSyntaxError, 166 "'inclusion_unlimited_args' did not receive value\(s\) for the argument\(s\): 'one'", 167 template.Template, '{% load custom %}{% inclusion_unlimited_args %}') 168 169 t = template.Template('{% load custom %}{% inclusion_unlimited_args_kwargs 37 40|add:2 56 eggs="scrambled" four=1|add:3 %}') 170 self.assertEqual(t.render(c), u'inclusion_unlimited_args_kwargs - Expected result: 37, 42, 56 / eggs=scrambled, four=4\n') 171 172 self.assertRaisesRegexp(template.TemplateSyntaxError, 173 "'inclusion_unlimited_args_kwargs' received some positional argument\(s\) after some keyword argument\(s\)", 174 template.Template, '{% load custom %}{% inclusion_unlimited_args_kwargs 37 40|add:2 eggs="scrambled" 56 four=1|add:3 %}') 175 176 self.assertRaisesRegexp(template.TemplateSyntaxError, 177 "'inclusion_unlimited_args_kwargs' received multiple values for keyword argument 'eggs'", 178 template.Template, '{% load custom %}{% inclusion_unlimited_args_kwargs 37 eggs="scrambled" eggs="scrambled" %}') 179 180 def test_include_tag_missing_context(self): 181 # The 'context' parameter must be present when takes_context is True 182 self.assertRaisesRegexp(template.TemplateSyntaxError, 183 "'inclusion_tag_without_context_parameter' is decorated with takes_context=True so it must have a first argument of 'context'", 184 template.Template, '{% load custom %}{% inclusion_tag_without_context_parameter 123 %}') 185 74 186 def test_inclusion_tags_from_template(self): 75 187 c = template.Context({'value': 42}) 76 188 … … class CustomTagTests(TestCase): 89 201 t = template.Template('{% load custom %}{% inclusion_params_and_context_from_template 37 %}') 90 202 self.assertEqual(t.render(c), u'inclusion_params_and_context_from_template - Expected result (context value: 42): 37\n') 91 203 204 t = template.Template('{% load custom %}{% inclusion_two_params_from_template 37 42 %}') 205 self.assertEqual(t.render(c), u'inclusion_two_params_from_template - Expected result: 37, 42\n') 206 207 t = template.Template('{% load custom %}{% inclusion_one_default_from_template 37 %}') 208 self.assertEqual(t.render(c), u'inclusion_one_default_from_template - Expected result: 37, hi\n') 209 210 t = template.Template('{% load custom %}{% inclusion_one_default_from_template 37 42 %}') 211 self.assertEqual(t.render(c), u'inclusion_one_default_from_template - Expected result: 37, 42\n') 212 213 t = template.Template('{% load custom %}{% inclusion_unlimited_args_from_template 37 %}') 214 self.assertEqual(t.render(c), u'inclusion_unlimited_args_from_template - Expected result: 37, hi\n') 215 216 t = template.Template('{% load custom %}{% inclusion_unlimited_args_from_template 37 42 56 89 %}') 217 self.assertEqual(t.render(c), u'inclusion_unlimited_args_from_template - Expected result: 37, 42, 56, 89\n') 218 219 t = template.Template('{% load custom %}{% inclusion_only_unlimited_args_from_template %}') 220 self.assertEqual(t.render(c), u'inclusion_only_unlimited_args_from_template - Expected result: \n') 221 222 t = template.Template('{% load custom %}{% inclusion_only_unlimited_args_from_template 37 42 56 89 %}') 223 self.assertEqual(t.render(c), u'inclusion_only_unlimited_args_from_template - Expected result: 37, 42, 56, 89\n') 224 92 225 def test_inclusion_tag_registration(self): 93 226 # Test that the decorators preserve the decorated function's docstring, name and attributes. 94 227 self.verify_tag(custom.inclusion_no_params, 'inclusion_no_params') … … class CustomTagTests(TestCase): 96 229 self.verify_tag(custom.inclusion_explicit_no_context, 'inclusion_explicit_no_context') 97 230 self.verify_tag(custom.inclusion_no_params_with_context, 'inclusion_no_params_with_context') 98 231 self.verify_tag(custom.inclusion_params_and_context, 'inclusion_params_and_context') 232 self.verify_tag(custom.inclusion_two_params, 'inclusion_two_params') 233 self.verify_tag(custom.inclusion_one_default, 'inclusion_one_default') 234 self.verify_tag(custom.inclusion_unlimited_args, 'inclusion_unlimited_args') 235 self.verify_tag(custom.inclusion_only_unlimited_args, 'inclusion_only_unlimited_args') 236 self.verify_tag(custom.inclusion_tag_without_context_parameter, 'inclusion_tag_without_context_parameter') 237 self.verify_tag(custom.inclusion_tag_use_l10n, 'inclusion_tag_use_l10n') 238 self.verify_tag(custom.inclusion_tag_current_app, 'inclusion_tag_current_app') 239 self.verify_tag(custom.inclusion_unlimited_args_kwargs, 'inclusion_unlimited_args_kwargs') 99 240 100 241 def test_15070_current_app(self): 101 242 """ … … class CustomTagTests(TestCase): 139 280 t = template.Template('{% load custom %}{% assignment_params_and_context 37 as var %}The result is: {{ var }}') 140 281 self.assertEqual(t.render(c), u'The result is: assignment_params_and_context - Expected result (context value: 42): 37') 141 282 283 t = template.Template('{% load custom %}{% assignment_two_params 37 42 as var %}The result is: {{ var }}') 284 self.assertEqual(t.render(c), u'The result is: assignment_two_params - Expected result: 37, 42') 285 286 t = template.Template('{% load custom %}{% assignment_one_default 37 as var %}The result is: {{ var }}') 287 self.assertEqual(t.render(c), u'The result is: assignment_one_default - Expected result: 37, hi') 288 289 t = template.Template('{% load custom %}{% assignment_one_default 37 two="hello" as var %}The result is: {{ var }}') 290 self.assertEqual(t.render(c), u'The result is: assignment_one_default - Expected result: 37, hello') 291 292 t = template.Template('{% load custom %}{% assignment_one_default one=99 two="hello" as var %}The result is: {{ var }}') 293 self.assertEqual(t.render(c), u'The result is: assignment_one_default - Expected result: 99, hello') 294 295 self.assertRaisesRegexp(template.TemplateSyntaxError, 296 "'assignment_one_default' received unexpected keyword argument 'three'", 297 template.Template, '{% load custom %}{% assignment_one_default 99 two="hello" three="foo" as var %}') 298 299 t = template.Template('{% load custom %}{% assignment_one_default 37 42 as var %}The result is: {{ var }}') 300 self.assertEqual(t.render(c), u'The result is: assignment_one_default - Expected result: 37, 42') 301 302 t = template.Template('{% load custom %}{% assignment_unlimited_args 37 as var %}The result is: {{ var }}') 303 self.assertEqual(t.render(c), u'The result is: assignment_unlimited_args - Expected result: 37, hi') 304 305 t = template.Template('{% load custom %}{% assignment_unlimited_args 37 42 56 89 as var %}The result is: {{ var }}') 306 self.assertEqual(t.render(c), u'The result is: assignment_unlimited_args - Expected result: 37, 42, 56, 89') 307 308 t = template.Template('{% load custom %}{% assignment_only_unlimited_args as var %}The result is: {{ var }}') 309 self.assertEqual(t.render(c), u'The result is: assignment_only_unlimited_args - Expected result: ') 310 311 t = template.Template('{% load custom %}{% assignment_only_unlimited_args 37 42 56 89 as var %}The result is: {{ var }}') 312 self.assertEqual(t.render(c), u'The result is: assignment_only_unlimited_args - Expected result: 37, 42, 56, 89') 313 142 314 self.assertRaisesRegexp(template.TemplateSyntaxError, 143 315 "'assignment_one_param' tag takes at least 2 arguments and the second last argument must be 'as'", 144 316 template.Template, '{% load custom %}{% assignment_one_param 37 %}The result is: {{ var }}') … … class CustomTagTests(TestCase): 151 323 "'assignment_one_param' tag takes at least 2 arguments and the second last argument must be 'as'", 152 324 template.Template, '{% load custom %}{% assignment_one_param 37 ass var %}The result is: {{ var }}') 153 325 326 self.assertRaisesRegexp(template.TemplateSyntaxError, 327 "'assignment_two_params' received too many positional arguments", 328 template.Template, '{% load custom %}{% assignment_two_params 37 42 56 as var %}The result is: {{ var }}') 329 330 self.assertRaisesRegexp(template.TemplateSyntaxError, 331 "'assignment_one_default' received too many positional arguments", 332 template.Template, '{% load custom %}{% assignment_one_default 37 42 56 as var %}The result is: {{ var }}') 333 334 self.assertRaisesRegexp(template.TemplateSyntaxError, 335 "'assignment_one_default' did not receive value\(s\) for the argument\(s\): 'one'", 336 template.Template, '{% load custom %}{% assignment_one_default as var %}The result is: {{ var }}') 337 338 self.assertRaisesRegexp(template.TemplateSyntaxError, 339 "'assignment_unlimited_args' did not receive value\(s\) for the argument\(s\): 'one'", 340 template.Template, '{% load custom %}{% assignment_unlimited_args as var %}The result is: {{ var }}') 341 342 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 }}') 343 self.assertEqual(t.render(c), u'The result is: assignment_unlimited_args_kwargs - Expected result: 37, 42, 56 / eggs=scrambled, four=4') 344 345 self.assertRaisesRegexp(template.TemplateSyntaxError, 346 "'assignment_unlimited_args_kwargs' received some positional argument\(s\) after some keyword argument\(s\)", 347 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 }}') 348 349 self.assertRaisesRegexp(template.TemplateSyntaxError, 350 "'assignment_unlimited_args_kwargs' received multiple values for keyword argument 'eggs'", 351 template.Template, '{% load custom %}{% assignment_unlimited_args_kwargs 37 eggs="scrambled" eggs="scrambled" as var %}The result is: {{ var }}') 352 154 353 def test_assignment_tag_registration(self): 155 354 # Test that the decorators preserve the decorated function's docstring, name and attributes. 156 355 self.verify_tag(custom.assignment_no_params, 'assignment_no_params') … … class CustomTagTests(TestCase): 158 357 self.verify_tag(custom.assignment_explicit_no_context, 'assignment_explicit_no_context') 159 358 self.verify_tag(custom.assignment_no_params_with_context, 'assignment_no_params_with_context') 160 359 self.verify_tag(custom.assignment_params_and_context, 'assignment_params_and_context') 360 self.verify_tag(custom.assignment_one_default, 'assignment_one_default') 361 self.verify_tag(custom.assignment_two_params, 'assignment_two_params') 362 self.verify_tag(custom.assignment_unlimited_args, 'assignment_unlimited_args') 363 self.verify_tag(custom.assignment_only_unlimited_args, 'assignment_only_unlimited_args') 364 self.verify_tag(custom.assignment_unlimited_args, 'assignment_unlimited_args') 365 self.verify_tag(custom.assignment_unlimited_args_kwargs, 'assignment_unlimited_args_kwargs') 366 self.verify_tag(custom.assignment_tag_without_context_parameter, 'assignment_tag_without_context_parameter') 161 367 162 368 def test_assignment_tag_missing_context(self): 163 # That the 'context' parameter must be present when takes_context is True 164 def an_assignment_tag_without_parameters(arg): 165 """Expected __doc__""" 166 return "Expected result" 167 168 register = template.Library() 169 decorator = register.assignment_tag(takes_context=True) 170 369 # The 'context' parameter must be present when takes_context is True 171 370 self.assertRaisesRegexp(template.TemplateSyntaxError, 172 " Any tag function decorated with takes_context=Truemust have a first argument of 'context'",173 decorator, an_assignment_tag_without_parameters)371 "'assignment_tag_without_context_parameter' is decorated with takes_context=True so it must have a first argument of 'context'", 372 template.Template, '{% load custom %}{% assignment_tag_without_context_parameter 123 as var %}') -
tests/regressiontests/templates/templatetags/custom.py
diff --git a/tests/regressiontests/templates/templatetags/custom.py b/tests/regressiontests/templates/templatetags/custom.py index dfa4171..0e07d53 100644
a b 1 1 from django import template 2 2 from django.template.defaultfilters import stringfilter 3 3 from django.template.loader import get_template 4 import operator 4 5 5 6 register = template.Library() 6 7 … … def params_and_context(context, arg): 40 41 return "params_and_context - Expected result (context value: %s): %s" % (context['value'], arg) 41 42 params_and_context.anything = "Expected params_and_context __dict__" 42 43 44 @register.simple_tag 45 def simple_two_params(one, two): 46 """Expected simple_two_params __doc__""" 47 return "simple_two_params - Expected result: %s, %s" % (one, two) 48 simple_two_params.anything = "Expected simple_two_params __dict__" 49 50 @register.simple_tag 51 def simple_one_default(one, two='hi'): 52 """Expected simple_one_default __doc__""" 53 return "simple_one_default - Expected result: %s, %s" % (one, two) 54 simple_one_default.anything = "Expected simple_one_default __dict__" 55 56 @register.simple_tag 57 def simple_unlimited_args(one, two='hi', *args): 58 """Expected simple_unlimited_args __doc__""" 59 return "simple_unlimited_args - Expected result: %s" % (', '.join([unicode(arg) for arg in [one, two] + list(args)])) 60 simple_unlimited_args.anything = "Expected simple_unlimited_args __dict__" 61 62 @register.simple_tag 63 def simple_only_unlimited_args(*args): 64 """Expected simple_only_unlimited_args __doc__""" 65 return "simple_only_unlimited_args - Expected result: %s" % ', '.join([unicode(arg) for arg in args]) 66 simple_only_unlimited_args.anything = "Expected simple_only_unlimited_args __dict__" 67 68 @register.simple_tag 69 def simple_unlimited_args_kwargs(one, two='hi', *args, **kwargs): 70 """Expected simple_unlimited_args_kwargs __doc__""" 71 # Sort the dictionary by key to guarantee the order for testing. 72 sorted_kwarg = sorted(kwargs.iteritems(), key=operator.itemgetter(0)) 73 return "simple_unlimited_args_kwargs - Expected result: %s / %s" % ( 74 ', '.join([unicode(arg) for arg in [one, two] + list(args)]), 75 ', '.join(['%s=%s' % (k, v) for (k, v) in sorted_kwarg]) 76 ) 77 simple_unlimited_args_kwargs.anything = "Expected simple_unlimited_args_kwargs __dict__" 78 79 @register.simple_tag(takes_context=True) 80 def simple_tag_without_context_parameter(arg): 81 """Expected simple_tag_without_context_parameter __doc__""" 82 return "Expected result" 83 simple_tag_without_context_parameter.anything = "Expected simple_tag_without_context_parameter __dict__" 84 85 @register.simple_tag(takes_context=True) 86 def current_app(context): 87 return "%s" % context.current_app 88 89 @register.simple_tag(takes_context=True) 90 def use_l10n(context): 91 return "%s" % context.use_l10n 92 93 @register.simple_tag(name='minustwo') 94 def minustwo_overridden_name(value): 95 return value - 2 96 97 register.simple_tag(lambda x: x - 1, name='minusone') 98 43 99 @register.inclusion_tag('inclusion.html') 44 100 def inclusion_no_params(): 45 101 """Expected inclusion_no_params __doc__""" … … def inclusion_params_and_context_from_template(context, arg): 100 156 return {"result" : "inclusion_params_and_context_from_template - Expected result (context value: %s): %s" % (context['value'], arg)} 101 157 inclusion_params_and_context_from_template.anything = "Expected inclusion_params_and_context_from_template __dict__" 102 158 103 @register.simple_tag(takes_context=True) 104 def current_app(context): 105 return "%s" % context.current_app 159 @register.inclusion_tag('inclusion.html') 160 def inclusion_two_params(one, two): 161 """Expected inclusion_two_params __doc__""" 162 return {"result": "inclusion_two_params - Expected result: %s, %s" % (one, two)} 163 inclusion_two_params.anything = "Expected inclusion_two_params __dict__" 164 165 @register.inclusion_tag(get_template('inclusion.html')) 166 def inclusion_two_params_from_template(one, two): 167 """Expected inclusion_two_params_from_template __doc__""" 168 return {"result": "inclusion_two_params_from_template - Expected result: %s, %s" % (one, two)} 169 inclusion_two_params_from_template.anything = "Expected inclusion_two_params_from_template __dict__" 170 171 @register.inclusion_tag('inclusion.html') 172 def inclusion_one_default(one, two='hi'): 173 """Expected inclusion_one_default __doc__""" 174 return {"result": "inclusion_one_default - Expected result: %s, %s" % (one, two)} 175 inclusion_one_default.anything = "Expected inclusion_one_default __dict__" 176 177 @register.inclusion_tag(get_template('inclusion.html')) 178 def inclusion_one_default_from_template(one, two='hi'): 179 """Expected inclusion_one_default_from_template __doc__""" 180 return {"result": "inclusion_one_default_from_template - Expected result: %s, %s" % (one, two)} 181 inclusion_one_default_from_template.anything = "Expected inclusion_one_default_from_template __dict__" 182 183 @register.inclusion_tag('inclusion.html') 184 def inclusion_unlimited_args(one, two='hi', *args): 185 """Expected inclusion_unlimited_args __doc__""" 186 return {"result": "inclusion_unlimited_args - Expected result: %s" % (', '.join([unicode(arg) for arg in [one, two] + list(args)]))} 187 inclusion_unlimited_args.anything = "Expected inclusion_unlimited_args __dict__" 188 189 @register.inclusion_tag(get_template('inclusion.html')) 190 def inclusion_unlimited_args_from_template(one, two='hi', *args): 191 """Expected inclusion_unlimited_args_from_template __doc__""" 192 return {"result": "inclusion_unlimited_args_from_template - Expected result: %s" % (', '.join([unicode(arg) for arg in [one, two] + list(args)]))} 193 inclusion_unlimited_args_from_template.anything = "Expected inclusion_unlimited_args_from_template __dict__" 194 195 @register.inclusion_tag('inclusion.html') 196 def inclusion_only_unlimited_args(*args): 197 """Expected inclusion_only_unlimited_args __doc__""" 198 return {"result": "inclusion_only_unlimited_args - Expected result: %s" % (', '.join([unicode(arg) for arg in args]))} 199 inclusion_only_unlimited_args.anything = "Expected inclusion_only_unlimited_args __dict__" 200 201 @register.inclusion_tag(get_template('inclusion.html')) 202 def inclusion_only_unlimited_args_from_template(*args): 203 """Expected inclusion_only_unlimited_args_from_template __doc__""" 204 return {"result": "inclusion_only_unlimited_args_from_template - Expected result: %s" % (', '.join([unicode(arg) for arg in args]))} 205 inclusion_only_unlimited_args_from_template.anything = "Expected inclusion_only_unlimited_args_from_template __dict__" 106 206 107 207 @register.inclusion_tag('test_incl_tag_current_app.html', takes_context=True) 108 208 def inclusion_tag_current_app(context): 209 """Expected inclusion_tag_current_app __doc__""" 109 210 return {} 110 111 @register.simple_tag(takes_context=True) 112 def use_l10n(context): 113 return "%s" % context.use_l10n 211 inclusion_tag_current_app.anything = "Expected inclusion_tag_current_app __dict__" 114 212 115 213 @register.inclusion_tag('test_incl_tag_use_l10n.html', takes_context=True) 116 214 def inclusion_tag_use_l10n(context): 215 """Expected inclusion_tag_use_l10n __doc__""" 117 216 return {} 217 inclusion_tag_use_l10n.anything = "Expected inclusion_tag_use_l10n __dict__" 218 219 @register.inclusion_tag('inclusion.html') 220 def inclusion_unlimited_args_kwargs(one, two='hi', *args, **kwargs): 221 """Expected inclusion_unlimited_args_kwargs __doc__""" 222 # Sort the dictionary by key to guarantee the order for testing. 223 sorted_kwarg = sorted(kwargs.iteritems(), key=operator.itemgetter(0)) 224 return {"result": "inclusion_unlimited_args_kwargs - Expected result: %s / %s" % ( 225 ', '.join([unicode(arg) for arg in [one, two] + list(args)]), 226 ', '.join(['%s=%s' % (k, v) for (k, v) in sorted_kwarg]) 227 )} 228 inclusion_unlimited_args_kwargs.anything = "Expected inclusion_unlimited_args_kwargs __dict__" 229 230 @register.inclusion_tag('inclusion.html', takes_context=True) 231 def inclusion_tag_without_context_parameter(arg): 232 """Expected inclusion_tag_without_context_parameter __doc__""" 233 return {} 234 inclusion_tag_without_context_parameter.anything = "Expected inclusion_tag_without_context_parameter __dict__" 118 235 119 236 @register.assignment_tag 120 237 def assignment_no_params(): … … def assignment_params_and_context(context, arg): 146 263 return "assignment_params_and_context - Expected result (context value: %s): %s" % (context['value'], arg) 147 264 assignment_params_and_context.anything = "Expected assignment_params_and_context __dict__" 148 265 149 register.simple_tag(lambda x: x - 1, name='minusone') 266 @register.assignment_tag 267 def assignment_two_params(one, two): 268 """Expected assignment_two_params __doc__""" 269 return "assignment_two_params - Expected result: %s, %s" % (one, two) 270 assignment_two_params.anything = "Expected assignment_two_params __dict__" 150 271 151 @register.simple_tag(name='minustwo') 152 def minustwo_overridden_name(value): 153 return value - 2 272 @register.assignment_tag 273 def assignment_one_default(one, two='hi'): 274 """Expected assignment_one_default __doc__""" 275 return "assignment_one_default - Expected result: %s, %s" % (one, two) 276 assignment_one_default.anything = "Expected assignment_one_default __dict__" 277 278 @register.assignment_tag 279 def assignment_unlimited_args(one, two='hi', *args): 280 """Expected assignment_unlimited_args __doc__""" 281 return "assignment_unlimited_args - Expected result: %s" % (', '.join([unicode(arg) for arg in [one, two] + list(args)])) 282 assignment_unlimited_args.anything = "Expected assignment_unlimited_args __dict__" 283 284 @register.assignment_tag 285 def assignment_only_unlimited_args(*args): 286 """Expected assignment_only_unlimited_args __doc__""" 287 return "assignment_only_unlimited_args - Expected result: %s" % ', '.join([unicode(arg) for arg in args]) 288 assignment_only_unlimited_args.anything = "Expected assignment_only_unlimited_args __dict__" 289 290 @register.assignment_tag 291 def assignment_unlimited_args_kwargs(one, two='hi', *args, **kwargs): 292 """Expected assignment_unlimited_args_kwargs __doc__""" 293 # Sort the dictionary by key to guarantee the order for testing. 294 sorted_kwarg = sorted(kwargs.iteritems(), key=operator.itemgetter(0)) 295 return "assignment_unlimited_args_kwargs - Expected result: %s / %s" % ( 296 ', '.join([unicode(arg) for arg in [one, two] + list(args)]), 297 ', '.join(['%s=%s' % (k, v) for (k, v) in sorted_kwarg]) 298 ) 299 assignment_unlimited_args_kwargs.anything = "Expected assignment_unlimited_args_kwargs __dict__" 300 301 @register.assignment_tag(takes_context=True) 302 def assignment_tag_without_context_parameter(arg): 303 """Expected assignment_tag_without_context_parameter __doc__""" 304 return "Expected result" 305 assignment_tag_without_context_parameter.anything = "Expected assignment_tag_without_context_parameter __dict__" 306 No newline at end of file