Django

Code

Ticket #6587: new_template_lib_loader_12.diff

File new_template_lib_loader_12.diff, 135.7 kB (added by oyvind, 4 months ago)

new loader using imp, may need some cleaup, and decide if add_to_builtins should change, and if a import should use a list instead of the getattr solution

  • a/django/template/__init__.py

    old new  
    4949u'<html></html>' 
    5050""" 
    5151import re 
     52import imp 
    5253from inspect import getargspec 
    5354from django.conf import settings 
    5455from django.template.context import Context, RequestContext, ContextPopException 
     
    5960from django.utils.translation import ugettext as _ 
    6061from django.utils.safestring import SafeData, EscapeData, mark_safe, mark_for_escaping 
    6162from django.utils.html import escape 
     63from django.templatetags import get_templatetags_modules  
    6264 
    6365__all__ = ('Template', 'Context', 'RequestContext', 'compile_string') 
    6466 
     
    913915            return func 
    914916        return dec 
    915917 
    916 def get_library(module_name): 
    917     lib = libraries.get(module_name, None) 
     918def import_library(templatetag_module, library_name): 
     919    try: 
     920        components = templatetag_module.split('.') 
     921        mod = __import__(templatetag_module) 
     922        for comp in components[1:]: 
     923            mod = getattr(mod, comp) 
     924        imp.find_module(library_name, mod.__path__) 
     925    except ImportError, AttributeError: 
     926        return None  
     927    library_module = '%s.%s' % (templatetag_module, library_name) 
     928    components = library_module.split('.') 
     929    mod = __import__(library_module) 
     930    for comp in components[1:]: 
     931        mod = getattr(mod, comp) 
     932    try: 
     933        return mod.register 
     934    except AttributeError: 
     935        raise InvalidTemplateLibrary("Template library %s does not have a variable named 'register'" % module_name) 
     936 
     937def get_library(library_name): 
     938    lib = libraries.get(library_name, None) 
    918939    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) 
     940 
     941        """  
     942        If library is not already loaded loop over all templatetags modules to locate it. 
     943 
     944        {% load somelib %} and {% load someotherlib %} loops twice. 
     945 
     946        Subsequent loads eg. {% load somelib %} in the same thread will grab the cached 
     947        module from libraries. 
     948        """ 
     949        templatetags_modules = get_templatetags_modules() 
     950        tried_modules = [] 
     951        for module in templatetags_modules: 
     952            taglib_module = '%s.%s' % (module, library_name)  
     953            tried_modules.append(taglib_module) 
     954            lib = import_library(module, library_name) 
     955            if lib: 
     956                libraries[library_name] = lib 
     957                break 
     958        if not lib: 
     959            raise InvalidTemplateLibrary("Template library %s not found, tried %s" % (library_name, ','.join(tried_modules)))  
    928960    return lib 
    929961 
    930 def add_to_builtins(module_name): 
    931     builtins.append(get_library(module_name)) 
     962def add_to_builtins(module, library_name): 
     963    builtins.append(import_library(module, library_name)) 
    932964 
    933 add_to_builtins('django.template.defaulttags') 
    934 add_to_builtins('django.template.defaultfilters') 
     965add_to_builtins('django.templatetags', 'defaulttags') 
     966add_to_builtins('django.templatetags', 'defaultfilters') 
  • /dev/null

    old new  
    1 """Default variable filters.""" 
    2  
    3 import re 
    4 import random as random_module 
    5 try: 
    6     from functools import wraps 
    7 except ImportError: 
    8     from django.utils.functional import wraps  # Python 2.3, 2.4 fallback. 
    9  
    10 from django.template import Variable, Library 
    11 from django.conf import settings 
    12 from django.utils.translation import ugettext, ungettext 
    13 from django.utils.encoding import force_unicode, iri_to_uri 
    14 from django.utils.safestring import mark_safe, SafeData 
    15  
    16 register = Library() 
    17  
    18 ####################### 
    19 # STRING DECORATOR    # 
    20 ####################### 
    21  
    22 def 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  
    49 def 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("'", "\\'") 
    56 addslashes.is_safe = True 
    57 addslashes = stringfilter(addslashes) 
    58  
    59 def capfirst(value): 
    60     """Capitalizes the first character of the value.""" 
    61     return value and value[0].upper() + value[1:] 
    62 capfirst.is_safe=True 
    63 capfirst = 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 ) 
    77 def 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 
    82 escapejs = stringfilter(escapejs) 
    83  
    84 def fix_ampersands(value): 
    85     """Replaces ampersands with ``&amp;`` entities.""" 
    86     from django.utils.html import fix_ampersands 
    87     return fix_ampersands(value) 
    88 fix_ampersands.is_safe=True 
    89 fix_ampersands = stringfilter(fix_ampersands) 
    90  
    91 def 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) 
    136 floatformat.is_safe = True 
    137  
    138 def iriencode(value): 
    139     """Escapes an IRI value for use in a URL.""" 
    140     return force_unicode(iri_to_uri(value)) 
    141 iriencode.is_safe = True 
    142 iriencode = stringfilter(iriencode) 
    143  
    144 def 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)) 
    158 linenumbers.is_safe = True 
    159 linenumbers.needs_autoescape = True 
    160 linenumbers = stringfilter(linenumbers) 
    161  
    162 def lower(value): 
    163     """Converts a string into all lowercase.""" 
    164     return value.lower() 
    165 lower.is_safe = True 
    166 lower = stringfilter(lower) 
    167  
    168 def 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) 
    176 make_list.is_safe = False 
    177 make_list = stringfilter(make_list) 
    178  
    179 def 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)) 
    188 slugify.is_safe = True 
    189 slugify = stringfilter(slugify) 
    190  
    191 def 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"" 
    205 stringformat.is_safe = True 
    206  
    207 def title(value): 
    208     """Converts a string into titlecase.""" 
    209     return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title()) 
    210 title.is_safe = True 
    211 title = stringfilter(title) 
    212  
    213 def 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) 
    225 truncatewords.is_safe = True 
    226 truncatewords = stringfilter(truncatewords) 
    227  
    228 def 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) 
    240 truncatewords_html.is_safe = True 
    241 truncatewords_html = stringfilter(truncatewords_html) 
    242  
    243 def upper(value): 
    244     """Converts a string into all uppercase.""" 
    245     return value.upper() 
    246 upper.is_safe = False 
    247 upper = stringfilter(upper) 
    248  
    249 def urlencode(value): 
    250     """Escapes a value for use in a URL.""" 
    251     from django.utils.http import urlquote 
    252     return urlquote(value) 
    253 urlencode.is_safe = False 
    254 urlencode = stringfilter(urlencode) 
    255  
    256 def 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)) 
    260 urlize.is_safe=True 
    261 urlize.needs_autoescape = True 
    262 urlize = stringfilter(urlize) 
    263  
    264 def 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)) 
    274 urlizetrunc.is_safe = True 
    275 urlizetrunc.needs_autoescape = True 
    276 urlizetrunc = stringfilter(urlizetrunc) 
    277  
    278 def wordcount(value): 
    279     """Returns the number of words.""" 
    280     return len(value.split()) 
    281 wordcount.is_safe = False 
    282 wordcount = stringfilter(wordcount) 
    283  
    284 def 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)) 
    292 wordwrap.is_safe = True 
    293 wordwrap = stringfilter(wordwrap) 
    294  
    295 def 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)) 
    302 ljust.is_safe = True 
    303 ljust = stringfilter(ljust) 
    304  
    305 def 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)) 
    312 rjust.is_safe = True 
    313 rjust = stringfilter(rjust) 
    314  
    315 def center(value, arg): 
    316     """Centers the value in a field of a given width.""" 
    317     return value.center(int(arg)) 
    318 center.is_safe = True 
    319 center = stringfilter(center) 
    320  
    321 def 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 
    330 cut = stringfilter(cut) 
    331  
    332 ################### 
    333 # HTML STRINGS    # 
    334 ################### 
    335  
    336 def 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) 
    342 escape.is_safe = True 
    343 escape = stringfilter(escape) 
    344  
    345 def 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)) 
    353 force_escape = stringfilter(force_escape) 
    354 force_escape.is_safe = True 
    355  
    356 def 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)) 
    365 linebreaks.is_safe = True 
    366 linebreaks.needs_autoescape = True 
    367 linebreaks = stringfilter(linebreaks) 
    368  
    369 def 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 />')) 
    378 linebreaksbr.is_safe = True 
    379 linebreaksbr.needs_autoescape = True 
    380 linebreaksbr = stringfilter(linebreaksbr) 
    381  
    382 def 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) 
    388 safe.is_safe = True 
    389 safe = stringfilter(safe) 
    390  
    391 def 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 
    400 removetags.is_safe = True 
    401 removetags = stringfilter(removetags) 
    402  
    403 def striptags(value): 
    404     """Strips all [X]HTML tags.""" 
    405     from django.utils.html import strip_tags 
    406     return strip_tags(value) 
    407 striptags.is_safe = True 
    408 striptags = stringfilter(striptags) 
    409  
    410 ################### 
    411 # LISTS           # 
    412 ################### 
    413  
    414 def 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] 
    423 dictsort.is_safe = False 
    424  
    425 def 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] 
    435 dictsortreversed.is_safe = False 
    436  
    437 def first(value): 
    438     """Returns the first item in a list.""" 
    439     try: 
    440         return value[0] 
    441     except IndexError: 
    442         return u'' 
    443 first.is_safe = False 
    444  
    445 def 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 
    457 join.is_safe = True 
    458  
    459 def last(value): 
    460     "Returns the last item in a list" 
    461     try: 
    462         return value[-1] 
    463     except IndexError: 
    464         return u'' 
    465 last.is_safe = True 
    466  
    467 def length(value): 
    468     """Returns the length of the value - useful for lists.""" 
    469     return len(value) 
    470 length.is_safe = True 
    471  
    472 def length_is(value, arg): 
    473     """Returns a boolean of whether the value's length is the argument.""" 
    474     return len(value) == int(arg) 
    475 length_is.is_safe = True 
    476  
    477 def random(value): 
    478     """Returns a random item from the list.""" 
    479     return random_module.choice(value) 
    480 random.is_safe = True 
    481  
    482 def 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. 
    501 slice_.is_safe = True 
    502  
    503 def 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)) 
    584 unordered_list.is_safe = True 
    585 unordered_list.needs_autoescape = True 
    586  
    587 ################### 
    588 # INTEGERS        # 
    589 ################### 
    590  
    591 def add(value, arg): 
    592     """Adds the arg to the value.""" 
    593     return int(value) + int(arg) 
    594 add.is_safe = False 
    595  
    596 def 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 
    614 get_digit.is_safe = False 
    615  
    616 ################### 
    617 # DATES           # 
    618 ################### 
    619  
    620 def 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) 
    628 date.is_safe = False 
    629  
    630 def 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) 
    638 time.is_safe = False 
    639  
    640 def 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) 
    648 timesince.is_safe = False 
    649  
    650 def 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) 
    659 timeuntil.is_safe = False 
    660  
    661 ################### 
    662 # LOGIC           # 
    663 ################### 
    664  
    665 def default(value, arg): 
    666     """If value is unavailable, use given default.""" 
    667     return value or arg 
    668 default.is_safe = False 
    669  
    670 def default_if_none(value, arg): 
    671     """If value is None, use given default.""" 
    672     if value is None: 
    673         return arg 
    674     return value 
    675 default_if_none.is_safe = False 
    676  
    677 def divisibleby(value, arg): 
    678     """Returns True if the value is devisible by the argument.""" 
    679     return int(value) % int(arg) == 0 
    680 divisibleby.is_safe = False 
    681  
    682 def 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 
    712 yesno.is_safe = False 
    713  
    714 ################### 
    715 # MISC            # 
    716 ################### 
    717  
    718 def 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)) 
    735 filesizeformat.is_safe = True 
    736  
    737 def 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 
    779 pluralize.is_safe = False 
    780  
    781 def 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) 
    785 phone2numeric.is_safe = True 
    786  
    787 def 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") 
    794 pprint.is_safe = True 
    795  
    796 # Syntax: register.filter(name of filter, callback) 
    797 register.filter(add) 
    798 register.filter(addslashes) 
    799 register.filter(capfirst) 
    800 register.filter(center) 
    801 register.filter(cut) 
    802 register.filter(date) 
    803 register.filter(default) 
    804 register.filter(default_if_none) 
    805 register.filter(dictsort) 
    806 register.filter(dictsortreversed) 
    807 register.filter(divisibleby) 
    808 register.filter(escape) 
    809 register.filter(escapejs) 
    810 register.filter(filesizeformat) 
    811 register.filter(first) 
    812 register.filter(fix_ampersands) 
    813 register.filter(floatformat) 
    814 register.filter(force_escape) 
    815 register.filter(get_digit) 
    816 register.filter(iriencode) 
    817 register.filter(join) 
    818 register.filter(last) 
    819 register.filter(length) 
    820 register.filter(length_is) 
    821 register.filter(linebreaks) 
    822 register.filter(linebreaksbr) 
    823 register.filter(linenumbers) 
    824 register.filter(ljust) 
    825 register.filter(lower) 
    826 register.filter(make_list) 
    827 register.filter(phone2numeric) 
    828 register.filter(pluralize) 
    829 register.filter(pprint) 
    830 register.filter(removetags) 
    831 register.filter(random) 
    832 register.filter(rjust) 
    833 register.filter(safe) 
    834 register.filter('slice', slice_) 
    835 register.filter(slugify) 
    836 register.filter(stringformat) 
    837 register.filter(striptags) 
    838 register.filter(time) 
    839 register.filter(timesince) 
    840 register.filter(timeuntil) 
    841 register.filter(title) 
    842 register.filter(truncatewords) 
    843 register.filter(truncatewords_html) 
    844 register.filter(unordered_list) 
    845 register.filter(upper) 
    846 register.filter(urlencode) 
    847 register.filter(urlize) 
    848 register.filter(urlizetrunc) 
    849 register.filter(wordcount) 
    850 register.filter(wordwrap) 
    851 register.filter(yesno) 
  • /dev/null

    old new  
    1 """Default tags used by the template system, available to all templates.""" 
    2  
    3 import sys 
    4 import re 
    5 from itertools import cycle as itertools_cycle 
    6 try: 
    7     reversed 
    8 except NameError: 
    9     from django.utils.itercompat import reversed     # Python 2.3 fallback 
    10  
    11 from django.template import Node, NodeList, Template, Context, Variable 
    12 from 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 
    13 from django.template import get_library, Library, InvalidTemplateLibrary 
    14 from django.conf import settings 
    15 from django.utils.encoding import smart_str, smart_unicode 
    16 from django.utils.itercompat import groupby 
    17 from django.utils.safestring import mark_safe 
    18  
    19 register = Library() 
    20  
    21 class 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  
    36 class CommentNode(Node): 
    37     def render(self, context): 
    38         return '' 
    39  
    40 class 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  
    51 class 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  
    59 class 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  
    71 class 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  
    85 class 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 = [] 
    103         if isinstance(self, nodetype): 
    104             nodes.append(self) 
    105         nodes.extend(self.nodelist_loop.get_nodes_by_type(nodetype)) 
    106         return nodes 
    107  
    108     def render(self, context): 
    109         nodelist = NodeList() 
    110         if 'forloop' in context: 
    111             parentloop = context['forloop'] 
    112         else: 
    113             parentloop = {} 
    114         context.push() 
    115         try: 
    116             values = self.sequence.resolve(context, True) 
    117         except VariableDoesNotExist: 
    118             values = [] 
    119         if values is None: 
    120             values = [] 
    121         if not hasattr(values, '__len__'): 
    122             values = list(values) 
    123         len_values = len(values) 
    124         if self.is_reversed: 
    125             values = reversed(values) 
    126 &nb