Index: template/__init__.py
===================================================================
--- template/__init__.py	(revision 7625)
+++ template/__init__.py	(working copy)
@@ -55,10 +55,14 @@
 from django.utils.itercompat import is_iterable
 from django.utils.functional import curry, Promise
 from django.utils.text import smart_split
-from django.utils.encoding import smart_unicode, force_unicode
+from django.utils.encoding import smart_str, smart_unicode, force_unicode
 from django.utils.translation import ugettext as _
 from django.utils.safestring import SafeData, EscapeData, mark_safe, mark_for_escaping
 from django.utils.html import escape
+try:
+    set
+except NameError:
+    from sets import Set as set   # Python 2.3 fallback
 
 __all__ = ('Template', 'Context', 'RequestContext', 'compile_string')
 
@@ -71,6 +75,7 @@
 FILTER_SEPARATOR = '|'
 FILTER_ARGUMENT_SEPARATOR = ':'
 VARIABLE_ATTRIBUTE_SEPARATOR = '.'
+TAG_KEYWORD_ARGUMENT_SEPARATOR = '='
 BLOCK_TAG_START = '{%'
 BLOCK_TAG_END = '%}'
 VARIABLE_TAG_START = '{{'
@@ -592,6 +597,68 @@
     """
     return Variable(path).resolve(context)
 
+def resolve_all_variables_for_call(variables, context, name, params, num_default,
+                                   varargs, varkw):
+    """
+    resolve an iterable of Variable objects into a list of args and a dict
+    of keyword arguments. support full python style keyword argument
+    processing::
+        >>> def foo(a, b, c=1, d=2):
+        ...     pass
+        >>> foo(1, 2)
+        >>> foo(1, b=2)
+        >>> foo(b=2, a=1, d=3)
+    """
+    args = []
+    kwdargs = {}
+    found_kwd = False
+    for variable in variables:
+        if not found_kwd:
+            try:
+                args.append(variable.resolve(context))
+            except VariableDoesNotExist:
+                if variable.var.count(TAG_KEYWORD_ARGUMENT_SEPARATOR) != 1:
+                    raise
+                found_kwd = True
+        if found_kwd:
+            try:
+                var, path = variable.var.split(TAG_KEYWORD_ARGUMENT_SEPARATOR)
+            except ValueError:
+                raise TemplateSyntaxError(
+                    "Expected keyword assignemnt, found '%s' instead" %
+                    variable.var)
+            if params and not varkw and name not in params:
+                raise TemplateSyntaxError(
+                    "%s got an unexpected keyword argument '%s'" % (name, var))
+            if var in kwdargs:
+                raise TemplateSyntaxError(
+                    "got multiple values for keyword argument '%s'"%(name,var))
+            kwdargs[smart_str(var)] = Variable(path).resolve(context)
+    if ((len(args) > len(params) and not varargs) or
+        ((len(args)+len(kwdargs)) > len(params) and not varkw)):
+        raise TemplateSyntaxError(
+                "%s takes at most %s arguments. (%s given)" % (
+                    name, len(params), len(args) + len(kwdargs)) )
+    if len(args) != (len(params)-num_default):
+        if len(args)>(len(params)-num_default):
+            # some args are kwd args (maybe multiple keyword error)
+            if not varargs:
+                allowed = set(params[len(args):])
+                not_allowed = set(kwdargs) - allowed
+                if not_allowed:
+                    raise TemplateSyntaxError(
+                        "%s got multiple values for keyword arguments: %s" % (
+                            name, ", ".join(not_allowed) ))
+        elif not varkw:
+            # not enough required parameters error
+            required = set(params[len(args):-num_default])
+            missing = required - set(kwdargs)
+            if missing:
+                raise TemplateSyntaxError(
+                    "%s takes at least %s non-keyword arguments (%s given)" % (
+                        name, len(params) - num_default, len(args)))
+    return args, kwdargs
+
 class Variable(object):
     """
     A template variable, resolvable against a given context. The variable may be
@@ -860,16 +927,21 @@
         return func
 
     def simple_tag(self,func):
-        params, xx, xxx, defaults = getargspec(func)
+        params, varargs, varkw, defaults = getargspec(func)
 
         class SimpleNode(Node):
             def __init__(self, vars_to_resolve):
                 self.vars_to_resolve = map(Variable, vars_to_resolve)
 
             def render(self, context):
-                resolved_vars = [var.resolve(context) for var in self.vars_to_resolve]
-                return func(*resolved_vars)
+                args, kwdargs = resolve_all_variables_for_call(
+                    self.vars_to_resolve, context,
+                    getattr(func, "_decorated_function", func).__name__,
+                    params, defaults and len(defaults) or 0,
+                    varargs, varkw)
 
+                return func(*args, **kwdargs)
+
         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)
@@ -877,7 +949,7 @@
 
     def inclusion_tag(self, file_name, context_class=Context, takes_context=False):
         def dec(func):
-            params, xx, xxx, defaults = getargspec(func)
+            params, varargs, varkw, defaults = getargspec(func)
             if takes_context:
                 if params[0] == 'context':
                     params = params[1:]
@@ -889,13 +961,15 @@
                     self.vars_to_resolve = map(Variable, vars_to_resolve)
 
                 def render(self, context):
-                    resolved_vars = [var.resolve(context) for var in self.vars_to_resolve]
+                    args, kwdargs = resolve_all_variables_for_call(
+                        self.vars_to_resolve, context,
+                        getattr(func, "_decorated_function", func).__name__,
+                        params, defaults and len(defaults) or 0,
+                        varargs, varkw)
                     if takes_context:
-                        args = [context] + resolved_vars
-                    else:
-                        args = resolved_vars
+                        args = [context] + args
 
-                    dict = func(*args)
+                    dict = func(*args, **kwdargs)
 
                     if not getattr(self, 'nodelist', False):
                         from django.template.loader import get_template, select_template
