Ticket #1105: 1105.simple_tag.r7813.diff
File 1105.simple_tag.r7813.diff, 13.3 KB (added by , 16 years ago) |
---|
-
django/template/__init__.py
792 792 else: 793 793 return force_unicode(output) 794 794 795 def generic_tag_compiler(params, defaults, name, node_class, parser, token ):795 def generic_tag_compiler(params, defaults, name, node_class, parser, token, takes_context=False, takes_nodelist=False): 796 796 "Returns a template.Node subclass." 797 797 bits = token.split_contents()[1:] 798 798 bmax = len(params) … … 804 804 else: 805 805 message = "%s takes between %s and %s arguments" % (name, bmin, bmax) 806 806 raise TemplateSyntaxError(message) 807 if takes_context: 808 node_class = curry(node_class, takes_context=takes_context) 809 if takes_nodelist: 810 nodelist = parser.parse(('end%s' % name,)) 811 parser.delete_first_token() 812 node_class = curry(node_class, nodelist=nodelist) 807 813 return node_class(bits) 808 814 809 815 class Library(object): … … 859 865 self.filters[getattr(func, "_decorated_function", func).__name__] = func 860 866 return func 861 867 862 def simple_tag(self,func): 863 params, xx, xxx, defaults = getargspec(func) 868 def simple_tag(self, compile_function=None, takes_nodelist=False, takes_context=False): 869 def dec(func): 870 params, xx, xxx, defaults = getargspec(func) 871 offset = 0 872 if takes_context: 873 if params[offset] != 'context': 874 raise TemplateSyntaxError("Any tag function decorated with takes_context=True must take positional 'context' argument") 875 offset += 1 876 if takes_nodelist: 877 if params[offset] != 'nodelist': 878 raise TemplateSyntaxError("Any tag function decorated with takes_nodelist=True must take positional 'nodelist' argument") 879 offset += 1 880 params = params[offset:] 881 882 class SimpleNode(Node): 883 def __init__(self, vars_to_resolve, takes_context=False, nodelist=None): 884 self.vars_to_resolve = map(Variable, vars_to_resolve) 885 self.takes_context = takes_context 886 self.nodelist = nodelist 887 888 def render(self, context): 889 func_args = [] 890 if self.takes_context: 891 func_args.append(context) 892 if self.nodelist: 893 func_args.append(self.nodelist) 894 func_args += [var.resolve(context) for var in self.vars_to_resolve] 895 return func(*func_args) 864 896 865 class SimpleNode(Node): 866 def __init__(self, vars_to_resolve): 867 self.vars_to_resolve = map(Variable, vars_to_resolve) 897 compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, SimpleNode, takes_nodelist=takes_nodelist, takes_context=takes_context) 898 compile_func.__doc__ = func.__doc__ 899 self.tag(getattr(func, "_decorated_function", func).__name__, compile_func) 900 return func 901 902 if not callable(compile_function): 903 return dec 904 return dec(compile_function) 868 905 869 def render(self, context):870 resolved_vars = [var.resolve(context) for var in self.vars_to_resolve]871 return func(*resolved_vars)872 873 compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, SimpleNode)874 compile_func.__doc__ = func.__doc__875 self.tag(getattr(func, "_decorated_function", func).__name__, compile_func)876 return func877 878 906 def inclusion_tag(self, file_name, context_class=Context, takes_context=False): 879 907 def dec(func): 880 908 params, xx, xxx, defaults = getargspec(func) -
tests/regressiontests/templates/tests.py
17 17 from django.utils.safestring import mark_safe 18 18 from django.utils.tzinfo import LocalTimezone 19 19 20 from decorators import DecoratorsTest 20 21 from unicode import unicode_tests 21 22 from context import context_tests 22 23 -
tests/regressiontests/templates/decorators.py
1 from unittest import TestCase 2 from sys import version_info 3 4 from django import template 5 6 register = template.Library() 7 8 # Very simple tag, with no parameters. 9 def a_simple_tag_without_parameters(arg): 10 """Expected __doc__""" 11 return "Expected result" 12 a_simple_tag_without_parameters.anything = "Expected __dict__" 13 14 # Tag that takes a context. 15 def a_simple_tag_with_context(context, arg): 16 """Expected __doc__""" 17 return "Expected result" 18 a_simple_tag_with_context.anything = "Expected __dict__" 19 20 # Tag that takes a nodelist. 21 def a_simple_tag_with_block(nodelist, arg): 22 """Expected __doc__""" 23 return "Expected result" 24 a_simple_tag_with_block.anything = "Expected __dict__" 25 26 # Tag that takes both a context and a nodlist. 27 def a_simple_tag_with_context_and_block(context, nodelist, arg): 28 """Expected __doc__""" 29 return "Expected result" 30 a_simple_tag_with_context_and_block.anything = "Expected __dict__" 31 32 # Tag that *wants* to take both a context and a nodelist, but that has arguments in wrong order. 33 def a_simple_tag_with_context_and_block_wrong_order(nodelist, context, arg): 34 """Expected __doc__""" 35 return "Expected result" 36 a_simple_tag_with_context_and_block_wrong_order.anything = "Expected __dict__" 37 38 39 40 41 class DecoratorsTest(TestCase): 42 def verify_decorator(self, decorator, func_name): 43 # Only check __name__ on Python 2.4 or later since __name__ can't be 44 # assigned to in earlier Python versions. 45 if version_info[0] >= 2 and version_info[1] >= 4: 46 self.assertEquals(decorator.__name__, func_name) 47 self.assertEquals(decorator.__doc__, 'Expected __doc__') 48 self.assertEquals(decorator.__dict__['anything'], 'Expected __dict__') 49 50 def test_simple_tag(self): 51 # Test that the decorators preserve the decorated function's docstring, name and attributes. 52 decorator = register.simple_tag(a_simple_tag_without_parameters) 53 self.verify_decorator(decorator, 'a_simple_tag_without_parameters') 54 55 decorator = register.simple_tag(takes_context=True)(a_simple_tag_with_context) 56 self.verify_decorator(decorator, 'a_simple_tag_with_context') 57 58 decorator = register.simple_tag(takes_nodelist=True)(a_simple_tag_with_block) 59 self.verify_decorator(decorator, 'a_simple_tag_with_block') 60 61 decorator = register.simple_tag(takes_context=True, takes_nodelist=True)(a_simple_tag_with_context_and_block) 62 self.verify_decorator(decorator, 'a_simple_tag_with_context_and_block') 63 64 # Now test that 'context' and 'nodelist' arguments and their order are correct. 65 decorator = register.simple_tag(takes_context=True) 66 self.assertRaises(template.TemplateSyntaxError, decorator, a_simple_tag_without_parameters) 67 68 decorator = register.simple_tag(takes_nodelist=True) 69 self.assertRaises(template.TemplateSyntaxError, decorator, a_simple_tag_without_parameters) 70 71 decorator = register.simple_tag(takes_nodelist=True, takes_context=True) 72 self.assertRaises(template.TemplateSyntaxError, decorator, a_simple_tag_with_context_and_block_wrong_order) 73 from unittest import TestCase 74 from sys import version_info 75 76 from django import template 77 78 register = template.Library() 79 80 # Very simple tag, with no parameters. 81 def a_simple_tag_without_parameters(arg): 82 """Expected __doc__""" 83 return "Expected result" 84 a_simple_tag_without_parameters.anything = "Expected __dict__" 85 86 # Tag that takes the context. 87 def a_simple_tag_with_context(context, arg): 88 """Expected __doc__""" 89 return "Expected result" 90 a_simple_tag_with_context.anything = "Expected __dict__" 91 92 # Tag that takes the inner block. 93 def a_simple_tag_with_block(nodelist, arg): 94 """Expected __doc__""" 95 return "Expected result" 96 a_simple_tag_with_block.anything = "Expected __dict__" 97 98 # Tag that takes both the context and the inner block. 99 def a_simple_tag_with_context_and_block(context, nodelist, arg): 100 """Expected __doc__""" 101 return "Expected result" 102 a_simple_tag_with_context_and_block.anything = "Expected __dict__" 103 104 # Tag that *wants* to take both the context and the inner block, but that has arguments in wrong order. 105 def a_simple_tag_with_context_and_block_wrong_order(nodelist, context, arg): 106 """Expected __doc__""" 107 return "Expected result" 108 a_simple_tag_with_context_and_block_wrong_order.anything = "Expected __dict__" 109 110 111 112 113 class DecoratorsTest(TestCase): 114 def verify_decorator(self, decorator, func_name): 115 # Only check __name__ on Python 2.4 or later since __name__ can't be 116 # assigned to in earlier Python versions. 117 if version_info[0] >= 2 and version_info[1] >= 4: 118 self.assertEquals(decorator.__name__, func_name) 119 self.assertEquals(decorator.__doc__, 'Expected __doc__') 120 self.assertEquals(decorator.__dict__['anything'], 'Expected __dict__') 121 122 def test_simple_tag(self): 123 # Test that the decorators preserve the decorated function's docstring, name and attributes. 124 decorator = register.simple_tag(a_simple_tag_without_parameters) 125 self.verify_decorator(decorator, 'a_simple_tag_without_parameters') 126 127 decorator = register.simple_tag(takes_context=True)(a_simple_tag_with_context) 128 self.verify_decorator(decorator, 'a_simple_tag_with_context') 129 130 decorator = register.simple_tag(takes_nodelist=True)(a_simple_tag_with_block) 131 self.verify_decorator(decorator, 'a_simple_tag_with_block') 132 133 decorator = register.simple_tag(takes_context=True, takes_nodelist=True)(a_simple_tag_with_context_and_block) 134 self.verify_decorator(decorator, 'a_simple_tag_with_context_and_block') 135 136 # Now test that 'context' and 'nodelist' arguments and their order are correct. 137 decorator = register.simple_tag(takes_context=True) 138 self.assertRaises(template.TemplateSyntaxError, decorator, a_simple_tag_without_parameters) 139 140 decorator = register.simple_tag(takes_nodelist=True) 141 self.assertRaises(template.TemplateSyntaxError, decorator, a_simple_tag_without_parameters) 142 143 decorator = register.simple_tag(takes_nodelist=True, takes_context=True) 144 self.assertRaises(template.TemplateSyntaxError, decorator, a_simple_tag_with_context_and_block_wrong_order) -
docs/templates_python.txt
1181 1181 * If the argument was a template variable, our function is passed the 1182 1182 current value of the variable, not the variable itself. 1183 1183 1184 When your template tag does not need access to the current context, writing a 1185 function to work with the input values and using the ``simple_tag`` helper is 1186 the easiest way to create a new tag. 1184 If your template tag needs to access the current context, you can use the 1185 ``takes_context`` option as follows:: 1187 1186 1187 # The first argument *must* be called "context" here. 1188 def current_time(context, format_string): 1189 timezone = context['timezone'] 1190 ... 1191 1192 register.simple_tag(takes_context=True)(current_time) 1193 1194 You can also use the decorator syntax if running in Python 2.4:: 1195 1196 @register.simple_tag(takes_context=True) 1197 def current_time(context, format_string): 1198 ... 1199 1200 For more information on how the ``takes_context`` option works, see the section 1201 on `inclusion tags`_. 1202 1203 If your template is a simple block tag, you can use the ``takes_block`` option as 1204 follows:: 1205 1206 # The first argument *must* be called "block_nodelist". 1207 def my_bock_tag(block_nodelist, an_argument): 1208 ... 1209 register.simple_tag(takes_block=True)(my_bock_tag) 1210 1211 In the above example, ``block_nodelist`` is a list of all nodes between 1212 ``{% my_bock_tag %}`` and ``{% endmy_bock_tag %}``, not counting 1213 ``{% my_bock_tag %}`` and ``{% endmy_bock_tag %}`` themselves. 1214 1215 It is also possible to use both ``takes_context`` and ``takes_block`` at the 1216 same time. For example:: 1217 1218 # The first argument *must* be called "context" and the second one "block_nodelist". 1219 def my_bock_tag(context, block_nodelist, an_argument): 1220 timezone = context['timezone'] 1221 content = block_nodelist.render(context) 1222 ... 1223 register.simple_tag(takes_context=True, takes_block=True)(my_bock_tag) 1224 1225 If you need to create a more complex block tag, refer to the section on 1226 `parsing until another block tag`_. 1227 1228 _inclusion tags: #inclusion-tags 1229 _block tags: #parsing-until-another-block-tag 1230 1188 1231 Inclusion tags 1189 1232 ~~~~~~~~~~~~~~ 1190 1233