Ticket #1105: super_simple_tag.diff
File super_simple_tag.diff, 11.7 KB (added by , 16 years ago) |
---|
-
django/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_block=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_block: 810 nodelist = parser.parse(('end%s' % name,)) 811 parser.delete_first_token() 812 node_class = curry(node_class, block_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_block=None, takes_context=None): 869 def dec(func): 870 params, xx, xxx, defaults = getargspec(func) 871 if takes_context and takes_block: 872 if params[0] == 'context' and params[1] == 'block_nodelist': 873 params = params[2:] 874 else: 875 raise TemplateSyntaxError("Any tag function decorated both with takes_context=True and with takes_block=True must have a first argument of 'context', and a second argument of 'block_nodelist'") 876 elif takes_block: 877 if params[0] == 'block_nodelist': 878 params = params[1:] 879 else: 880 raise TemplateSyntaxError("Any tag function decorated with takes_block=True must have a first argument of 'block_nodelist'") 881 elif takes_context: 882 if params[0] == 'context': 883 params = params[1:] 884 else: 885 raise TemplateSyntaxError("Any tag function decorated with takes_context=True must have a first argument of 'context'") 864 886 865 class SimpleNode(Node): 866 def __init__(self, vars_to_resolve): 867 self.vars_to_resolve = map(Variable, vars_to_resolve) 887 class SimpleNode(Node): 888 def __init__(self, vars_to_resolve, takes_context=False, block_nodelist=None): 889 self.vars_to_resolve = map(Variable, vars_to_resolve) 890 self.takes_context = takes_context 891 self.block_nodelist = block_nodelist 892 if block_nodelist is not None: 893 # Save a nodelist attribute so it can be used in the Node.get_nodes_by_type method 894 self.nodelist = block_nodelist 895 896 def render(self, context): 897 resolved_vars = [var.resolve(context) for var in self.vars_to_resolve] 898 if self.takes_context and self.block_nodelist: 899 func_args = [context, self.block_nodelist] + resolved_vars 900 elif self.block_nodelist: 901 func_args = [self.block_nodelist] + resolved_vars 902 elif self.takes_context: 903 func_args = [context] + resolved_vars 904 else: 905 func_args = resolved_vars 906 return func(*func_args) 868 907 869 def render(self, context): 870 resolved_vars = [var.resolve(context) for var in self.vars_to_resolve] 871 return func(*resolved_vars) 908 compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, SimpleNode, takes_block=takes_block, takes_context=takes_context) 909 compile_func.__doc__ = func.__doc__ 910 self.tag(getattr(func, "_decorated_function", func).__name__, compile_func) 911 return func 912 913 if takes_context is not None or takes_block is not None: 914 # Examples: @register.simple_tag(takes_context=True) or @register.simple_tag(takes_context=True, takes_block=True) 915 return dec 916 elif compile_function is None: 917 # @register.simple_tag() 918 return dec 919 elif callable(compile_function): 920 # @register.simple_tag 921 return dec(compile_function) 922 else: 923 raise TemplateSyntaxError("Incorrect parameters for simple_tag. Accepted parameters are: takes_context and/or takes_block.") 872 924 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 925 def inclusion_tag(self, file_name, context_class=Context, takes_context=False): 879 926 def dec(func): 880 927 params, xx, xxx, defaults = getargspec(func) -
django/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 -
django/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 the 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 the inner block. 21 def a_simple_tag_with_block(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 the context and the inner block. 27 def a_simple_tag_with_context_and_block(context, block_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 the context and the inner block, but that has arguments in wrong order. 33 def a_simple_tag_with_context_and_block_wrong_order(block_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_block=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_block=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 'block_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_block=True) 69 self.assertRaises(template.TemplateSyntaxError, decorator, a_simple_tag_without_parameters) 70 71 decorator = register.simple_tag(takes_block=True, takes_context=True) 72 self.assertRaises(template.TemplateSyntaxError, decorator, a_simple_tag_with_context_and_block_wrong_order) -
django/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