Index: django/template/__init__.py
===================================================================
--- django/template/__init__.py	(revision 7813)
+++ django/template/__init__.py	(working copy)
@@ -792,7 +792,7 @@
         else:
             return force_unicode(output)
 
-def generic_tag_compiler(params, defaults, name, node_class, parser, token):
+def generic_tag_compiler(params, defaults, name, node_class, parser, token, takes_context=False, takes_nodelist=False):
     "Returns a template.Node subclass."
     bits = token.split_contents()[1:]
     bmax = len(params)
@@ -804,6 +804,12 @@
         else:
             message = "%s takes between %s and %s arguments" % (name, bmin, bmax)
         raise TemplateSyntaxError(message)
+    if takes_context:
+        node_class = curry(node_class, takes_context=takes_context) 
+    if takes_nodelist:
+        nodelist = parser.parse(('end%s' % name,)) 
+        parser.delete_first_token() 
+        node_class = curry(node_class, nodelist=nodelist) 
     return node_class(bits)
 
 class Library(object):
@@ -859,22 +865,44 @@
         self.filters[getattr(func, "_decorated_function", func).__name__] = func
         return func
 
-    def simple_tag(self,func):
-        params, xx, xxx, defaults = getargspec(func)
+    def simple_tag(self, compile_function=None, takes_nodelist=False, takes_context=False):
+        def dec(func):
+            params, xx, xxx, defaults = getargspec(func)
+            offset = 0
+            if takes_context:
+                if params[offset] != 'context':
+                    raise TemplateSyntaxError("Any tag function decorated with takes_context=True must take positional 'context' argument")
+                offset += 1
+            if takes_nodelist:
+                if params[offset] != 'nodelist':
+                    raise TemplateSyntaxError("Any tag function decorated with takes_nodelist=True must take positional 'nodelist' argument")
+                offset += 1
+            params = params[offset:]
+            
+            class SimpleNode(Node):
+                def __init__(self, vars_to_resolve, takes_context=False, nodelist=None): 
+                    self.vars_to_resolve = map(Variable, vars_to_resolve)
+                    self.takes_context = takes_context
+                    self.nodelist = nodelist
+    
+                def render(self, context):
+                    func_args = []
+                    if self.takes_context:
+                        func_args.append(context)
+                    if self.nodelist:
+                        func_args.append(self.nodelist)
+                    func_args += [var.resolve(context) for var in self.vars_to_resolve]
+                    return func(*func_args)
 
-        class SimpleNode(Node):
-            def __init__(self, vars_to_resolve):
-                self.vars_to_resolve = map(Variable, vars_to_resolve)
+            compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, SimpleNode, takes_nodelist=takes_nodelist, takes_context=takes_context)
+            compile_func.__doc__ = func.__doc__
+            self.tag(getattr(func, "_decorated_function", func).__name__, compile_func)
+            return func
+        
+        if not callable(compile_function):
+            return dec
+        return dec(compile_function)
 
-            def render(self, context):
-                resolved_vars = [var.resolve(context) for var in self.vars_to_resolve]
-                return func(*resolved_vars)
-
-        compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, SimpleNode)
-        compile_func.__doc__ = func.__doc__
-        self.tag(getattr(func, "_decorated_function", func).__name__, compile_func)
-        return func
-
     def inclusion_tag(self, file_name, context_class=Context, takes_context=False):
         def dec(func):
             params, xx, xxx, defaults = getargspec(func)
Index: tests/regressiontests/templates/tests.py
===================================================================
--- tests/regressiontests/templates/tests.py	(revision 7813)
+++ tests/regressiontests/templates/tests.py	(working copy)
@@ -17,6 +17,7 @@
 from django.utils.safestring import mark_safe
 from django.utils.tzinfo import LocalTimezone
 
+from decorators import DecoratorsTest
 from unicode import unicode_tests
 from context import context_tests
 
