keywords arguments parser for custom template tags

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
def parse_kw_args(tagname, bits, args_spec=None, restrict=False):
    """ keywords arguments parser for template tags

    returns a list of (argname, value) tuples
    (NB: keeps ordering and is easily turned into a dict).

    Params:
    * tagname : the name of calling tag (for error messages)
    * bits : sequence of tokens to parse as kw args
    * args_spec : (optional) dict of argname=>validator for kwargs, cf below
    * restrict : if True, only argnames in args_specs will be accepted

    If restrict=False and args_spec is None (default), this will just try
    to parse a sequence of key=val strings into a 

    About args_spec validators :
    * A validator can be either a callable, a regular expression or None.

    * If it's a callable, the callable must take the value as argument and
    return a (possibly different) value, which will become the final value
    for the argument. Any exception raised by the validator will be
    considered a rejection.

    * If it's a regexp, the value will be matched against it. A failure
    will be considered as a rejection.

    * Using None as validator only makes sense with the restrict flag set
    to True. This is useful when the only validation is on the argument
    name being expected.
    """
    
    args = []

    if restrict:
        if args_spec is None:
            raise ValueError("you must pass an args_spec dict if you want to restrict allowed args")        
        allowed = list(args_spec.keys())
        do_validate = True
    else:
        do_validate = args_spec is not None
        
    for bit in bits:
        try:
            name, val = bit.split('=')
        except ValueError:
            raise template.TemplateSyntaxError(
                "keyword arguments to '%s' tag must have 'key=value' form (got : '%s')" \
                % (tagname, bit)
                )
        
        name = str(name)        
        if do_validate:
            if restrict:
                if name in allowed:
                    # we only want each name once
                    del allowed[allowed.index(name)]
                else:
                    raise template.TemplateSyntaxError(
                        "keyword arguments to '%s' tag must be one of % (got : '%s')" \
                        % (tagname, ",".join(allowed), name)
                        )

                validate = args_spec[name]
            else: 
                validate = args_spec.get(name, None)
                
            if validate is not None:
                if callable(validate):
                    try:
                        val = validate(val)
                    except Exception, e:
                        raise template.TemplateSyntaxError(
                            "invalid optional argument '%s' for '%s' tag: '%s' (%s)" \
                            % (tagname, name, val, e)
                            )
                else:
                    # assume re
                    if re.match(validate, val) is None:
                        raise template.TemplateSyntaxError(
                            "invalid optional argument '%s' for '%s' tag: '%s' (doesn't match '%s')" \
                            % (tagname, name, val, validate)
                        )
                    
        # should be ok if we managed to get here        
        args.append((name, val))
    
    return args

More like this

  1. View to retrieve objects meeting a complex tag query by nathangeffen 3 years, 4 months ago
  2. dict recurse template tag for django by stefanp 4 years ago
  3. Convert LaTeX templates to various output formats by blizz 7 years, 1 month ago
  4. Django csrf_token Template Tag Fix by Reustle 3 years, 8 months ago
  5. UTC DateTime field by ludo 6 years, 8 months ago

Comments

bruno (on January 23, 2009):

NB : Fixed a problem with indentation (validation callback was not called when restrict==True)

#

ffsffd (on January 19, 2011):

del allowed[allowed.index(name)] should be written as allowed.remove(name)

#

(Forgotten your password?)