Ticket #7438: kwdarg_template_tag.patch

File kwdarg_template_tag.patch, 6.4 KB (added by dougn, 7 years ago)

initial patch with full support (but no unit tests)

  • template/__init__.py

     
    5555from django.utils.itercompat import is_iterable
    5656from django.utils.functional import curry, Promise
    5757from django.utils.text import smart_split
    58 from django.utils.encoding import smart_unicode, force_unicode
     58from django.utils.encoding import smart_str, smart_unicode, force_unicode
    5959from django.utils.translation import ugettext as _
    6060from django.utils.safestring import SafeData, EscapeData, mark_safe, mark_for_escaping
    6161from django.utils.html import escape
     62try:
     63    set
     64except NameError:
     65    from sets import Set as set   # Python 2.3 fallback
    6266
    6367__all__ = ('Template', 'Context', 'RequestContext', 'compile_string')
    6468
     
    7175FILTER_SEPARATOR = '|'
    7276FILTER_ARGUMENT_SEPARATOR = ':'
    7377VARIABLE_ATTRIBUTE_SEPARATOR = '.'
     78TAG_KEYWORD_ARGUMENT_SEPARATOR = '='
    7479BLOCK_TAG_START = '{%'
    7580BLOCK_TAG_END = '%}'
    7681VARIABLE_TAG_START = '{{'
     
    592597    """
    593598    return Variable(path).resolve(context)
    594599
     600def resolve_all_variables_for_call(variables, context, name, params, num_default,
     601                                   varargs, varkw):
     602    """
     603    resolve an iterable of Variable objects into a list of args and a dict
     604    of keyword arguments. support full python style keyword argument
     605    processing::
     606        >>> def foo(a, b, c=1, d=2):
     607        ...     pass
     608        >>> foo(1, 2)
     609        >>> foo(1, b=2)
     610        >>> foo(b=2, a=1, d=3)
     611    """
     612    args = []
     613    kwdargs = {}
     614    found_kwd = False
     615    for variable in variables:
     616        if not found_kwd:
     617            try:
     618                args.append(variable.resolve(context))
     619            except VariableDoesNotExist:
     620                if variable.var.count(TAG_KEYWORD_ARGUMENT_SEPARATOR) != 1:
     621                    raise
     622                found_kwd = True
     623        if found_kwd:
     624            try:
     625                var, path = variable.var.split(TAG_KEYWORD_ARGUMENT_SEPARATOR)
     626            except ValueError:
     627                raise TemplateSyntaxError(
     628                    "Expected keyword assignemnt, found '%s' instead" %
     629                    variable.var)
     630            if params and not varkw and name not in params:
     631                raise TemplateSyntaxError(
     632                    "%s got an unexpected keyword argument '%s'" % (name, var))
     633            if var in kwdargs:
     634                raise TemplateSyntaxError(
     635                    "got multiple values for keyword argument '%s'"%(name,var))
     636            kwdargs[smart_str(var)] = Variable(path).resolve(context)
     637    if ((len(args) > len(params) and not varargs) or
     638        ((len(args)+len(kwdargs)) > len(params) and not varkw)):
     639        raise TemplateSyntaxError(
     640                "%s takes at most %s arguments. (%s given)" % (
     641                    name, len(params), len(args) + len(kwdargs)) )
     642    if len(args) != (len(params)-num_default):
     643        if len(args)>(len(params)-num_default):
     644            # some args are kwd args (maybe multiple keyword error)
     645            if not varargs:
     646                allowed = set(params[len(args):])
     647                not_allowed = set(kwdargs) - allowed
     648                if not_allowed:
     649                    raise TemplateSyntaxError(
     650                        "%s got multiple values for keyword arguments: %s" % (
     651                            name, ", ".join(not_allowed) ))
     652        elif not varkw:
     653            # not enough required parameters error
     654            required = set(params[len(args):-num_default])
     655            missing = required - set(kwdargs)
     656            if missing:
     657                raise TemplateSyntaxError(
     658                    "%s takes at least %s non-keyword arguments (%s given)" % (
     659                        name, len(params) - num_default, len(args)))
     660    return args, kwdargs
     661
    595662class Variable(object):
    596663    """
    597664    A template variable, resolvable against a given context. The variable may be
     
    860927        return func
    861928
    862929    def simple_tag(self,func):
    863         params, xx, xxx, defaults = getargspec(func)
     930        params, varargs, varkw, defaults = getargspec(func)
    864931
    865932        class SimpleNode(Node):
    866933            def __init__(self, vars_to_resolve):
    867934                self.vars_to_resolve = map(Variable, vars_to_resolve)
    868935
    869936            def render(self, context):
    870                 resolved_vars = [var.resolve(context) for var in self.vars_to_resolve]
    871                 return func(*resolved_vars)
     937                args, kwdargs = resolve_all_variables_for_call(
     938                    self.vars_to_resolve, context,
     939                    getattr(func, "_decorated_function", func).__name__,
     940                    params, defaults and len(defaults) or 0,
     941                    varargs, varkw)
    872942
     943                return func(*args, **kwdargs)
     944
    873945        compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, SimpleNode)
    874946        compile_func.__doc__ = func.__doc__
    875947        self.tag(getattr(func, "_decorated_function", func).__name__, compile_func)
     
    877949
    878950    def inclusion_tag(self, file_name, context_class=Context, takes_context=False):
    879951        def dec(func):
    880             params, xx, xxx, defaults = getargspec(func)
     952            params, varargs, varkw, defaults = getargspec(func)
    881953            if takes_context:
    882954                if params[0] == 'context':
    883955                    params = params[1:]
     
    889961                    self.vars_to_resolve = map(Variable, vars_to_resolve)
    890962
    891963                def render(self, context):
    892                     resolved_vars = [var.resolve(context) for var in self.vars_to_resolve]
     964                    args, kwdargs = resolve_all_variables_for_call(
     965                        self.vars_to_resolve, context,
     966                        getattr(func, "_decorated_function", func).__name__,
     967                        params, defaults and len(defaults) or 0,
     968                        varargs, varkw)
    893969                    if takes_context:
    894                         args = [context] + resolved_vars
    895                     else:
    896                         args = resolved_vars
     970                        args = [context] + args
    897971
    898                     dict = func(*args)
     972                    dict = func(*args, **kwdargs)
    899973
    900974                    if not getattr(self, 'nodelist', False):
    901975                        from django.template.loader import get_template, select_template
Back to Top