Index: tests/regressiontests/templates/decorators.py
===================================================================
--- tests/regressiontests/templates/decorators.py	(revision 0)
+++ tests/regressiontests/templates/decorators.py	(revision 0)
@@ -0,0 +1,144 @@
+from unittest import TestCase
+from sys import version_info
+
+from django import template
+
+register = template.Library()
+
+# Very simple tag, with no parameters.
+def a_simple_tag_without_parameters(arg):
+    """Expected __doc__"""
+    return "Expected result"
+a_simple_tag_without_parameters.anything = "Expected __dict__"
+
+# Tag that takes a context.
+def a_simple_tag_with_context(context, arg):
+    """Expected __doc__"""
+    return "Expected result"
+a_simple_tag_with_context.anything = "Expected __dict__"
+
+# Tag that takes a nodelist.
+def a_simple_tag_with_block(nodelist, arg):
+    """Expected __doc__"""
+    return "Expected result"
+a_simple_tag_with_block.anything = "Expected __dict__"
+
+# Tag that takes both a context and a nodlist.
+def a_simple_tag_with_context_and_block(context, nodelist, arg):
+    """Expected __doc__"""
+    return "Expected result"
+a_simple_tag_with_context_and_block.anything = "Expected __dict__"
+
+# Tag that *wants* to take both a context and a nodelist, but that has arguments in wrong order.
+def a_simple_tag_with_context_and_block_wrong_order(nodelist, context, arg):
+    """Expected __doc__"""
+    return "Expected result"
+a_simple_tag_with_context_and_block_wrong_order.anything = "Expected __dict__"
+
+
+
+
+class DecoratorsTest(TestCase):
+    def verify_decorator(self, decorator, func_name):
+        # Only check __name__ on Python 2.4 or later since __name__ can't be
+        # assigned to in earlier Python versions.
+        if version_info[0] >= 2 and version_info[1] >= 4:
+            self.assertEquals(decorator.__name__, func_name)
+        self.assertEquals(decorator.__doc__, 'Expected __doc__')
+        self.assertEquals(decorator.__dict__['anything'], 'Expected __dict__')
+
+    def test_simple_tag(self):
+        # Test that the decorators preserve the decorated function's docstring, name and attributes.
+        decorator = register.simple_tag(a_simple_tag_without_parameters)
+        self.verify_decorator(decorator, 'a_simple_tag_without_parameters')
+        
+        decorator = register.simple_tag(takes_context=True)(a_simple_tag_with_context)
+        self.verify_decorator(decorator, 'a_simple_tag_with_context')
+        
+        decorator = register.simple_tag(takes_nodelist=True)(a_simple_tag_with_block)
+        self.verify_decorator(decorator, 'a_simple_tag_with_block')
+        
+        decorator = register.simple_tag(takes_context=True, takes_nodelist=True)(a_simple_tag_with_context_and_block)
+        self.verify_decorator(decorator, 'a_simple_tag_with_context_and_block')
+        
+        # Now test that 'context' and 'nodelist' arguments and their order are correct.
+        decorator = register.simple_tag(takes_context=True)
+        self.assertRaises(template.TemplateSyntaxError, decorator, a_simple_tag_without_parameters)
+        
+        decorator = register.simple_tag(takes_nodelist=True)
+        self.assertRaises(template.TemplateSyntaxError, decorator, a_simple_tag_without_parameters)
+        
+        decorator = register.simple_tag(takes_nodelist=True, takes_context=True)
+        self.assertRaises(template.TemplateSyntaxError, decorator, a_simple_tag_with_context_and_block_wrong_order)
+from unittest import TestCase
+from sys import version_info
+
+from django import template
+
+register = template.Library()
+
+# Very simple tag, with no parameters.
+def a_simple_tag_without_parameters(arg):
+    """Expected __doc__"""
+    return "Expected result"
+a_simple_tag_without_parameters.anything = "Expected __dict__"
+
+# Tag that takes the context.
+def a_simple_tag_with_context(context, arg):
+    """Expected __doc__"""
+    return "Expected result"
+a_simple_tag_with_context.anything = "Expected __dict__"
+
+# Tag that takes the inner block.
+def a_simple_tag_with_block(nodelist, arg):
+    """Expected __doc__"""
+    return "Expected result"
+a_simple_tag_with_block.anything = "Expected __dict__"
+
+# Tag that takes both the context and the inner block.
+def a_simple_tag_with_context_and_block(context, nodelist, arg):
+    """Expected __doc__"""
+    return "Expected result"
+a_simple_tag_with_context_and_block.anything = "Expected __dict__"
+
+# Tag that *wants* to take both the context and the inner block, but that has arguments in wrong order.
+def a_simple_tag_with_context_and_block_wrong_order(nodelist, context, arg):
+    """Expected __doc__"""
+    return "Expected result"
+a_simple_tag_with_context_and_block_wrong_order.anything = "Expected __dict__"
+
+
+
+
+class DecoratorsTest(TestCase):
+    def verify_decorator(self, decorator, func_name):
+        # Only check __name__ on Python 2.4 or later since __name__ can't be
+        # assigned to in earlier Python versions.
+        if version_info[0] >= 2 and version_info[1] >= 4:
+            self.assertEquals(decorator.__name__, func_name)
+        self.assertEquals(decorator.__doc__, 'Expected __doc__')
+        self.assertEquals(decorator.__dict__['anything'], 'Expected __dict__')
+
+    def test_simple_tag(self):
+        # Test that the decorators preserve the decorated function's docstring, name and attributes.
+        decorator = register.simple_tag(a_simple_tag_without_parameters)
+        self.verify_decorator(decorator, 'a_simple_tag_without_parameters')
+        
+        decorator = register.simple_tag(takes_context=True)(a_simple_tag_with_context)
+        self.verify_decorator(decorator, 'a_simple_tag_with_context')
+        
+        decorator = register.simple_tag(takes_nodelist=True)(a_simple_tag_with_block)
+        self.verify_decorator(decorator, 'a_simple_tag_with_block')
+        
+        decorator = register.simple_tag(takes_context=True, takes_nodelist=True)(a_simple_tag_with_context_and_block)
+        self.verify_decorator(decorator, 'a_simple_tag_with_context_and_block')
+        
+        # Now test that 'context' and 'nodelist' arguments and their order are correct.
+        decorator = register.simple_tag(takes_context=True)
+        self.assertRaises(template.TemplateSyntaxError, decorator, a_simple_tag_without_parameters)
+        
+        decorator = register.simple_tag(takes_nodelist=True)
+        self.assertRaises(template.TemplateSyntaxError, decorator, a_simple_tag_without_parameters)
+        
+        decorator = register.simple_tag(takes_nodelist=True, takes_context=True)
+        self.assertRaises(template.TemplateSyntaxError, decorator, a_simple_tag_with_context_and_block_wrong_order)
Index: docs/templates_python.txt
===================================================================
--- docs/templates_python.txt	(revision 7813)
+++ docs/templates_python.txt	(working copy)
@@ -1181,10 +1181,53 @@
     * If the argument was a template variable, our function is passed the
       current value of the variable, not the variable itself.
 
