Code

Ticket #7438: kwdarg_template_tag.patch

File kwdarg_template_tag.patch, 6.4 KB (added by dougn, 6 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