Django

Code

Changeset 4558

Show
Ignore:
Timestamp:
02/23/07 12:02:51 (2 years ago)
Author:
jacob
Message:

Fixed #343: filters that take strings now handle non-strings correctly. Thanks to Boffbowsh for the original patch, and to SmileyChris? for the updated patch and tests.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/template/defaultfilters.py

    r4496 r4558  
    99register = Library() 
    1010 
     11####################### 
     12# STRING DECORATOR    # 
     13####################### 
     14 
     15def smart_string(obj): 
     16    # FUTURE: Unicode strings should probably be normalized to a specific 
     17    # encoding and non-unicode strings should be converted to unicode too. 
     18#    if isinstance(obj, unicode): 
     19#        obj = obj.encode(settings.DEFAULT_CHARSET) 
     20#    else: 
     21#        obj = unicode(obj, settings.DEFAULT_CHARSET) 
     22    # FUTURE: Replace dumb string logic below with cool unicode logic above. 
     23    if not isinstance(obj, basestring): 
     24        obj = str(obj) 
     25    return obj 
     26 
     27def stringfilter(func): 
     28    """ 
     29    Decorator for filters which should only receive strings. The object passed 
     30    as the first positional argument will be converted to a string. 
     31    """ 
     32    def _dec(*args, **kwargs): 
     33        if args: 
     34            args = list(args) 
     35            args[0] = smart_string(args[0]) 
     36        return func(*args, **kwargs) 
     37         
     38    # Make sure the internal name is the original function name because this 
     39    # is the internal name of the filter if passed directly to Library().filter 
     40    _dec.__name__ = func.__name__ 
     41     
     42    # Include a reference to the real function (used to check original 
     43    # arguments by the template parser). 
     44    _dec._decorated_function = getattr(func, '_decorated_function', func) 
     45    return _dec 
     46 
    1147################### 
    1248# STRINGS         # 
     
    1753    "Adds slashes - useful for passing strings to JavaScript, for example." 
    1854    return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'") 
     55addslashes = stringfilter(addslashes) 
    1956 
    2057def capfirst(value): 
    2158    "Capitalizes the first character of the value" 
    22     value = str(value) 
    2359    return value and value[0].upper() + value[1:] 
    24  
     60capfirst = stringfilter(capfirst) 
     61  
    2562def fix_ampersands(value): 
    2663    "Replaces ampersands with ``&`` entities" 
    2764    from django.utils.html import fix_ampersands 
    2865    return fix_ampersands(value) 
     66fix_ampersands = stringfilter(fix_ampersands) 
    2967 
    3068def floatformat(text, arg=-1): 
     
    5391        d = int(arg) 
    5492    except ValueError: 
    55         return str(f) 
     93        return smart_string(f) 
    5694    m = f - int(f) 
    5795    if not m and d < 0: 
     
    70108        lines[i] = ("%0" + width  + "d. %s") % (i + 1, escape(line)) 
    71109    return '\n'.join(lines) 
     110linenumbers = stringfilter(linenumbers) 
    72111 
    73112def lower(value): 
    74113    "Converts a string into all lowercase" 
    75114    return value.lower() 
     115lower = stringfilter(lower) 
    76116 
    77117def make_list(value): 
     
    80120    digits. For a string, it's a list of characters. 
    81121    """ 
    82     return list(str(value)) 
     122    return list(value) 
     123make_list = stringfilter(make_list) 
    83124 
    84125def slugify(value): 
     
    86127    value = re.sub('[^\w\s-]', '', value).strip().lower() 
    87128    return re.sub('[-\s]+', '-', value) 
     129slugify = stringfilter(slugify) 
    88130 
    89131def stringformat(value, arg): 
     
    97139    """ 
    98140    try: 
    99         return ("%" + arg) % value 
     141        return ("%" + str(arg)) % value 
    100142    except (ValueError, TypeError): 
    101143        return "" 
     
    104146    "Converts a string into titlecase" 
    105147    return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title()) 
     148title = stringfilter(title) 
    106149 
    107150def truncatewords(value, arg): 
     
    119162        value = str(value) 
    120163    return truncate_words(value, length) 
     164truncatewords = stringfilter(truncatewords) 
    121165 
    122166def truncatewords_html(value, arg): 
     
    134178        value = str(value) 
    135179    return truncate_html_words(value, length) 
     180truncatewords_html = stringfilter(truncatewords_html) 
    136181 
    137182def upper(value): 
    138183    "Converts a string into all uppercase" 
    139184    return value.upper() 
     185upper = stringfilter(upper) 
    140186 
    141187def urlencode(value): 
     
    145191        value = str(value) 
    146192    return urllib.quote(value) 
     193urlencode = stringfilter(urlencode) 
    147194 
    148195def urlize(value): 
     
    150197    from django.utils.html import urlize 
    151198    return urlize(value, nofollow=True) 
     199urlize = stringfilter(urlize) 
    152200 
    153201def urlizetrunc(value, limit): 
     
    160208    from django.utils.html import urlize 
    161209    return urlize(value, trim_url_limit=int(limit), nofollow=True) 
     210urlizetrunc = stringfilter(urlizetrunc) 
    162211 
    163212def wordcount(value): 
    164213    "Returns the number of words" 
    165214    return len(value.split()) 
     215wordcount = stringfilter(wordcount) 
    166216 
    167217def wordwrap(value, arg): 
     
    172222    """ 
    173223    from django.utils.text import wrap 
    174     return wrap(str(value), int(arg)) 
     224    return wrap(value, int(arg)) 
     225wordwrap = stringfilter(wordwrap) 
    175226 
    176227def ljust(value, arg): 
     
    180231    Argument: field size 
    181232    """ 
    182     return str(value).ljust(int(arg)) 
     233    return value.ljust(int(arg)) 
     234ljust = stringfilter(ljust) 
    183235 
    184236def rjust(value, arg): 
     
    188240    Argument: field size 
    189241    """ 
    190     return str(value).rjust(int(arg)) 
     242    return value.rjust(int(arg)) 
     243rjust = stringfilter(rjust) 
    191244 
    192245def center(value, arg): 
    193246    "Centers the value in a field of a given width" 
    194     return str(value).center(int(arg)) 
     247    return value.center(int(arg)) 
     248center = stringfilter(center) 
    195249 
    196250def cut(value, arg): 
    197251    "Removes all values of arg from the given string" 
    198252    return value.replace(arg, '') 
     253cut = stringfilter(cut) 
    199254 
    200255################### 
     
    206261    from django.utils.html import escape 
    207262    return escape(value) 
     263escape = stringfilter(escape) 
    208264 
    209265def linebreaks(value): 
     
    211267    from django.utils.html import linebreaks 
    212268    return linebreaks(value) 
     269linebreaks = stringfilter(linebreaks) 
    213270 
    214271def linebreaksbr(value): 
    215272    "Converts newlines into <br />s" 
    216273    return value.replace('\n', '<br />') 
     274linebreaksbr = stringfilter(linebreaksbr) 
    217275 
    218276def removetags(value, tags): 
     
    225283    value = endtag_re.sub('', value) 
    226284    return value 
     285removetags = stringfilter(removetags) 
    227286 
    228287def striptags(value): 
    229288    "Strips all [X]HTML tags" 
    230289    from django.utils.html import strip_tags 
    231     if not isinstance(value, basestring): 
    232         value = str(value) 
    233290    return strip_tags(value) 
     291striptags = stringfilter(striptags) 
    234292 
    235293################### 
     
    266324    "Joins a list with a string, like Python's ``str.join(list)``" 
    267325    try: 
    268         return arg.join(map(str, value)) 
     326        return arg.join(map(smart_string, value)) 
    269327    except AttributeError: # fail silently but nicely 
    270328        return value 
  • django/trunk/django/template/__init__.py

    r4465 r4558  
    581581        provided = list(provided) 
    582582        plen = len(provided) 
     583        # Check to see if a decorator is providing the real function. 
     584        func = getattr(func, '_decorated_function', func) 
    583585        args, varargs, varkw, defaults = getargspec(func) 
    584586        # First argument is filter input. 
  • django/trunk/docs/templates_python.txt

    r4502 r4558  
    655655will use the function's name as the filter name. 
    656656 
     657Template filters which expect strings 
     658~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     659If you are writing a template filter which only expects a string as the first 
     660argument, you should use the included decorator ``to_str`` which will convert 
     661an object to it's string value before being passed to your function:: 
     662 
     663    def lower(value): 
     664        return value.lower() 
     665    lower = template.to_str(lower) 
     666 
    657667Writing custom template tags 
    658668---------------------------- 
  • django/trunk/tests/regressiontests/defaultfilters/tests.py

    r4496 r4558  
    389389'0800 3569377' 
    390390 
    391  
     391# Filters shouldn't break if passed non-strings 
     392>>> addslashes(123) 
     393'123' 
     394>>> linenumbers(123) 
     395'1. 123' 
     396>>> lower(123) 
     397'123' 
     398>>> make_list(123) 
     399['1', '2', '3'] 
     400>>> slugify(123) 
     401'123' 
     402>>> title(123) 
     403'123' 
     404>>> truncatewords(123, 2) 
     405'123' 
     406>>> upper(123) 
     407'123' 
     408>>> urlencode(123) 
     409'123' 
     410>>> urlize(123) 
     411'123' 
     412>>> urlizetrunc(123, 1) 
     413'123' 
     414>>> wordcount(123) 
     415
     416>>> wordwrap(123, 2) 
     417'123' 
     418>>> ljust('123', 4) 
     419'123 ' 
     420>>> rjust('123', 4) 
     421' 123' 
     422>>> center('123', 5) 
     423' 123 ' 
     424>>> center('123', 6) 
     425' 123  ' 
     426>>> cut(123, '2') 
     427'13' 
     428>>> escape(123) 
     429'123' 
     430>>> linebreaks(123) 
     431'<p>123</p>' 
     432>>> linebreaksbr(123) 
     433'123' 
     434>>> removetags(123, 'a') 
     435'123' 
     436>>> striptags(123) 
     437'123' 
    392438 
    393439"""