-When your template tag does not need access to the current context, writing a
-function to work with the input values and using the ``simple_tag`` helper is
-the easiest way to create a new tag.
+If your template tag needs to access the current context, you can use the
+``takes_context`` option as follows::
 
+    # The first argument *must* be called "context" here.
+    def current_time(context, format_string):
+        timezone = context['timezone']
+        ...
+
+    register.simple_tag(takes_context=True)(current_time)
+
+You can also use the decorator syntax if running in Python 2.4::
+
+    @register.simple_tag(takes_context=True)
+    def current_time(context, format_string):
+        ...
+
+For more information on how the ``takes_context`` option works, see the section
+on `inclusion tags`_.
+
+If your template is a simple block tag, you can use the ``takes_block`` option as
+follows::
+
+    # The first argument *must* be called "block_nodelist".
+    def my_bock_tag(block_nodelist, an_argument):
+        ...
+    register.simple_tag(takes_block=True)(my_bock_tag)
+
+In the above example, ``block_nodelist`` is a list of all nodes between 
+``{% my_bock_tag %}`` and ``{% endmy_bock_tag %}``, not counting 
+``{% my_bock_tag %}`` and ``{% endmy_bock_tag %}`` themselves.
+
+It is also possible to use both ``takes_context`` and ``takes_block`` at the
+same time. For example::
+
+    # The first argument *must* be called "context" and the second one "block_nodelist".
+    def my_bock_tag(context, block_nodelist, an_argument):
+        timezone = context['timezone']
+        content = block_nodelist.render(context)
+        ...
+    register.simple_tag(takes_context=True, takes_block=True)(my_bock_tag)
+
+If you need to create a more complex block tag, refer to the section on 
+`parsing until another block tag`_.
+
+_inclusion tags: #inclusion-tags
+_block tags: #parsing-until-another-block-tag
+
 Inclusion tags
 ~~~~~~~~~~~~~~
 
