Django

Code

Ticket #393: str_filters.patch

File str_filters.patch, 10.1 kB (added by SmileyChris, 2 years ago)

Shiny new patch, with tests and docs

  • django/template/__init__.py

    old new  
    574574    def args_check(name, func, provided): 
    575575        provided = list(provided) 
    576576        plen = len(provided) 
     577        # Check to see if a decorator is providing the real function. 
     578        func = getattr(func, '_decorated_function', func) 
    577579        args, varargs, varkw, defaults = getargspec(func) 
    578580        # First argument is filter input. 
    579581        args.pop(0) 
  • django/template/defaultfilters.py

    old new  
    88 
    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 to_str(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    # Make sure the internal name is the original function name because this 
     38    # is the internal name of the filter if passed directly to Library().filter 
     39    _dec.__name__ = func.__name__ 
     40    # Include a reference to the real function (used to check original 
     41    # arguments by the template parser). 
     42    _dec._decorated_function = getattr(func, '_decorated_function', func) 
     43    return _dec 
     44 
    1145################### 
    1246# STRINGS         # 
    1347################### 
    1448 
    1549 
     50#@to_str 
    1651def addslashes(value): 
    1752    "Adds slashes - useful for passing strings to JavaScript, for example." 
    1853    return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'") 
     54addslashes = to_str(addslashes) 
    1955 
     56#@to_str 
    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 = to_str(capfirst) 
     61  
     62#@to_str 
    2563def fix_ampersands(value): 
    2664    "Replaces ampersands with ``&`` entities" 
    2765    from django.utils.html import fix_ampersands 
    2866    return fix_ampersands(value) 
     67fix_ampersands = to_str(fix_ampersands) 
    2968 
    3069def floatformat(text, arg=-1): 
    3170    """ 
     
    5291    try: 
    5392        d = int(arg) 
    5493    except ValueError: 
    55         return str(f) 
     94        return smart_string(f) 
    5695    m = f - int(f) 
    5796    if not m and d < 0: 
    5897        return '%d' % int(f) 
     
    6099        formatstr = '%%.%df' % abs(d) 
    61100        return formatstr % f 
    62101 
     102#@to_str 
    63103def linenumbers(value): 
    64104    "Displays text with line numbers" 
    65105    from django.utils.html import escape 
     
    69109    for i, line in enumerate(lines): 
    70110        lines[i] = ("%0" + width  + "d. %s") % (i + 1, escape(line)) 
    71111    return '\n'.join(lines) 
     112linenumbers = to_str(linenumbers) 
    72113 
     114#@to_str 
    73115def lower(value): 
    74116    "Converts a string into all lowercase" 
    75117    return value.lower() 
     118lower = to_str(lower) 
    76119 
     120#@to_str 
    77121def make_list(value): 
    78122    """ 
    79123    Returns the value turned into a list. For an integer, it's a list of 
    80124    digits. For a string, it's a list of characters. 
    81125    """ 
    82     return list(str(value)) 
     126    return list(value) 
     127make_list = to_str(make_list) 
    83128 
     129#@to_str 
    84130def slugify(value): 
    85131    "Converts to lowercase, removes non-alpha chars and converts spaces to hyphens" 
    86132    value = re.sub('[^\w\s-]', '', value).strip().lower() 
    87133    return re.sub('[-\s]+', '-', value) 
     134slugify = to_str(slugify) 
    88135 
    89136def stringformat(value, arg): 
    90137    """ 
     
    96143    of Python string formatting 
    97144    """ 
    98145    try: 
    99         return ("%" + arg) % value 
     146        return ("%" + str(arg)) % value 
    100147    except (ValueError, TypeError): 
    101148        return "" 
    102149 
     150#@to_str 
    103151def title(value): 
    104152    "Converts a string into titlecase" 
    105153    return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title()) 
     154title = to_str(title) 
    106155 
     156#@to_str 
    107157def truncatewords(value, arg): 
    108158    """ 
    109159    Truncates a string after a certain number of words 
     
    115165        length = int(arg) 
    116166    except ValueError: # invalid literal for int() 
    117167        return value # Fail silently. 
    118     if not isinstance(value, basestring): 
    119         value = str(value) 
    120168    return truncate_words(value, length) 
     169truncatewords = to_str(truncatewords) 
    121170 
     171#@to_str 
    122172def upper(value): 
    123173    "Converts a string into all uppercase" 
    124174    return value.upper() 
     175upper = to_str(upper) 
    125176 
     177#@to_str 
    126178def urlencode(value): 
    127179    "Escapes a value for use in a URL" 
    128180    import urllib 
    129181    return urllib.quote(value) 
     182urlencode = to_str(urlencode) 
    130183 
     184#@to_str 
    131185def urlize(value): 
    132186    "Converts URLs in plain text into clickable links" 
    133187    from django.utils.html import urlize 
    134188    return urlize(value, nofollow=True) 
     189urlize = to_str(urlize) 
    135190 
     191#@to_str 
    136192def urlizetrunc(value, limit): 
    137193    """ 
    138194    Converts URLs into clickable links, truncating URLs to the given character limit, 
     
    142198    """ 
    143199    from django.utils.html import urlize 
    144200    return urlize(value, trim_url_limit=int(limit), nofollow=True) 
     201urlizetrunc = to_str(urlizetrunc) 
    145202 
     203#@to_str 
    146204def wordcount(value): 
    147205    "Returns the number of words" 
    148206    return len(value.split()) 
     207wordcount = to_str(wordcount) 
    149208 
     209#@to_str 
    150210def wordwrap(value, arg): 
    151211    """ 
    152212    Wraps words at specified line length 
     
    154214    Argument: number of characters to wrap the text at. 
    155215    """ 
    156216    from django.utils.text import wrap 
    157     return wrap(str(value), int(arg)) 
     217    return wrap(value, int(arg)) 
     218wordwrap = to_str(wordwrap) 
    158219 
     220#@to_str 
    159221def ljust(value, arg): 
    160222    """ 
    161223    Left-aligns the value in a field of a given width 
    162224 
    163225    Argument: field size 
    164226    """ 
    165     return str(value).ljust(int(arg)) 
     227    return value.ljust(int(arg)) 
     228ljust = to_str(ljust) 
    166229 
     230#@to_str 
    167231def rjust(value, arg): 
    168232    """ 
    169233    Right-aligns the value in a field of a given width 
    170234 
    171235    Argument: field size 
    172236    """ 
    173     return str(value).rjust(int(arg)) 
     237    return value.rjust(int(arg)) 
     238rjust = to_str(rjust) 
    174239 
     240#@to_str 
    175241def center(value, arg): 
    176242    "Centers the value in a field of a given width" 
    177     return str(value).center(int(arg)) 
     243    return value.center(int(arg)) 
     244center = to_str(center) 
    178245 
     246#@to_str 
    179247def cut(value, arg): 
    180248    "Removes all values of arg from the given string" 
    181249    return value.replace(arg, '') 
     250cut = to_str(cut) 
    182251 
    183252################### 
    184253# HTML STRINGS    # 
    185254################### 
    186255 
     256#@to_str 
    187257def escape(value): 
    188258    "Escapes a string's HTML" 
    189259    from django.utils.html import escape 
    190260    return escape(value) 
     261escape = to_str(escape) 
    191262 
     263#@to_str 
    192264def linebreaks(value): 
    193265    "Converts newlines into <p> and <br />s" 
    194266    from django.utils.html import linebreaks 
    195267    return linebreaks(value) 
     268linebreaks = to_str(linebreaks) 
    196269 
     270#@to_str 
    197271def linebreaksbr(value): 
    198272    "Converts newlines into <br />s" 
    199273    return value.replace('\n', '<br />') 
     274linebreaksbr = to_str(linebreaksbr) 
    200275 
     276#@to_str 
    201277def removetags(value, tags): 
    202278    "Removes a space separated list of [X]HTML tags from the output" 
    203279    tags = [re.escape(tag) for tag in tags.split()] 
     
    207283    value = starttag_re.sub('', value) 
    208284    value = endtag_re.sub('', value) 
    209285    return value 
     286removetags = to_str(removetags) 
    210287 
     288#@to_str 
    211289def striptags(value): 
    212290    "Strips all [X]HTML tags" 
    213291    from django.utils.html import strip_tags 
    214     if not isinstance(value, basestring): 
    215         value = str(value) 
    216292    return strip_tags(value) 
     293striptags = to_str(striptags) 
    217294 
    218295################### 
    219296# LISTS           # 
     
    248325def join(value, arg): 
    249326    "Joins a list with a string, like Python's ``str.join(list)``" 
    250327    try: 
    251         return arg.join(map(str, value)) 
     328        return arg.join(map(smart_string, value)) 
    252329    except AttributeError: # fail silently but nicely 
    253330        return value 
    254331 
  • docs/templates_python.txt

    old new  
    654654If you leave off the ``name`` argument, as in the second example above, Django 
    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---------------------------- 
    659669 
  • tests/regressiontests/defaultfilters/tests.py

    old new  
    372372>>> phone2numeric('0800 flowers') 
    373373'0800 3569377' 
    374374 
     375# Filters shouldn't break if passed non-strings 
     376>>> addslashes(123) 
     377'123' 
     378>>> linenumbers(123) 
     379'1. 123' 
     380>>> lower(123) 
     381'123' 
     382>>> make_list(123) 
     383['1', '2', '3'] 
     384>>> slugify(123) 
     385'123' 
     386>>> title(123) 
     387'123' 
     388>>> truncatewords(123, 2) 
     389'123' 
     390>>> upper(123) 
     391'123' 
     392>>> urlencode(123) 
     393'123' 
     394>>> urlize(123) 
     395'123' 
     396>>> urlizetrunc(123, 1) 
     397'123' 
     398>>> wordcount(123) 
     3991 
     400>>> wordwrap(123, 2) 
     401'123' 
     402>>> ljust('123', 4) 
     403'123 ' 
     404>>> rjust('123', 4) 
     405' 123' 
     406>>> center('123', 5) 
     407' 123 ' 
     408>>> center('123', 6) 
     409' 123  ' 
     410>>> cut(123, '2') 
     411'13' 
     412>>> escape(123) 
     413'123' 
     414>>> linebreaks(123) 
     415'<p>123</p>' 
     416>>> linebreaksbr(123) 
     417'123' 
     418>>> removetags(123, 'a') 
     419'123' 
     420>>> striptags(123) 
     421'123' 
    375422 
    376  
    377423""" 
    378424 
    379425from django.template.defaultfilters import *