Django

Code

Ticket #6587: new_template_lib_loader_10.diff

File new_template_lib_loader_10.diff, 70.0 kB (added by oyvind, 5 months ago)

fixed import to get the correct module, moved defaulttags and defaultfilters to avoid circular imports, fixes #6579

  • a/django/template/__init__.py

    old new  
    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 
     62from django.templatetags import get_templatetags_modules  
    6263 
    6364__all__ = ('Template', 'Context', 'RequestContext', 'compile_string') 
    6465 
     
    913914            return func 
    914915        return dec 
    915916 
    916 def get_library(module_name): 
    917     lib = libraries.get(module_name, None) 
     917def import_library(module_name): 
     918    try: 
     919        components = module_name.split('.') 
     920        mod = __import__(module_name) 
     921        for comp in components[1:]: 
     922            mod = getattr(mod, comp) 
     923    except ImportError, AttributeError: 
     924        return None 
     925    try: 
     926        return mod.register 
     927    except AttributeError: 
     928        raise InvalidTemplateLibrary("Template library %s does not have a variable named 'register'" % module_name) 
     929 
     930def get_library(library_name): 
     931    lib = libraries.get(library_name, None) 
    918932    if not lib: 
    919         try: 
    920             mod = __import__(module_name, {}, {}, ['']) 
    921         except ImportError, e: 
    922             raise InvalidTemplateLibrary("Could not load template library from %s, %s" % (module_name, e)) 
    923         try: 
    924             lib = mod.register 
    925             libraries[module_name] = lib 
    926         except AttributeError: 
    927             raise InvalidTemplateLibrary("Template library %s does not have a variable named 'register'" % module_name) 
     933 
     934        """  
     935        If library is not already loaded loop over all templatetags modules to locate it. 
     936 
     937        {% load somelib %} and {% load someotherlib %} loops twice. 
     938 
     939        Subsequent loads eg. {% load somelib %} in the same thread will grab the cached 
     940        module from libraries. 
     941        """ 
     942        templatetags_modules = get_templatetags_modules() 
     943        tried_modules = [] 
     944        for module in templatetags_modules: 
     945            taglib_module = '%s.%s' % (module, library_name)  
     946            tried_modules.append(taglib_module) 
     947            lib = import_library(taglib_module) 
     948            if lib: 
     949                libraries[library_name] = lib 
     950                break 
     951        if not lib: 
     952            raise InvalidTemplateLibrary("Template library %s not found, tried %s" % (library_name, ','.join(tried_modules)))  
    928953    return lib 
    929954 
    930955def add_to_builtins(module_name): 
    931     builtins.append(get_library(module_name)) 
     956    builtins.append(import_library(module_name)) 
    932957 
    933 add_to_builtins('django.template.defaulttags') 
    934 add_to_builtins('django.template.defaultfilters') 
     958add_to_builtins('django.templatetags.defaulttags') 
     959add_to_builtins('django.templatetags.defaultfilters') 
  • a/django/template/defaulttags.py

    old new  
    852852    for taglib in bits[1:]: 
    853853        # add the library to the parser 
    854854        try: 
    855             lib = get_library("django.templatetags.%s" % taglib) 
     855            lib = get_library(taglib) 
    856856            parser.add_library(lib) 
    857857        except InvalidTemplateLibrary, e: 
    858858            raise TemplateSyntaxError("'%s' is not a valid tag library: %s" % 
  • a/django/templatetags/__init__.py

    old new  
    11from django.conf import settings 
    22 
    3 for a in settings.INSTALLED_APPS: 
    4     try: 
    5         __path__.extend(__import__(a + '.templatetags', {}, {}, ['']).__path__) 
    6     except ImportError: 
    7         pass 
     3templatetags_modules= []  
     4 
     5def get_templatetags_modules(): 
     6    if not templatetags_modules: 
     7        """ Populate list once per thread. """ 
     8        for app_module in ['django'] + list(settings.INSTALLED_APPS): 
     9            try: 
     10                name = '%s.templatetags' % app_module 
     11                components = name.split('.')  
     12                mod = __import__(name) 
     13                for comp in components[1:]: 
     14                    mod = getattr(mod, comp) 
     15                templatetags_modules.append(name) 
     16            except ImportError, AttributeError: 
     17                pass 
     18    return templatetags_modules  
  • /dev/null

    old new  
     1"""Default variable filters.""" 
     2 
     3import re 
     4import random as random_module 
     5try: 
     6    from functools import wraps 
     7except ImportError: 
     8    from django.utils.functional import wraps  # Python 2.3, 2.4 fallback. 
     9 
     10from django.template import Variable, Library 
     11from django.conf import settings 
     12from django.utils.translation import ugettext, ungettext 
     13from django.utils.encoding import force_unicode, iri_to_uri 
     14from django.utils.safestring import mark_safe, SafeData 
     15 
     16register = Library() 
     17 
     18####################### 
     19# STRING DECORATOR    # 
     20####################### 
     21 
     22def stringfilter(func): 
     23    """ 
     24    Decorator for filters which should only receive unicode objects. The object 
     25    passed as the first positional argument will be converted to a unicode 
     26    object. 
     27    """ 
     28    def _dec(*args, **kwargs): 
     29        if args: 
     30            args = list(args) 
     31            args[0] = force_unicode(args[0]) 
     32            if isinstance(args[0], SafeData) and getattr(func, 'is_safe', False): 
     33                return mark_safe(func(*args, **kwargs)) 
     34        return func(*args, **kwargs) 
     35 
     36    # Include a reference to the real function (used to check original 
     37    # arguments by the template parser). 
     38    _dec._decorated_function = getattr(func, '_decorated_function', func) 
     39    for attr in ('is_safe', 'needs_autoescape'): 
     40        if hasattr(func, attr): 
     41            setattr(_dec, attr, getattr(func, attr)) 
     42    return wraps(func)(_dec) 
     43 
     44################### 
     45# STRINGS         # 
     46################### 
     47 
     48 
     49def addslashes(value): 
     50    """ 
     51    Adds slashes before quotes. Useful for escaping strings in CSV, for 
     52    example. Less useful for escaping JavaScript; use the ``escapejs`` 
     53    filter instead. 
     54    """ 
     55    return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'") 
     56addslashes.is_safe = True 
     57addslashes = stringfilter(addslashes) 
     58 
     59def capfirst(value): 
     60    """Capitalizes the first character of the value.""" 
     61    return value and value[0].upper() + value[1:] 
     62capfirst.is_safe=True 
     63capfirst = stringfilter(capfirst) 
     64 
     65_js_escapes = ( 
     66    ('\\', '\\\\'), 
     67    ('"', '\\"'), 
     68    ("'", "\\'"), 
     69    ('\n', '\\n'), 
     70    ('\r', '\\r'), 
     71    ('\b', '\\b'), 
     72    ('\f', '\\f'), 
     73    ('\t', '\\t'), 
     74    ('\v', '\\v'), 
     75    ('</', '<\\/'), 
     76) 
     77def escapejs(value): 
     78    """Backslash-escapes characters for use in JavaScript strings.""" 
     79    for bad, good in _js_escapes: 
     80        value = value.replace(bad, good) 
     81    return value 
     82escapejs = stringfilter(escapejs) 
     83 
     84def fix_ampersands(value): 
     85    """Replaces ampersands with ``&amp;`` entities.""" 
     86    from django.utils.html import fix_ampersands 
     87    return fix_ampersands(value) 
     88fix_ampersands.is_safe=True 
     89fix_ampersands = stringfilter(fix_ampersands) 
     90 
     91def floatformat(text, arg=-1): 
     92    """ 
     93    Displays a float to a specified number of decimal places. 
     94 
     95    If called without an argument, it displays the floating point number with 
     96    one decimal place -- but only if there's a decimal place to be displayed: 
     97 
     98    * num1 = 34.23234 
     99    * num2 = 34.00000 
     100    * num3 = 34.26000 
     101    * {{ num1|floatformat }} displays "34.2" 
     102    * {{ num2|floatformat }} displays "34" 
     103    * {{ num3|floatformat }} displays "34.3" 
     104 
     105    If arg is positive, it will always display exactly arg number of decimal 
     106    places: 
     107 
     108    * {{ num1|floatformat:3 }} displays "34.232" 
     109    * {{ num2|floatformat:3 }} displays "34.000" 
     110    * {{ num3|floatformat:3 }} displays "34.260" 
     111 
     112    If arg is negative, it will display arg number of decimal places -- but 
     113    only if there are places to be displayed: 
     114 
     115    * {{ num1|floatformat:"-3" }} displays "34.232" 
     116    * {{ num2|floatformat:"-3" }} displays "34" 
     117    * {{ num3|floatformat:"-3" }} displays "34.260" 
     118    """ 
     119    try: 
     120        f = float(text) 
     121    except (ValueError, TypeError): 
     122        return u'' 
     123    try: 
     124        d = int(arg) 
     125    except ValueError: 
     126        return force_unicode(f) 
     127    try: 
     128        m = f - int(f) 
     129    except OverflowError: 
     130        return force_unicode(f) 
     131    if not m and d < 0: 
     132        return mark_safe(u'%d' % int(f)) 
     133    else: 
     134        formatstr = u'%%.%df' % abs(d) 
     135        return mark_safe(formatstr % f) 
     136floatformat.is_safe = True 
     137 
     138def iriencode(value): 
     139    """Escapes an IRI value for use in a URL.""" 
     140    return force_unicode(iri_to_uri(value)) 
     141iriencode.is_safe = True 
     142iriencode = stringfilter(iriencode) 
     143 
     144def linenumbers(value, autoescape=None): 
     145    """Displays text with line numbers.""" 
     146    from django.utils.html import escape 
     147    lines = value.split(u'\n') 
     148    # Find the maximum width of the line count, for use with zero padding 
     149    # string format command 
     150    width = unicode(len(unicode(len(lines)))) 
     151    if not autoescape or isinstance(value, SafeData): 
     152        for i, line in enumerate(lines): 
     153            lines[i] = (u"%0" + width  + u"d. %s") % (i + 1, line) 
     154    else: 
     155        for i, line in enumerate(lines): 
     156            lines[i] = (u"%0" + width  + u"d. %s") % (i + 1, escape(line)) 
     157    return mark_safe(u'\n'.join(lines)) 
     158linenumbers.is_safe = True 
     159linenumbers.needs_autoescape = True 
     160linenumbers = stringfilter(linenumbers) 
     161 
     162def lower(value): 
     163    """Converts a string into all lowercase.""" 
     164    return value.lower() 
     165lower.is_safe = True 
     166lower = stringfilter(lower) 
     167 
     168def make_list(value): 
     169    """ 
     170    Returns the value turned into a list. 
     171 
     172    For an integer, it's a list of digits. 
     173    For a string, it's a list of characters. 
     174    """ 
     175    return list(value) 
     176make_list.is_safe = False 
     177make_list = stringfilter(make_list) 
     178 
     179def slugify(value): 
     180    """ 
     181    Normalizes string, converts to lowercase, removes non-alpha characters, 
     182    and converts spaces to hyphens. 
     183    """ 
     184    import unicodedata 
     185    value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore') 
     186    value = unicode(re.sub('[^\w\s-]', '', value).strip().lower()) 
     187    return mark_safe(re.sub('[-\s]+', '-', value)) 
     188slugify.is_safe = True 
     189slugify = stringfilter(slugify) 
     190 
     191def stringformat(value, arg): 
     192    """ 
     193    Formats the variable according to the arg, a string formatting specifier. 
     194 
     195    This specifier uses Python string formating syntax, with the exception that 
     196    the leading "%" is dropped. 
     197 
     198    See http://docs.python.org/lib/typesseq-strings.html for documentation 
     199    of Python string formatting 
     200    """ 
     201    try: 
     202        return (u"%" + unicode(arg)) % value 
     203    except (ValueError, TypeError): 
     204        return u"" 
     205stringformat.is_safe = True 
     206 
     207def title(value): 
     208    """Converts a string into titlecase.""" 
     209    return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title()) 
     210title.is_safe = True 
     211title = stringfilter(title) 
     212 
     213def truncatewords(value, arg): 
     214    """ 
     215    Truncates a string after a certain number of words. 
     216 
     217    Argument: Number of words to truncate after. 
     218    """ 
     219    from django.utils.text import truncate_words 
     220    try: 
     221        length = int(arg) 
     222    except ValueError: # Invalid literal for int(). 
     223        return value # Fail silently. 
     224    return truncate_words(value, length) 
     225truncatewords.is_safe = True 
     226truncatewords = stringfilter(truncatewords) 
     227 
     228def truncatewords_html(value, arg): 
     229    """ 
     230    Truncates HTML after a certain number of words. 
     231 
     232    Argument: Number of words to truncate after. 
     233    """ 
     234    from django.utils.text import truncate_html_words 
     235    try: 
     236        length = int(arg) 
     237    except ValueError: # invalid literal for int() 
     238        return value # Fail silently. 
     239    return truncate_html_words(value, length) 
     240truncatewords_html.is_safe = True 
     241truncatewords_html = stringfilter(truncatewords_html) 
     242 
     243def upper(value): 
     244    """Converts a string into all uppercase.""" 
     245    return value.upper() 
     246upper.is_safe = False 
     247upper = stringfilter(upper) 
     248 
     249def urlencode(value): 
     250    """Escapes a value for use in a URL.""" 
     251    from django.utils.http import urlquote 
     252    return urlquote(value) 
     253urlencode.is_safe = False 
     254urlencode = stringfilter(urlencode) 
     255 
     256def urlize(value, autoescape=None): 
     257    """Converts URLs in plain text into clickable links.""" 
     258    from django.utils.html import urlize 
     259    return mark_safe(urlize(value, nofollow=True, autoescape=autoescape)) 
     260urlize.is_safe=True 
     261urlize.needs_autoescape = True 
     262urlize = stringfilter(urlize) 
     263 
     264def urlizetrunc(value, limit, autoescape=None): 
     265    """ 
     266    Converts URLs into clickable links, truncating URLs to the given character 
     267    limit, and adding 'rel=nofollow' attribute to discourage spamming. 
     268 
     269    Argument: Length to truncate URLs to. 
     270    """ 
     271    from django.utils.html import urlize 
     272    return mark_safe(urlize(value, trim_url_limit=int(limit), nofollow=True, 
     273                            autoescape=autoescape)) 
     274urlizetrunc.is_safe = True 
     275urlizetrunc.needs_autoescape = True 
     276urlizetrunc = stringfilter(urlizetrunc) 
     277 
     278def wordcount(value): 
     279    """Returns the number of words.""" 
     280    return len(value.split()) 
     281wordcount.is_safe = False 
     282wordcount = stringfilter(wordcount) 
     283 
     284def wordwrap(value, arg): 
     285    """ 
     286    Wraps words at specified line length. 
     287 
     288    Argument: number of characters to wrap the text at. 
     289    """ 
     290    from django.utils.text import wrap 
     291    return wrap(value, int(arg)) 
     292wordwrap.is_safe = True 
     293wordwrap = stringfilter(wordwrap) 
     294 
     295def ljust(value, arg): 
     296    """ 
     297    Left-aligns the value in a field of a given width. 
     298 
     299    Argument: field size. 
     300    """ 
     301    return value.ljust(int(arg)) 
     302ljust.is_safe = True 
     303ljust = stringfilter(ljust) 
     304 
     305def rjust(value, arg): 
     306    """ 
     307    Right-aligns the value in a field of a given width. 
     308 
     309    Argument: field size. 
     310    """ 
     311    return value.rjust(int(arg)) 
     312rjust.is_safe = True 
     313rjust = stringfilter(rjust) 
     314 
     315def center(value, arg): 
     316    """Centers the value in a field of a given width.""" 
     317    return value.center(int(arg)) 
     318center.is_safe = True 
     319center = stringfilter(center) 
     320 
     321def cut(value, arg): 
     322    """ 
     323    Removes all values of arg from the given string. 
     324    """ 
     325    safe = isinstance(value, SafeData) 
     326    value = value.replace(arg, u'') 
     327    if safe and arg != ';': 
     328        return mark_safe(value) 
     329    return value 
     330cut = stringfilter(cut) 
     331 
     332################### 
     333# HTML STRINGS    # 
     334################### 
     335 
     336def escape(value): 
     337    """ 
     338    Marks the value as a string that should not be auto-escaped. 
     339    """ 
     340    from django.utils.safestring import mark_for_escaping 
     341    return mark_for_escaping(value) 
     342escape.is_safe = True 
     343escape = stringfilter(escape) 
     344 
     345def force_escape(value): 
     346    """ 
     347    Escapes a string's HTML. This returns a new string containing the escaped 
     348    characters (as opposed to "escape", which marks the content for later 
     349    possible escaping). 
     350    """ 
     351    from django.utils.html import escape 
     352    return mark_safe(escape(value)) 
     353force_escape = stringfilter(force_escape) 
     354force_escape.is_safe = True 
     355 
     356def linebreaks(value, autoescape=None): 
     357    """ 
     358    Replaces line breaks in plain text with appropriate HTML; a single 
     359    newline becomes an HTML line break (``<br />``) and a new line 
     360    followed by a blank line becomes a paragraph break (``</p>``). 
     361    """ 
     362    from django.utils.html import linebreaks 
     363    autoescape = autoescape and not isinstance(value, SafeData) 
     364    return mark_safe(linebreaks(value, autoescape)) 
     365linebreaks.is_safe = True 
     366linebreaks.needs_autoescape = True 
     367linebreaks = stringfilter(linebreaks) 
     368 
     369def linebreaksbr(value, autoescape=None): 
     370    """ 
     371    Converts all newlines in a piece of plain text to HTML line breaks 
     372    (``<br />``). 
     373    """ 
     374    if autoescape and not isinstance(value, SafeData): 
     375        from django.utils.html import escape 
     376        value = escape(value) 
     377    return mark_safe(value.replace('\n', '<br />')) 
     378linebreaksbr.is_safe = True 
     379linebreaksbr.needs_autoescape = True 
     380linebreaksbr = stringfilter(linebreaksbr) 
     381 
     382def safe(value): 
     383    """ 
     384    Marks the value as a string that should not be auto-escaped. 
     385    """ 
     386    from django.utils.safestring import mark_safe 
     387    return mark_safe(value) 
     388safe.is_safe = True 
     389safe = stringfilter(safe) 
     390 
     391def removetags(value, tags): 
     392    """Removes a space separated list of [X]HTML tags from the output.""" 
     393    tags = [re.escape(tag) for tag in tags.split()] 
     394    tags_re = u'(%s)' % u'|'.join(tags) 
     395    starttag_re = re.compile(ur'<%s(/?>|(\s+[^>]*>))' % tags_re, re.U) 
     396    endtag_re = re.compile(u'</%s>' % tags_re) 
     397    value = starttag_re.sub(u'', value) 
     398    value = endtag_re.sub(u'', value) 
     399    return value 
     400removetags.is_safe = True 
     401removetags = stringfilter(removetags) 
     402 
     403def striptags(value): 
     404    """Strips all [X]HTML tags.""" 
     405    from django.utils.html import strip_tags 
     406    return strip_tags(value) 
     407striptags.is_safe = True 
     408striptags = stringfilter(striptags) 
     409 
     410################### 
     411# LISTS           # 
     412################### 
     413 
     414def dictsort(value, arg): 
     415    """ 
     416    Takes a list of dicts, returns that list sorted by the property given in 
     417    the argument. 
     418    """ 
     419    var_resolve = Variable(arg).resolve 
     420    decorated = [(var_resolve(item), item) for item in value] 
     421    decorated.sort() 
     422    return [item[1] for item in decorated] 
     423dictsort.is_safe = False 
     424 
     425def dictsortreversed(value, arg): 
     426    """ 
     427    Takes a list of dicts, returns that list sorted in reverse order by the 
     428    property given in the argument. 
     429    """ 
     430    var_resolve = Variable(arg).resolve 
     431    decorated = [(var_resolve(item), item) for item in value] 
     432    decorated.sort() 
     433    decorated.reverse() 
     434    return [item[1] for item in decorated] 
     435dictsortreversed.is_safe = False 
     436 
     437def first(value): 
     438    """Returns the first item in a list.""" 
     439    try: 
     440        return value[0] 
     441    except IndexError: 
     442        return u'' 
     443first.is_safe = False 
     444 
     445def join(value, arg): 
     446    """Joins a list with a string, like Python's ``str.join(list)``.""" 
     447    try: 
     448        data = arg.join(map(force_unicode, value)) 
     449    except AttributeError: # fail silently but nicely 
     450        return value 
     451    safe_args = reduce(lambda lhs, rhs: lhs and isinstance(rhs, SafeData), 
     452            value, True) 
     453    if safe_args: 
     454        return mark_safe(data) 
     455    else: 
     456        return data 
     457join.is_safe = True 
     458 
     459def last(value): 
     460    "Returns the last item in a list" 
     461    try: 
     462        return value[-1] 
     463    except IndexError: 
     464        return u'' 
     465last.is_safe = True 
     466 
     467def length(value): 
     468    """Returns the length of the value - useful for lists.""" 
     469    return len(value) 
     470length.is_safe = True 
     471 
     472def length_is(value, arg): 
     473    """Returns a boolean of whether the value's length is the argument.""" 
     474    return len(value) == int(arg) 
     475length_is.is_safe = True 
     476 
     477def random(value): 
     478    """Returns a random item from the list.""" 
     479    return random_module.choice(value) 
     480random.is_safe = True 
     481 
     482def slice_(value, arg): 
     483    """ 
     484    Returns a slice of the list. 
     485 
     486    Uses the same syntax as Python's list slicing; see 
     487    http://diveintopython.org/native_data_types/lists.html#odbchelper.list.slice 
     488    for an introduction. 
     489    """ 
     490    try: 
     491        bits = [] 
     492        for x in arg.split(u':'): 
     493            if len(x) == 0: 
     494                bits.append(None) 
     495            else: 
     496                bits.append(int(x)) 
     497        return value[slice(*bits)] 
     498 
     499    except (ValueError, TypeError): 
     500        return value # Fail silently. 
     501slice_.is_safe = True 
     502 
     503def unordered_list(value, autoescape=None): 
     504    """ 
     505    Recursively takes a self-nested list and returns an HTML unordered list -- 
     506    WITHOUT opening and closing <ul> tags. 
     507 
     508    The list is assumed to be in the proper format. For example, if ``var`` 
     509    contains: ``['States', ['Kansas', ['Lawrence', 'Topeka'], 'Illinois']]``, 
     510    then ``{{ var|unordered_list }}`` would return:: 
     511 
     512        <li>States 
     513        <ul> 
     514                <li>Kansas 
     515                <ul> 
     516                        <li>Lawrence</li> 
     517                        <li>Topeka</li> 
     518                </ul> 
     519                </li> 
     520                <li>Illinois</li> 
     521        </ul> 
     522        </li> 
     523    """ 
     524    if autoescape: 
     525        from django.utils.html import conditional_escape 
     526        escaper = conditional_escape 
     527    else: 
     528        escaper = lambda x: x 
     529    def convert_old_style_list(list_): 
     530        """ 
     531        Converts old style lists to the new easier to understand format. 
     532 
     533        The old list format looked like: 
     534            ['Item 1', [['Item 1.1', []], ['Item 1.2', []]] 
     535 
     536        And it is converted to: 
     537            ['Item 1', ['Item 1.1', 'Item 1.2]] 
     538        """ 
     539        if not isinstance(list_, (tuple, list)) or len(list_) != 2: 
     540            return list_, False 
     541        first_item, second_item = list_ 
     542        if second_item == []: 
     543            return [first_item], True 
     544        old_style_list = True 
     545        new_second_item = [] 
     546        for sublist in second_item: 
     547            item, old_style_list = convert_old_style_list(sublist) 
     548            if not old_style_list: 
     549                break 
     550            new_second_item.extend(item) 
     551        if old_style_list: 
     552            second_item = new_second_item 
     553        return [first_item, second_item], old_style_list 
     554    def _helper(list_, tabs=1): 
     555        indent = u'\t' * tabs 
     556        output = [] 
     557 
     558        list_length = len(list_) 
     559        i = 0 
     560        while i < list_length: 
     561            title = list_[i] 
     562            sublist = '' 
     563            sublist_item = None 
     564            if isinstance(title, (list, tuple)): 
     565                sublist_item = title 
     566                title = '' 
     567            elif i < list_length - 1: 
     568                next_item = list_[i+1] 
     569                if next_item and isinstance(next_item, (list, tuple)): 
     570                    # The next item is a sub-list. 
     571                    sublist_item = next_item 
     572                    # We've processed the next item now too. 
     573                    i += 1 
     574            if sublist_item: 
     575                sublist = _helper(sublist_item, tabs+1) 
     576                sublist = '\n%s<ul>\n%s\n%s</ul>\n%s' % (indent, sublist, 
     577                                                         indent, indent) 
     578            output.append('%s<li>%s%s</li>' % (indent, 
     579                    escaper(force_unicode(title)), sublist)) 
     580            i += 1 
     581        return '\n'.join(output) 
     582    value, converted = convert_old_style_list(value) 
     583    return mark_safe(_helper(value)) 
     584unordered_list.is_safe = True 
     585unordered_list.needs_autoescape = True 
     586 
     587################### 
     588# INTEGERS        # 
     589################### 
     590 
     591def add(value, arg): 
     592    """Adds the arg to the value.""" 
     593    return int(value) + int(arg) 
     594add.is_safe = False 
     595 
     596def get_digit(value, arg): 
     597    """ 
     598    Given a whole number, returns the requested digit of it, where 1 is the 
     599    right-most digit, 2 is the second-right-most digit, etc. Returns the 
     600    original value for invalid input (if input or argument is not an integer, 
     601    or if argument is less than 1). Otherwise, output is always an integer. 
     602    """ 
     603    try: 
     604        arg = int(arg) 
     605        value = int(value) 
     606    except ValueError: 
     607        return value # Fail silently for an invalid argument 
     608    if arg < 1: 
     609        return value 
     610    try: 
     611        return int(str(value)[-arg]) 
     612    except IndexError: 
     613        return 0 
     614get_digit.is_safe = False 
     615 
     616################### 
     617# DATES           # 
     618################### 
     619 
     620def date(value, arg=None): 
     621    """Formats a date according to the given format.""" 
     622    from django.utils.dateformat import format 
     623    if not value: 
     624        return u'' 
     625    if arg is None: 
     626        arg = settings.DATE_FORMAT 
     627    return format(value, arg) 
     628date.is_safe = False 
     629 
     630def time(value, arg=None): 
     631    """Formats a time according to the given format.""" 
     632    from django.utils.dateformat import time_format 
     633    if value in (None, u''): 
     634        return u'' 
     635    if arg is None: 
     636        arg = settings.TIME_FORMAT 
     637    return time_format(value, arg) 
     638time.is_safe = False 
     639 
     640def timesince(value, arg=None): 
     641    """Formats a date as the time since that date (i.e. "4 days, 6 hours").""" 
     642    from django.utils.timesince import timesince 
     643    if not value: 
     644        return u'' 
     645    if arg: 
     646        return timesince(arg, value) 
     647    return timesince(value) 
     648timesince.is_safe = False 
     649 
     650def timeuntil(value, arg=None): 
     651    """Formats a date as the time until that date (i.e. "4 days, 6 hours").""" 
     652    from django.utils.timesince import timesince 
     653    from datetime import datetime 
     654    if not value: 
     655        return u'' 
     656    if arg: 
     657        return timesince(arg, value) 
     658    return timesince(datetime.now(), value) 
     659timeuntil.is_safe = False 
     660 
     661################### 
     662# LOGIC           # 
     663################### 
     664 
     665def default(value, arg): 
     666    """If value is unavailable, use given default.""" 
     667    return value or arg 
     668default.is_safe = False 
     669 
     670def default_if_none(value, arg): 
     671    """If value is None, use given default.""" 
     672    if value is None: 
     673        return arg 
     674    return value 
     675default_if_none.is_safe = False 
     676 
     677def divisibleby(value, arg): 
     678    """Returns True if the value is devisible by the argument.""" 
     679    return int(value) % int(arg) == 0 
     680divisibleby.is_safe = False 
     681 
     682def yesno(value, arg=None): 
     683    """ 
     684    Given a string mapping values for true, false and (optionally) None, 
     685    returns one of those strings accoding to the value: 
     686 
     687    ==========  ======================  ================================== 
     688    Value       Argument                Outputs 
     689    ==========  ======================  ================================== 
     690    ``True``    ``"yeah,no,maybe"``     ``yeah`` 
     691    ``False``   ``"yeah,no,maybe"``     ``no`` 
     692    ``None``    ``"yeah,no,maybe"``     ``maybe`` 
     693    ``None``    ``"yeah,no"``           ``"no"`` (converts None to False 
     694                                        if no mapping for None is given. 
     695    ==========  ======================  ================================== 
     696    """ 
     697    if arg is None: 
     698        arg = ugettext('yes,no,maybe') 
     699    bits = arg.split(u',') 
     700    if len(bits) < 2: 
     701        return value # Invalid arg. 
     702    try: 
     703        yes, no, maybe = bits 
     704    except ValueError: 
     705        # Unpack list of wrong size (no "maybe" value provided). 
     706        yes, no, maybe = bits[0], bits[1], bits[1] 
     707    if value is None: 
     708        return maybe 
     709    if value: 
     710        return yes 
     711    return no 
     712yesno.is_safe = False 
     713 
     714################### 
     715# MISC            # 
     716################### 
     717 
     718def filesizeformat(bytes): 
     719    """ 
     720    Formats the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB, 
     721    102 bytes, etc). 
     722    """ 
     723    try: 
     724        bytes = float(bytes) 
     725    except TypeError: 
     726        return u"0 bytes" 
     727 
     728    if bytes < 1024: 
     729        return ungettext("%(size)d byte", "%(size)d bytes", bytes) % {'size': bytes} 
     730    if bytes < 1024 * 1024: 
     731        return ugettext("%.1f KB") % (bytes / 1024) 
     732    if bytes < 1024 * 1024 * 1024: 
     733        return ugettext("%.1f MB") % (bytes / (1024 * 1024)) 
     734    return ugettext("%.1f GB") % (bytes / (1024 * 1024 * 1024)) 
     735filesizeformat.is_safe = True 
     736 
     737def pluralize(value, arg=u's'): 
     738    """ 
     739    Returns a plural suffix if the value is not 1. By default, 's' is used as 
     740    the suffix: 
     741 
     742    * If value is 0, vote{{ value|pluralize }} displays "0 votes". 
     743    * If value is 1, vote{{ value|pluralize }} displays "1 vote". 
     744    * If value is 2, vote{{ value|pluralize }} displays "2 votes". 
     745 
     746    If an argument is provided, that string is used instead: 
     747 
     748    * If value is 0, class{{ value|pluralize:"es" }} displays "0 classes". 
     749    * If value is 1, class{{ value|pluralize:"es" }} displays "1 class". 
     750    * If value is 2, class{{ value|pluralize:"es" }} displays "2 classes". 
     751 
     752    If the provided argument contains a comma, the text before the comma is 
     753    used for the singular case and the text after the comma is used for the 
     754    plural case: 
     755 
     756    * If value is 0, cand{{ value|pluralize:"y,ies" }} displays "0 candies". 
     757    * If value is 1, cand{{ value|pluralize:"y,ies" }} displays "1 candy". 
     758    * If value is 2, cand{{ value|pluralize:"y,ies" }} displays "2 candies". 
     759    """ 
     760    if not u',' in arg: 
     761        arg = u',' + arg 
     762    bits = arg.split(u',') 
     763    if len(bits) > 2: 
     764        return u'' 
     765    singular_suffix, plural_suffix = bits[:2] 
     766 
     767    try: 
     768        if int(value) != 1: 
     769            return plural_suffix 
     770    except ValueError: # Invalid string that's not a number. 
     771        pass 
     772    except TypeError: # Value isn't a string or a number; maybe it's a list? 
     773        try: 
     774            if len(value) != 1: 
     775                return plural_suffix 
     776        except TypeError: # len() of unsized object. 
     777            pass 
     778    return singular_suffix 
     779pluralize.is_safe = False 
     780 
     781def phone2numeric(value): 
     782    """Takes a phone number and converts it in to its numerical equivalent.""" 
     783    from django.utils.text import phone2numeric 
     784    return phone2numeric(value) 
     785phone2numeric.is_safe = True 
     786 
     787def pprint(value): 
     788    """A wrapper around pprint.pprint -- for debugging, really.""" 
     789    from pprint import pformat 
     790    try: 
     791        return pformat(value) 
     792    except Exception, e: 
     793        return u"Error in formatting: %s" % force_unicode(e, errors="replace") 
     794pprint.is_safe = True 
     795 
     796# Syntax: register.filter(name of filter, callback) 
     797register.filter(add) 
     798register.filter(addslashes) 
     799register.filter(capfirst) 
     800register.filter(center) 
     801register.filter(cut) 
     802register.filter(date) 
     803register.filter(default) 
     804register.filter(default_if_none) 
     805register.filter(dictsort) 
     806register.filter(dictsortreversed) 
     807register.filter(divisibleby) 
     808register.filter(escape) 
     809register.filter(escapejs) 
     810register.filter(filesizeformat) 
     811register.filter(first) 
     812register.filter(fix_ampersands) 
     813register.filter(floatformat) 
     814register.filter(force_escape) 
     815register.filter(get_digit) 
     816register.filter(iriencode) 
     817register.filter(join) 
     818register.filter(last) 
     819register.filter(length) 
     820register.filter(length_is) 
     821register.filter(linebreaks) 
     822register.filter(linebreaksbr) 
     823register.filter(linenumbers) 
     824register.filter(ljust) 
     825register.filter(lower) 
     826register.filter(make_list) 
     827register.filter(phone2numeric) 
     828register.filter(pluralize) 
     829register.filter(pprint) 
     830register.filter(removetags) 
     831register.filter(random) 
     832register.filter(rjust) 
     833register.filter(safe) 
     834register.filter('slice', slice_) 
     835register.filter(slugify) 
     836register.filter(stringformat) 
     837register.filter(striptags) 
     838register.filter(time) 
     839register.filter(timesince) 
     840register.filter(timeuntil) 
     841register.filter(title) 
     842register.filter(truncatewords) 
     843register.filter(truncatewords_html) 
     844register.filter(unordered_list) 
     845register.filter(upper) 
     846register.filter(urlencode) 
     847register.filter(urlize) 
     848register.filter(urlizetrunc) 
     849register.filter(wordcount) 
     850register.filter(wordwrap) 
     851register.filter(yesno) 
  • /dev/null

    old new  
     1"""Default tags used by the template system, available to all templates.""" 
     2 
     3import sys 
     4import re 
     5from itertools import cycle as itertools_cycle 
     6try: 
     7    reversed 
     8except NameError: 
     9    from django.utils.itercompat import reversed     # Python 2.3 fallback 
     10 
     11from django.template import Node, NodeList, Template, Context, Variable 
     12from django.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, SINGLE_BRACE_START, SINGLE_BRACE_END, COMMENT_TAG_START, COMMENT_TAG_END 
     13from django.template import get_library, Library, InvalidTemplateLibrary 
     14from django.conf import settings 
     15from django.utils.encoding import smart_str, smart_unicode 
     16from django.utils.itercompat import groupby 
     17from django.utils.safestring import mark_safe 
     18 
     19register = Library() 
     20 
     21class AutoEscapeControlNode(Node): 
     22    """Implements the actions of the autoescape tag.""" 
     23    def __init__(self, setting, nodelist): 
     24        self.setting, self.nodelist = setting, nodelist 
     25 
     26    def render(self, context): 
     27        old_setting = context.autoescape 
     28        context.autoescape = self.setting 
     29        output = self.nodelist.render(context) 
     30        context.autoescape = old_setting 
     31        if self.setting: 
     32            return mark_safe(output) 
     33        else: 
     34            return output 
     35 
     36class CommentNode(Node): 
     37    def render(self, context): 
     38        return '' 
     39 
     40class CycleNode(Node): 
     41    def __init__(self, cyclevars, variable_name=None): 
     42        self.cycle_iter = itertools_cycle([Variable(v) for v in cyclevars]) 
     43        self.variable_name = variable_name 
     44 
     45    def render(self, context): 
     46        value = self.cycle_iter.next().resolve(context) 
     47        if self.variable_name: 
     48            context[self.variable_name] = value 
     49        return value 
     50 
     51class DebugNode(Node): 
     52    def render(self, context): 
     53        from pprint import pformat 
     54        output = [pformat(val) for val in context] 
     55        output.append('\n\n') 
     56        output.append(pformat(sys.modules)) 
     57        return ''.join(output) 
     58 
     59class FilterNode(Node): 
     60    def __init__(self, filter_expr, nodelist): 
     61        self.filter_expr, self.nodelist = filter_expr, nodelist 
     62 
     63    def render(self, context): 
     64        output = self.nodelist.render(context) 
     65        # Apply filters. 
     66        context.update({'var': output}) 
     67        filtered = self.filter_expr.resolve(context) 
     68        context.pop() 
     69        return filtered 
     70 
     71class FirstOfNode(Node): 
     72    def __init__(self, vars): 
     73        self.vars = map(Variable, vars) 
     74 
     75    def render(self, context): 
     76        for var in self.vars: 
     77            try: 
     78                value = var.resolve(context) 
     79            except VariableDoesNotExist: 
     80                continue 
     81            if value: 
     82                return smart_unicode(value) 
     83        return u'' 
     84 
     85class ForNode(Node): 
     86    def __init__(self, loopvars, sequence, is_reversed, nodelist_loop): 
     87        self.loopvars, self.sequence = loopvars, sequence 
     88        self.is_reversed = is_reversed 
     89        self.nodelist_loop = nodelist_loop 
     90 
     91    def __repr__(self): 
     92        reversed_text = self.is_reversed and ' reversed' or '' 
     93        return "<For Node: for %s in %s, tail_len: %d%s>" % \ 
     94            (', '.join(self.loopvars), self.sequence, len(self.nodelist_loop), 
     95             reversed_text) 
     96 
     97    def __iter__(self): 
     98        for node in self.nodelist_loop: 
     99            yield node 
     100 
     101    def get_nodes_by_type(self, nodetype): 
     102        nodes = []