Ticket #911: django-template-scoping.diff

File django-template-scoping.diff, 90.7 KB (added by rjwittams, 18 years ago)
  • django/contrib/markup/templatetags/markup.py

    === django/contrib/markup/templatetags/markup.py
    ==================================================================
     
    1616
    1717from django.core import template
    1818
    19 def textile(value, _):
     19register = template.Library()
     20
     21def textile(value):
    2022    try:
    2123        import textile
    2224    except ImportError:
     
    2426    else:
    2527        return textile.textile(value)
    2628       
    27 def markdown(value, _):
     29def markdown(value):
    2830    try:
    2931        import markdown
    3032    except ImportError:
     
    3234    else:
    3335        return markdown.markdown(value)
    3436       
    35 def restructuredtext(value, _):
     37def restructuredtext(value):
    3638    try:
    3739        from docutils.core import publish_parts
    3840    except ImportError:
     
    4143        parts = publish_parts(source=value, writer_name="html4css1")
    4244        return parts["fragment"]
    4345       
    44 template.register_filter("textile", textile, False)
    45 template.register_filter("markdown", markdown, False)
    46 template.register_filter("restructuredtext", restructuredtext, False)
    47  No newline at end of file
     46register.filter(textile)
     47register.filter(markdown)
     48register.filter(restructuredtext)
     49 No newline at end of file
  • django/contrib/comments/templatetags/comments.py

    === django/contrib/comments/templatetags/comments.py
    ==================================================================
     
    66from django.models.core import contenttypes
    77import re
    88
     9register = template.Library()
     10
    911COMMENT_FORM = '''
    1012{% load i18n %}
    1113{% if display_form %}
     
    360362        return CommentListNode(package, module, var_name, obj_id, tokens[5], self.free, ordering)
    361363
    362364# registration comments
    363 template.register_tag('get_comment_list', DoGetCommentList(False))
    364 template.register_tag('comment_form', DoCommentForm(False))
    365 template.register_tag('get_comment_count', DoCommentCount(False))
     365register.tag('get_comment_list',DoGetCommentList(False))
     366register.tag('comment_form', DoCommentForm(False))
     367register.tag('get_comment_count',DoCommentCount(False))
    366368# free comments
    367 template.register_tag('get_free_comment_list', DoGetCommentList(True))
    368 template.register_tag('free_comment_form', DoCommentForm(True))
    369 template.register_tag('get_free_comment_count', DoCommentCount(True))
     369register.tag('get_free_comment_list', DoGetCommentList(True))
     370register.tag('free_comment_form', DoCommentForm(True))
     371register.tag('get_free_comment_count', DoCommentCount(True))
  • django/contrib/admin/templatetags/adminmedia.py

    === django/contrib/admin/templatetags/adminmedia.py
    ==================================================================
     
    1 from django.core.template.decorators import simple_tag
     1from django.core.template import Library
     2register = Library()
    23
    34def admin_media_prefix():
    45    try:
     
    67    except ImportError:
    78        return ''
    89    return ADMIN_MEDIA_PREFIX
    9 admin_media_prefix = simple_tag(admin_media_prefix)
     10admin_media_prefix = register.simple_tag(admin_media_prefix)
     11 No newline at end of file
  • django/contrib/admin/templatetags/admin_modify.py

    === django/contrib/admin/templatetags/admin_modify.py
    ==================================================================
     
    22from django.utils.html import escape
    33from django.utils.text import capfirst
    44from django.utils.functional import curry
    5 from django.core.template.decorators import simple_tag, inclusion_tag
    65from django.contrib.admin.views.main import AdminBoundField
    76from django.core.meta.fields import BoundField, Field
    87from django.core.meta import BoundRelatedObject, TABULAR, STACKED
    98from django.conf.settings import ADMIN_MEDIA_PREFIX
    109import re
    1110
     11register = template.Library()
     12
    1213word_re = re.compile('[A-Z][a-z]+')
    1314
    1415def class_name_to_underscored(name):
    1516    return '_'.join([s.lower() for s in word_re.findall(name)[:-1]])
    1617
    17 #@simple_tag
     18#@register.simple_tag
    1819def include_admin_script(script_path):
    1920    return '<script type="text/javascript" src="%s%s"></script>' % (ADMIN_MEDIA_PREFIX, script_path)
    20 include_admin_script = simple_tag(include_admin_script)
     21include_admin_script = register.simple_tag(include_admin_script)
    2122
    22 #@inclusion_tag('admin/submit_line', takes_context=True)
     23#@register.inclusion_tag('admin/submit_line', takes_context=True)
    2324def submit_row(context, bound_manipulator):
    2425    change = context['change']
    2526    add = context['add']
     
    3637        'show_save_and_continue': not is_popup,
    3738        'show_save': True
    3839    }
    39 submit_row = inclusion_tag('admin/submit_line', takes_context=True)(submit_row)
     40submit_row = register.inclusion_tag('admin/submit_line', takes_context=True)(submit_row)
    4041
    41 #@simple_tag
     42#@register.simple_tag
    4243def field_label(bound_field):
    4344    class_names = []
    4445    if isinstance(bound_field.field, meta.BooleanField):
     
    5354    class_str = class_names and ' class="%s"' % ' '.join(class_names) or ''
    5455    return '<label for="%s"%s>%s%s</label> ' % (bound_field.element_id, class_str, \
    5556        capfirst(bound_field.field.verbose_name), colon)
    56 field_label = simple_tag(field_label)
     57field_label = register.simple_tag(field_label)
    5758
    5859class FieldWidgetNode(template.Node):
    5960    nodelists = {}
     
    170171        context.pop()
    171172        return output
    172173
    173 #@simple_tag
     174#@register.simple_tag
    174175def output_all(form_fields):
    175176    return ''.join([str(f) for f in form_fields])
    176 output_all = simple_tag(output_all)
     177output_all = register.simple_tag(output_all)
    177178
    178 #@simple_tag
     179#@register.simple_tag
    179180def auto_populated_field_script(auto_pop_fields, change = False):
    180181    for field in auto_pop_fields:
    181182        t = []
     
    191192                     ' if(!e._changed) { e.value = URLify(%s, %s);} }; ' % (
    192193                     f, field.name, add_values, field.maxlength))
    193194    return ''.join(t)
    194 auto_populated_field_script = simple_tag(auto_populated_field_script)
     195auto_populated_field_script = register.simple_tag(auto_populated_field_script)
    195196
    196 #@simple_tag
     197#@register.simple_tag
    197198def filter_interface_script_maybe(bound_field):
    198199    f = bound_field.field
    199200    if f.rel and isinstance(f.rel, meta.ManyToMany) and f.rel.filter_interface:
     
    202203              f.name, f.verbose_name, f.rel.filter_interface-1, ADMIN_MEDIA_PREFIX)
    203204    else:
    204205        return ''
    205 filter_interface_script_maybe = simple_tag(filter_interface_script_maybe)
     206filter_interface_script_maybe = register.simple_tag(filter_interface_script_maybe)
    206207
    207208def do_one_arg_tag(node_factory, parser,token):
    208209    tokens = token.contents.split()
     
    213214def register_one_arg_tag(node):
    214215    tag_name = class_name_to_underscored(node.__name__)
    215216    parse_func = curry(do_one_arg_tag, node)
    216     template.register_tag(tag_name, parse_func)
     217    register.tag(tag_name, parse_func)
    217218
    218219one_arg_tag_nodes = (
    219220    FieldWidgetNode,
     
    223224for node in one_arg_tag_nodes:
    224225    register_one_arg_tag(node)
    225226
    226 #@inclusion_tag('admin/field_line', takes_context=True)
     227#@register.inclusion_tag('admin/field_line', takes_context=True)
    227228def admin_field_line(context, argument_val):
    228229    if (isinstance(argument_val, BoundField)):
    229230        bound_fields = [argument_val]
     
    249250        'bound_fields':  bound_fields,
    250251        'class_names': " ".join(class_names),
    251252    }
    252 admin_field_line = inclusion_tag('admin/field_line', takes_context=True)(admin_field_line)
     253admin_field_line = register.inclusion_tag('admin/field_line', takes_context=True)(admin_field_line)
    253254
    254 #@simple_tag
     255#@register.simple_tag
    255256def object_pk(bound_manip, ordered_obj):
    256257    return bound_manip.get_ordered_object_pk(ordered_obj)
    257258
    258 object_pk = simple_tag(object_pk)
     259object_pk = register.simple_tag(object_pk)
  • django/contrib/admin/templatetags/log.py

    === django/contrib/admin/templatetags/log.py
    ==================================================================
     
    11from django.models.admin import log
    22from django.core import template
    33
     4register = template.Library()
     5
    46class AdminLogNode(template.Node):
    57    def __init__(self, limit, varname, user):
    68        self.limit, self.varname, self.user = limit, varname, user
     
    4850                raise template.TemplateSyntaxError, "Fourth argument in '%s' must be 'for_user'" % self.tag_name
    4951        return AdminLogNode(limit=tokens[1], varname=tokens[3], user=(len(tokens) > 5 and tokens[5] or None))
    5052
    51 template.register_tag('get_admin_log', DoGetAdminLog('get_admin_log'))
     53register.tag('get_admin_log', DoGetAdminLog('get_admin_log'))
  • django/contrib/admin/templatetags/admin_list.py

    === django/contrib/admin/templatetags/admin_list.py
    ==================================================================
     
    33from django.contrib.admin.views.main import IS_POPUP_VAR, EMPTY_CHANGELIST_VALUE, MONTHS
    44from django.core import meta, template
    55from django.core.exceptions import ObjectDoesNotExist
    6 from django.core.template.decorators import simple_tag, inclusion_tag
    76from django.utils import dateformat
    87from django.utils.html import strip_tags, escape
    98from django.utils.text import capfirst
    109from django.utils.translation import get_date_formats
    1110from django.conf.settings import ADMIN_MEDIA_PREFIX
     11from django.core.template import Library
    1212
     13register = Library()
     14
    1315DOT = '.'
    1416
    15 #@simple_tag
     17#@register.simple_tag
    1618def paginator_number(cl,i):
    1719    if i == DOT:
    1820       return '... '
     
    2022       return '<span class="this-page">%d</span> ' % (i+1)
    2123    else:
    2224       return '<a href="%s"%s>%d</a> ' % (cl.get_query_string({PAGE_VAR: i}), (i == cl.paginator.pages-1 and ' class="end"' or ''), i+1)
    23 paginator_number = simple_tag(paginator_number)
     25paginator_number = register.simple_tag(paginator_number)
    2426
    25 #@inclusion_tag('admin/pagination')
     27#@register.inclusion_tag('admin/pagination')
    2628def pagination(cl):
    2729    paginator, page_num = cl.paginator, cl.page_num
    2830
     
    6466        'ALL_VAR': ALL_VAR,
    6567        '1': 1,
    6668    }
    67 pagination = inclusion_tag('admin/pagination')(pagination)
     69pagination = register.inclusion_tag('admin/pagination')(pagination)
    6870
    6971def result_headers(cl):
    7072    lookup_opts = cl.lookup_opts
     
    177179    for res in cl.result_list:
    178180        yield list(items_for_result(cl,res))
    179181
    180 #@inclusion_tag("admin/change_list_results")
     182#@register.inclusion_tag("admin/change_list_results")
    181183def result_list(cl):
    182184    res = list(results(cl))
    183185    return {'cl': cl,
    184186            'result_headers': list(result_headers(cl)),
    185187            'results': list(results(cl))}
    186 result_list = inclusion_tag("admin/change_list_results")(result_list)
     188result_list = register.inclusion_tag("admin/change_list_results")(result_list)
    187189
    188 #@inclusion_tag("admin/date_hierarchy")
     190#@register.inclusion_tag("admin/date_hierarchy")
    189191def date_hierarchy(cl):
    190192    lookup_opts, params, lookup_params, lookup_mod = \
    191193      cl.lookup_opts, cl.params, cl.lookup_params, cl.lookup_mod
     
    256258                    'title': year.year
    257259                } for year in years ]
    258260            }
    259 date_hierarchy = inclusion_tag('admin/date_hierarchy')(date_hierarchy)
     261date_hierarchy = register.inclusion_tag('admin/date_hierarchy')(date_hierarchy)
    260262
    261 #@inclusion_tag('admin/search_form')
     263#@register.inclusion_tag('admin/search_form')
    262264def search_form(cl):
    263265    return {
    264266        'cl': cl,
    265267        'show_result_count': cl.result_count != cl.full_result_count and not cl.opts.one_to_one_field,
    266268        'search_var': SEARCH_VAR
    267269    }
    268 search_form = inclusion_tag('admin/search_form')(search_form)
     270search_form = register.inclusion_tag('admin/search_form')(search_form)
    269271
    270 #@inclusion_tag('admin/filter')
     272#@register.inclusion_tag('admin/filter')
    271273def filter(cl, spec):
    272274    return {'title': spec.title(), 'choices' : list(spec.choices(cl))}
    273 filter = inclusion_tag('admin/filter')(filter)
     275filter = register.inclusion_tag('admin/filter')(filter)
    274276
    275 #@inclusion_tag('admin/filters')
     277#@register.inclusion_tag('admin/filters')
    276278def filters(cl):
    277279    return {'cl': cl}
    278 filters = inclusion_tag('admin/filters')(filters)
     280filters = register.inclusion_tag('admin/filters')(filters)
  • django/contrib/admin/templatetags/adminapplist.py

    === django/contrib/admin/templatetags/adminapplist.py
    ==================================================================
     
    11from django.core import template
    22
     3register = template.Library()
     4
    35class AdminApplistNode(template.Node):
    46    def __init__(self, varname):
    57        self.varname = varname
     
    5456        raise template.TemplateSyntaxError, "First argument to '%s' tag must be 'as'" % tokens[0]
    5557    return AdminApplistNode(tokens[2])
    5658
    57 template.register_tag('get_admin_app_list', get_admin_app_list)
     59register.tag('get_admin_app_list', get_admin_app_list)
  • django/contrib/admin/views/template.py

    === django/contrib/admin/views/template.py
    ==================================================================
     
    5555            node = loader.do_extends(parser, token)
    5656            node.template_dirs = settings_module.TEMPLATE_DIRS
    5757            return node
    58         template.register_tag('extends', new_do_extends)
     58        register = template.Library()
     59        register.tag('extends', new_do_extends)
     60        template.builtins.append(register)
    5961
    6062        # now validate the template using the new template dirs
    6163        # making sure to reset the extends function in any case
     
    6567            tmpl.render(template.Context({}))
    6668        except template.TemplateSyntaxError, e:
    6769            error = e
    68         template.register_tag('extends', loader.do_extends)
     70        template.builtins.remove(register)
    6971        if error:
    7072            raise validators.ValidationError, e.args
  • django/contrib/admin/templates/widget/file.html

    === django/contrib/admin/templates/widget/file.html
    ==================================================================
     
    1 {% if bound_field.original_value %}
     1{% load admin_modify %}{% if bound_field.original_value %}
    22Currently: <a href="{{ bound_field.original_url }}" > {{ bound_field.original_value }} </a><br />
    33Change: {% output_all bound_field.form_fields %}
    44{% else %} {% output_all bound_field.form_fields %} {% endif %}
  • django/contrib/admin/templates/widget/default.html

    === django/contrib/admin/templates/widget/default.html
    ==================================================================
     
    1 {% output_all bound_field.form_fields %}
     1{% load admin_modify %}{% output_all bound_field.form_fields %}
  • django/contrib/admin/templates/widget/foreign.html

    === django/contrib/admin/templates/widget/foreign.html
    ==================================================================
     
     1{% load admin_modify adminmedia %}
    12{% output_all bound_field.form_fields %}
    23{% if bound_field.raw_id_admin %}
    34            <a href="../../../{{ bound_field.field.rel.to.app_label }}/{{ bound_field.field.rel.to.module_name }}/" class="related-lookup" id="lookup_{{bound_field.element_id}}" onclick="return showRelatedObjectLookupPopup(this);"> <img src="{% admin_media_prefix %}img/admin/selector-search.gif" width="16" height="16" alt="Lookup"></a>
     
    45{% else %}
    56{% if bound_field.needs_add_label %}
    67            <a href="../../../{{ bound_field.field.rel.to.app_label }}/{{ bound_field.field.rel.to.module_name }}/add/" class="add-another" id="add_{{ bound_field.element_id}}" onclick="return showAddAnotherPopup(this);"> <img src="{% admin_media_prefix %}img/admin/icon_addlink.gif" width="10" height="10" alt="Add Another"/></a>
    7 {% endif %} {% endif %}
     8{% endif %}{% endif %}
  • django/contrib/admin/templates/admin/change_list.html

    === django/contrib/admin/templates/admin/change_list.html
    ==================================================================
     
    1 {% load admin_list %}
    2 {% load i18n %}
     1{% load adminmedia admin_list i18n %}
    32{% extends "admin/base_site" %}
    43{% block bodyclass %}change-list{% endblock %}
    54{% if not is_popup %}{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans "Home" %}</a> &rsaquo; {{ cl.opts.verbose_name_plural|capfirst }} </div>{% endblock %}{% endif %}
  • django/contrib/admin/templates/admin/edit_inline_stacked.html

    === django/contrib/admin/templates/admin/edit_inline_stacked.html
    ==================================================================
     
     1{% load admin_modify %}
    12<fieldset class="module aligned">
    23   {% for fcw in bound_related_object.form_field_collection_wrappers %}
    34      <h2>{{ bound_related_object.relation.opts.verbose_name|capfirst }}&nbsp;#{{ forloop.counter }}</h2>
  • django/contrib/admin/templates/admin/filter.html

    === django/contrib/admin/templates/admin/filter.html
    ==================================================================
     
     1{% load i18n %}
    12<h3>{% blocktrans %} By {{ title }} {% endblocktrans %}</h3>
    23<ul>
    34{% for choice in choices %}
  • django/contrib/admin/templates/admin/search_form.html

    === django/contrib/admin/templates/admin/search_form.html
    ==================================================================
     
     1{% load adminmedia %}
    12{% if cl.lookup_opts.admin.search_fields %}
    23<div id="toolbar"><form id="changelist-search" action="" method="get">
    34<label><img src="{% admin_media_prefix %}img/admin/icon_searchbox.png" /></label>
  • django/contrib/admin/templates/admin/pagination.html

    === django/contrib/admin/templates/admin/pagination.html
    ==================================================================
     
     1{% load admin_list %}
    12<p class="paginator">
    23{% if pagination_required %}
    34{% for i in page_range %}
  • django/contrib/admin/templates/admin/field_line.html

    === django/contrib/admin/templates/admin/field_line.html
    ==================================================================
     
     1{% load admin_modify %}
    12<div class="{{ class_names }}" >
    23{% for bound_field in bound_fields %}{{ bound_field.html_error_list }}{% endfor %}
    34{% for bound_field in bound_fields %}
  • django/contrib/admin/templates/admin/edit_inline_tabular.html

    === django/contrib/admin/templates/admin/edit_inline_tabular.html
    ==================================================================
     
     1{% load admin_modify %}
    12<fieldset class="module">
    23   <h2>{{ bound_related_object.relation.opts.verbose_name_plural|capfirst }}</h2><table>
    34   <thead><tr>
  • django/contrib/admin/templates/admin/filters.html

    === django/contrib/admin/templates/admin/filters.html
    ==================================================================
     
     1{% load admin_list %}
    12{% if cl.has_filters %}<div id="changelist-filter">
    23<h2>Filter</h2>
    34{% for spec in cl.filter_specs %}
  • django/contrib/admin/templates/admin/change_form.html

    === django/contrib/admin/templates/admin/change_form.html
    ==================================================================
     
    11{% extends "admin/base_site" %}
    2 {% load i18n %}
    3 {% load admin_modify %}
    4 {% load adminmedia %}
     2{% load i18n admin_modify adminmedia %}
    53{% block extrahead %}
    64{% for js in bound_manipulator.javascript_imports %}{% include_admin_script js %}{% endfor %}
    75{% endblock %}
  • django/core/template/defaultfilters.py

    === django/core/template/defaultfilters.py
    ==================================================================
     
    11"Default variable filters"
    22
    3 from django.core.template import register_filter, resolve_variable
     3from django.core.template import resolve_variable, Library
    44import re
    55import random as random_module
    66
     7register = Library()
    78###################
    89# STRINGS         #
    910###################
    1011
    11 def addslashes(value, _):
     12
     13def addslashes(value):
    1214    "Adds slashes - useful for passing strings to JavaScript, for example."
    1315    return value.replace('"', '\\"').replace("'", "\\'")
    1416
    15 def capfirst(value, _):
     17def capfirst(value):
    1618    "Capitalizes the first character of the value"
    1719    value = str(value)
    1820    return value and value[0].upper() + value[1:]
    1921
    20 def fix_ampersands(value, _):
     22def fix_ampersands(value):
    2123    "Replaces ampersands with ``&amp;`` entities"
    2224    from django.utils.html import fix_ampersands
    2325    return fix_ampersands(value)
    2426
    25 def floatformat(text, _):
     27def floatformat(text):
    2628    """
    2729    Displays a floating point number as 34.2 (with one decimal place) -- but
    2830    only if there's a point to be displayed
     
    3739    else:
    3840        return '%d' % int(f)
    3941
    40 def linenumbers(value, _):
     42def linenumbers(value):
    4143    "Displays text with line numbers"
    4244    from django.utils.html import escape
    4345    lines = value.split('\n')
     
    4749        lines[i] = ("%0" + width  + "d. %s") % (i + 1, escape(line))
    4850    return '\n'.join(lines)
    4951
    50 def lower(value, _):
     52def lower(value):
    5153    "Converts a string into all lowercase"
    5254    return value.lower()
    5355
    54 def make_list(value, _):
     56def make_list(value):
    5557    """
    5658    Returns the value turned into a list. For an integer, it's a list of
    5759    digits. For a string, it's a list of characters.
    5860    """
    5961    return list(str(value))
    6062
    61 def slugify(value, _):
     63def slugify(value):
    6264    "Converts to lowercase, removes non-alpha chars and converts spaces to hyphens"
    6365    value = re.sub('[^\w\s-]', '', value).strip().lower()
    6466    return re.sub('\s+', '-', value)
     
    7779    except (ValueError, TypeError):
    7880        return ""
    7981
    80 def title(value, _):
     82def title(value):
    8183    "Converts a string into titlecase"
    8284    return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title())
    8385
     
    9698        value = str(value)
    9799    return truncate_words(value, length)
    98100
    99 def upper(value, _):
     101def upper(value):
    100102    "Converts a string into all uppercase"
    101103    return value.upper()
    102104
    103 def urlencode(value, _):
     105def urlencode(value):
    104106    "Escapes a value for use in a URL"
    105107    import urllib
    106108    return urllib.quote(value)
    107109
    108 def urlize(value, _):
     110def urlize(value):
    109111    "Converts URLs in plain text into clickable links"
    110112    from django.utils.html import urlize
    111113    return urlize(value, nofollow=True)
     
    119121    from django.utils.html import urlize
    120122    return urlize(value, trim_url_limit=int(limit), nofollow=True)
    121123
    122 def wordcount(value, _):
     124def wordcount(value):
    123125    "Returns the number of words"
    124126    return len(value.split())
    125127
     
    160162# HTML STRINGS    #
    161163###################
    162164
    163 def escape(value, _):
     165def escape(value):
    164166    "Escapes a string's HTML"
    165167    from django.utils.html import escape
    166168    return escape(value)
    167169
    168 def linebreaks(value, _):
     170def linebreaks(value):
    169171    "Converts newlines into <p> and <br />s"
    170172    from django.utils.html import linebreaks
    171173    return linebreaks(value)
    172174
    173 def linebreaksbr(value, _):
     175def linebreaksbr(value):
    174176    "Converts newlines into <br />s"
    175177    return value.replace('\n', '<br />')
    176178
     
    184186    value = endtag_re.sub('', value)
    185187    return value
    186188
    187 def striptags(value, _):
     189def striptags(value):
    188190    "Strips all [X]HTML tags"
    189191    from django.utils.html import strip_tags
    190192    if not isinstance(value, basestring):
     
    214216    decorated.reverse()
    215217    return [item[1] for item in decorated]
    216218
    217 def first(value, _):
     219def first(value):
    218220    "Returns the first item in a list"
    219221    try:
    220222        return value[0]
     
    228230    except AttributeError: # fail silently but nicely
    229231        return value
    230232
    231 def length(value, _):
     233def length(value):
    232234    "Returns the length of the value - useful for lists"
    233235    return len(value)
    234236
     
    236238    "Returns a boolean of whether the value's length is the argument"
    237239    return len(value) == int(arg)
    238240
    239 def random(value, _):
     241def random(value):
    240242    "Returns a random item from the list"
    241243    return random_module.choice(value)
    242244
     
    253255    except (ValueError, TypeError):
    254256        return value # Fail silently.
    255257
    256 def unordered_list(value, _):
     258def unordered_list(value):
    257259    """
    258260    Recursively takes a self-nested list and returns an HTML unordered list --
    259261    WITHOUT opening and closing <ul> tags.
     
    313315###################
    314316# DATES           #
    315317###################
     318from django.conf.settings import DATE_FORMAT, TIME_FORMAT
    316319
    317 def date(value, arg):
     320def date(value, arg = DATE_FORMAT):
    318321    "Formats a date according to the given format"
    319322    from django.utils.dateformat import format
    320323    return format(value, arg)
    321324
    322 def time(value, arg):
     325def time(value, arg = TIME_FORMAT):
    323326    "Formats a time according to the given format"
    324327    from django.utils.dateformat import time_format
    325328    return time_format(value, arg)
    326329
    327 def timesince(value, _):
     330def timesince(value):
    328331    'Formats a date as the time since that date (i.e. "4 days, 6 hours")'
    329332    from django.utils.timesince import timesince
    330333    return timesince(value)
     
    347350    "Returns true if the value is devisible by the argument"
    348351    return int(value) % int(arg) == 0
    349352
    350 def yesno(value, arg):
     353def yesno(value, arg=_("yes,no,maybe")):
    351354    """
    352355    Given a string mapping values for true, false and (optionally) None,
    353356    returns one of those strings accoding to the value:
     
    379382# MISC            #
    380383###################
    381384
    382 def filesizeformat(bytes, _):
     385def filesizeformat(bytes):
    383386    """
    384387    Format the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB, 102
    385388    bytes, etc).
     
    393396        return "%.1f MB" % (bytes / (1024 * 1024))
    394397    return "%.1f GB" % (bytes / (1024 * 1024 * 1024))
    395398
    396 def pluralize(value, _):
     399def pluralize(value):
    397400    "Returns 's' if the value is not 1, for '1 vote' vs. '2 votes'"
    398401    try:
    399402        if int(value) != 1:
     
    408411            pass
    409412    return ''
    410413
    411 def phone2numeric(value, _):
     414def phone2numeric(value):
    412415    "Takes a phone number and converts it in to its numerical equivalent"
    413416    from django.utils.text import phone2numeric
    414417    return phone2numeric(value)
    415418
    416 def pprint(value, _):
     419def pprint(value):
    417420    "A wrapper around pprint.pprint -- for debugging, really"
    418421    from pprint import pformat
    419422    return pformat(value)
    420423
    421 # Syntax: register_filter(name of filter, callback, has_argument)
    422 register_filter('add', add, True)
    423 register_filter('addslashes', addslashes, False)
    424 register_filter('capfirst', capfirst, False)
    425 register_filter('center', center, True)
    426 register_filter('cut', cut, True)
    427 register_filter('date', date, True)
    428 register_filter('default', default, True)
    429 register_filter('default_if_none', default_if_none, True)
    430 register_filter('dictsort', dictsort, True)
    431 register_filter('dictsortreversed', dictsortreversed, True)
    432 register_filter('divisibleby', divisibleby, True)
    433 register_filter('escape', escape, False)
    434 register_filter('filesizeformat', filesizeformat, False)
    435 register_filter('first', first, False)
    436 register_filter('fix_ampersands', fix_ampersands, False)
    437 register_filter('floatformat', floatformat, False)
    438 register_filter('get_digit', get_digit, True)
    439 register_filter('join', join, True)
    440 register_filter('length', length, False)
    441 register_filter('length_is', length_is, True)
    442 register_filter('linebreaks', linebreaks, False)
    443 register_filter('linebreaksbr', linebreaksbr, False)
    444 register_filter('linenumbers', linenumbers, False)
    445 register_filter('ljust', ljust, True)
    446 register_filter('lower', lower, False)
    447 register_filter('make_list', make_list, False)
    448 register_filter('phone2numeric', phone2numeric, False)
    449 register_filter('pluralize', pluralize, False)
    450 register_filter('pprint', pprint, False)
    451 register_filter('removetags', removetags, True)
    452 register_filter('random', random, False)
    453 register_filter('rjust', rjust, True)
    454 register_filter('slice', slice_, True)
    455 register_filter('slugify', slugify, False)
    456 register_filter('stringformat', stringformat, True)
    457 register_filter('striptags', striptags, False)
    458 register_filter('time', time, True)
    459 register_filter('timesince', timesince, False)
    460 register_filter('title', title, False)
    461 register_filter('truncatewords', truncatewords, True)
    462 register_filter('unordered_list', unordered_list, False)
    463 register_filter('upper', upper, False)
    464 register_filter('urlencode', urlencode, False)
    465 register_filter('urlize', urlize, False)
    466 register_filter('urlizetrunc', urlizetrunc, True)
    467 register_filter('wordcount', wordcount, False)
    468 register_filter('wordwrap', wordwrap, True)
    469 register_filter('yesno', yesno, True)
     424# Syntax: register.filter(name of filter, callback)
     425register.filter(add)
     426register.filter(addslashes)
     427register.filter(capfirst)
     428register.filter(center)
     429register.filter(cut)
     430register.filter(date)
     431register.filter(default)
     432register.filter(default_if_none)
     433register.filter(dictsort)
     434register.filter(dictsortreversed)
     435register.filter(divisibleby)
     436register.filter(escape)
     437register.filter(filesizeformat)
     438register.filter(first)
     439register.filter(fix_ampersands)
     440register.filter(floatformat)
     441register.filter(get_digit)
     442register.filter(join)
     443register.filter(length)
     444register.filter(length_is)
     445register.filter(linebreaks)
     446register.filter(linebreaksbr)
     447register.filter(linenumbers)
     448register.filter(ljust)
     449register.filter(lower)
     450register.filter(make_list)
     451register.filter(phone2numeric)
     452register.filter(pluralize)
     453register.filter(pprint)
     454register.filter(removetags)
     455register.filter(random)
     456register.filter(rjust)
     457register.filter(slice_)
     458register.filter(slugify)
     459register.filter(stringformat)
     460register.filter(striptags)
     461register.filter(time)
     462register.filter(timesince)
     463register.filter(title)
     464register.filter(truncatewords)
     465register.filter(unordered_list)
     466register.filter(upper)
     467register.filter(urlencode)
     468register.filter(urlize)
     469register.filter(urlizetrunc)
     470register.filter(wordcount)
     471register.filter(wordwrap)
     472register.filter(yesno)
     473 No newline at end of file
  • django/core/template/__init__.py

    === django/core/template/__init__.py
    ==================================================================
     
    33
    44How it works:
    55
    6 The tokenize() function converts a template string (i.e., a string containing
     6The Lexer.tokenize() function converts a template string (i.e., a string containing
    77markup with custom template tags) to tokens, which can be either plain text
    88(TOKEN_TEXT), variables (TOKEN_VAR) or block statements (TOKEN_BLOCK).
    99
     
    5555'\n<html>\n\n</html>\n'
    5656"""
    5757import re
     58from inspect import getargspec
     59from django.utils.functional import curry
    5860from django.conf.settings import DEFAULT_CHARSET, TEMPLATE_DEBUG
    5961
    6062__all__ = ('Template','Context','compile_string')
     
    8284tag_re = re.compile('(%s.*?%s|%s.*?%s)' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END),
    8385                                          re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END)))
    8486
    85 # global dict used by register_tag; maps custom tags to callback functions
    86 registered_tags = {}
     87# global dictionary of libraries that have been loaded using get_library
     88libraries = {}
     89# global list of libraries to load by default for a new parser
     90builtins = []
    8791
    88 # global dict used by register_filter; maps custom filters to callback functions
    89 registered_filters = {}
    90 
    9192class TemplateSyntaxError(Exception):
    9293    pass
    9394
     
    105106    "Any function raising this exception will be ignored by resolve_variable"
    106107    pass
    107108
     109class InvalidTemplateLibraryException(Exception):
     110    pass
     111
    108112class Origin(object):
    109113    def __init__(self, name):
    110114        self.name = name
     
    264268class Parser(object):
    265269    def __init__(self, tokens):
    266270        self.tokens = tokens
     271        self.tags = {}
     272        self.filters = {}
     273        for lib in builtins:
     274            self.add_library(lib)
    267275
    268276    def parse(self, parse_until=[]):
    269277        nodelist = self.create_nodelist()
     
    274282            elif token.token_type == TOKEN_VAR:
    275283                if not token.contents:
    276284                    self.empty_variable(token)
    277                 var_node = self.create_variable_node(token.contents)
     285                filter_expression = self.compile_filter(token.contents)
     286                var_node = self.create_variable_node(filter_expression)
    278287                self.extend_nodelist(nodelist, var_node,token)
    279288            elif token.token_type == TOKEN_BLOCK:
    280289                if token.contents in parse_until:
     
    288297                # execute callback function for this tag and append resulting node
    289298                self.enter_command(command, token)
    290299                try:
    291                     compile_func = registered_tags[command]
     300                    compile_func = self.tags[command]
    292301                except KeyError:
    293302                    self.invalid_block_tag(token, command)
    294303                try:
     
    302311            self.unclosed_block_tag(parse_until)
    303312        return nodelist
    304313
    305     def create_variable_node(self, contents):
    306         return VariableNode(contents)
     314    def create_variable_node(self, filter_expression):
     315        return VariableNode(filter_expression)
    307316
    308317    def create_nodelist(self):
    309318        return NodeList()
     
    344353    def delete_first_token(self):
    345354        del self.tokens[0]
    346355
     356    def add_library(self, lib):
     357        self.tags.update(lib.tags)
     358        self.filters.update(lib.filters)
     359
     360    def compile_filter(self,token):
     361        "Convenient wrapper for FilterExpression"
     362        return FilterExpression(token, self)
     363
     364    def find_filter(self, filter_name):
     365        if self.filters.has_key(filter_name):
     366            return self.filters[filter_name]
     367        else:
     368            raise TemplateSyntaxError, "Invalid filter: '%s'" % filter_name
     369
    347370class DebugParser(Parser):
    348371    def __init__(self, lexer):
    349372        super(DebugParser, self).__init__(lexer)
     
    483506         (?:%(arg_sep)s
    484507             (?:
    485508              %(i18n_open)s"(?P<i18n_arg>%(str)s)"%(i18n_close)s|
    486               "(?P<arg>%(str)s)"
     509              "(?P<constant_arg>%(str)s)"|
     510              (?P<var_arg>[%(var_chars)s]+)
    487511             )
    488512         )?
    489513 )""" % {
     
    498522filter_raw_string = filter_raw_string.replace("\n", "").replace(" ", "")
    499523filter_re = re.compile(filter_raw_string)
    500524
    501 class FilterParser(object):
     525class FilterExpression(object):
    502526    """
    503527    Parses a variable token and its optional filters (all as a single string),
    504528    and return a list of tuples of the filter name and arguments.
     
    513537    This class should never be instantiated outside of the
    514538    get_filters_from_token helper function.
    515539    """
    516     def __init__(self, token):
     540    def __init__(self, token, parser):
     541        self.token = token
    517542        matches = filter_re.finditer(token)
    518543        var = None
    519544        filters = []
     
    536561                    raise TemplateSyntaxError, "Variables and attributes may not begin with underscores: '%s'" % var
    537562            else:
    538563                filter_name = match.group("filter_name")
    539                 arg, i18n_arg = match.group("arg","i18n_arg")
     564                args = []
     565                constant_arg, i18n_arg, var_arg = match.group("constant_arg","i18n_arg", "var_arg")
    540566                if i18n_arg:
    541                     arg =_(i18n_arg.replace('\\', ''))
    542                 if arg:
    543                     arg = arg.replace('\\', '')
    544                 if not registered_filters.has_key(filter_name):
    545                     raise TemplateSyntaxError, "Invalid filter: '%s'" % filter_name
    546                 if registered_filters[filter_name][1] == True and arg is None:
    547                     raise TemplateSyntaxError, "Filter '%s' requires an argument" % filter_name
    548                 if registered_filters[filter_name][1] == False and arg is not None:
    549                     raise TemplateSyntaxError, "Filter '%s' should not have an argument (argument is %r)" % (filter_name, arg)
    550                 filters.append( (filter_name,arg) )
     567                    args.append((False, _(i18n_arg.replace('\\', ''))))
     568                elif constant_arg:
     569                    args.append((False, constant_arg.replace('\\', '')))
     570                elif var_arg:
     571                    args.append((True, var_arg))
     572                   
     573                filter_func = parser.find_filter(filter_name)
     574                #if needs_arg == True and arg is None:
     575                #   raise TemplateSyntaxError, "Filter '%s' requires an argument" % filter_name
     576                #if needs_arg == False and arg is not None:
     577                #   raise TemplateSyntaxError, "Filter '%s' should not have an argument (argument is %r)" % (filter_name, arg)
     578               
     579                self.args_check(filter_name,filter_func, args)
     580                filters.append( (filter_func,args))
    551581                upto = match.end()
    552582        if upto != len(token):
    553583            raise TemplateSyntaxError, "Could not parse the remainder: %s" % token[upto:]
    554584        self.var , self.filters = var, filters
    555585
    556 def get_filters_from_token(token):
    557     "Convenient wrapper for FilterParser"
    558     p = FilterParser(token)
    559     return (p.var, p.filters)
     586    def resolve(self, context):
     587        try:
     588            obj = resolve_variable(self.var, context)
     589        except VariableDoesNotExist:
     590            obj = ''
     591        for func, args in self.filters:
     592            arg_vals = []
     593            for lookup, arg in args:
     594                if not lookup:
     595                    arg_vals.append(arg)
     596                else:
     597                    arg_vals.append(resolve_variable(arg, context))
     598            obj = func(obj, *arg_vals)
     599        return obj
     600   
     601    def args_check(name, func, provided):
     602        provided = list(provided)
     603        plen = len(provided)
     604        (args, varargs, varkw, defaults) = getargspec(func)
     605        # first argument is filter input
     606        args.pop(0)
     607        if defaults:
     608            nondefs = args[:-len(defaults)]
     609        else:
     610            nondefs = args
     611        # args without defaults must be provided
     612        #print args, nondefs
     613        try:
     614            for arg in nondefs:
     615                provided.pop(0)
     616        except IndexError:
     617            #Not enough
     618            raise TemplateSyntaxError, "%s requires %d arguments, %d provided" % (name, len(nondefs), plen)
     619       
     620       
     621        #defaults can be overridden
     622        defaults = defaults and list(defaults) or []
     623        try:
     624            for parg in provided:
     625                defaults.pop(0)
     626        except IndexError:
     627            #Too many
     628            raise TemplateSyntaxError, "%s requires %d arguments, %d provided" % (name, len(nondefs), plen)
     629       
     630        return True
     631               
     632       
     633    args_check = staticmethod(args_check)
     634   
     635    def __str__(self):
     636        return self.token
    560637
    561638def resolve_variable(path, context):
    562639    """
     
    607684            del bits[0]
    608685    return current
    609686
    610 def resolve_variable_with_filters(var_string, context):
    611     """
    612     var_string is a full variable expression with optional filters, like:
    613         a.b.c|lower|date:"y/m/d"
    614     This function resolves the variable in the context, applies all filters and
    615     returns the object.
    616     """
    617     var, filters = get_filters_from_token(var_string)
    618     try:
    619         obj = resolve_variable(var, context)
    620     except VariableDoesNotExist:
    621         obj = ''
    622     for name, arg in filters:
    623         obj = registered_filters[name][0](obj, arg)
    624     return obj
    625 
    626687class Node:
    627688    def render(self, context):
    628689        "Return the node rendered as a string"
     
    687748        return self.s
    688749
    689750class VariableNode(Node):
    690     def __init__(self, var_string):
    691         self.var_string = var_string
     751    def __init__(self, filter_expression):
     752        self.filter_expression = filter_expression
    692753
    693754    def __repr__(self):
    694         return "<Variable Node: %s>" % self.var_string
     755        return "<Variable Node: %s>" % self.filter_expression
    695756
    696757    def encode_output(self, output):
    697758        # Check type so that we don't run str() on a Unicode object
     
    703764            return output
    704765
    705766    def render(self, context):
    706         output = resolve_variable_with_filters(self.var_string, context)
     767        output = self.filter_expression.resolve(context)
    707768        return self.encode_output(output)
    708769
    709770class DebugVariableNode(VariableNode):
    710771    def render(self, context):
    711772        try:
    712              output = resolve_variable_with_filters(self.var_string, context)
     773             output = self.filter_expression.resolve(context)
    713774        except TemplateSyntaxError, e:
    714775            if not hasattr(e, 'source'):
    715776                e.source = self.source
    716777            raise
    717778        return self.encode_output(output)
    718779
    719 def register_tag(token_command, callback_function):
    720     registered_tags[token_command] = callback_function
     780def generic_tag_compiler(params, defaults, name, node_class, parser, token):
     781    "Returns a template.Node subclass."
     782    bits = token.contents.split()[1:]
     783    bmax = len(params)
     784    def_len = defaults and len(defaults) or 0
     785    bmin = bmax - def_len
     786    if(len(bits) < bmin or len(bits) > bmax):
     787        if bmin == bmax:
     788            message = "%s takes %s arguments" % (name, bmin)
     789        else:
     790            message = "%s takes between %s and %s arguments" % (name, bmin, bmax)
     791        raise TemplateSyntaxError, message
     792    return node_class(bits)
    721793
    722 def unregister_tag(token_command):
    723     del registered_tags[token_command]
     794class Library(object):
     795    def __init__(self):
     796        self.filters = {}
     797        self.tags = {}
     798   
     799    def tag(self, name = None, compile_function = None):
     800        if name == None and compile_function == None:
     801            #@register.tag()
     802            return self.tag_function
     803        elif name != None and compile_function == None:
     804            if(callable(name)):
     805                #@register.tag
     806                return self.tag_function(name)
     807            else:
     808                #@register.tag('somename') or @register.tag(name='somename')
     809                def dec(func):
     810                    return self.tag(name, func)
     811                return dec
     812        elif name != None and compile_function != None:
     813            #register.tag('somename', somefunc)
     814            self.tags[name] = compile_function
     815            return compile_function
     816        else:
     817            raise InvalidTemplateLibraryException, "Unsupported arguments to Library.tag,(%r, %r)", (name, compile_function)
     818   
     819    def tag_function(self,func):
     820        self.tags[func.__name__] = func
     821        return func
     822   
     823    def filter(self, name = None, filter_func = None):
     824        if name == None and filter_func == None:
     825            #@register.filter()
     826            return self.filter_function
     827        elif filter_func == None:
     828            if(callable(name)):
     829                #@register.filter
     830                return self.filter_function(name)
     831            else:
     832                #@register.filter('somename') or @register.filter(name='somename')
     833                def dec(func):
     834                    return self.filter(name, func)
     835                return dec
     836        elif name != None and filter_func != None:
     837            #register.filter('somename', somefunc)
     838            self.filters[name] = filter_func
     839        else:
     840            raise InvalidTemplateLibraryException, "Unsupported arguments to Library.filter,(%r, %r, %r)", (name, compile_function, has_arg)
    724841
    725 def register_filter(filter_name, callback_function, has_arg):
    726     registered_filters[filter_name] = (callback_function, has_arg)
     842    def filter_function(self, func):
     843        self.filters[func.__name__] = func
     844        return func
     845   
     846    def simple_tag(self,func):
     847        (params, xx, xxx, defaults) = getargspec(func)
     848   
     849        class SimpleNode(Node):
     850            def __init__(self, vars_to_resolve):
     851                self.vars_to_resolve = vars_to_resolve
     852   
     853            def render(self, context):
     854                resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve]
     855                return func(*resolved_vars)
     856   
     857        compile_func = curry(generic_tag_compiler, params, defaults, func.__name__, SimpleNode)
     858        compile_func.__doc__ = func.__doc__
     859        self.tag(func.__name__, compile_func)
     860        return func
    727861
    728 def unregister_filter(filter_name):
    729     del registered_filters[filter_name]
     862    def inclusion_tag(self, file_name, context_class=Context, takes_context=False):
     863        def dec(func):
     864            (params, xx, xxx, defaults) = getargspec(func)
     865            if takes_context:
     866                if params[0] == 'context':
     867                    params = params[1:]
     868                else:
     869                    raise TemplateSyntaxError, "Any tag function decorated with takes_context=True must have a first argument of 'context'"
     870   
     871            class InclusionNode(Node):
     872                def __init__(self, vars_to_resolve):
     873                    self.vars_to_resolve = vars_to_resolve
     874   
     875                def render(self, context):
     876                    resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve]
     877                    if takes_context:
     878                        args = [context] + resolved_vars
     879                    else:
     880                        args = resolved_vars
     881   
     882                    dict = func(*args)
     883   
     884                    if not getattr(self, 'nodelist', False):
     885                        from django.core.template_loader import get_template
     886                        t = get_template(file_name)
     887                        self.nodelist = t.nodelist
     888                    return self.nodelist.render(context_class(dict))
     889   
     890            compile_func = curry(generic_tag_compiler, params, defaults, func.__name__, InclusionNode)
     891            compile_func.__doc__ = func.__doc__
     892            self.tag(func.__name__, compile_func)
     893            return func
     894        return dec
    730895
    731 import defaulttags
    732 import defaultfilters
     896def get_library(module_name):
     897    lib = libraries.get(module_name, None)
     898    if not lib:
     899        try:
     900            mod = __import__(module_name, '', '', [''])
     901        except ImportError, e:
     902            raise InvalidTemplateLibraryException, \
     903              "Could not load template library from %s, %s"  % (module_name, e)
     904        for k, v in mod.__dict__.items():
     905            if isinstance(v, Library):
     906                lib = v
     907                libraries[module_name] = lib
     908                break
     909    if not lib:
     910        raise InvalidTemplateLibraryException, \
     911              "Template library %s does not have a Library member"  % module_name
     912    return lib
     913
     914def add_to_builtins(module_name):
     915    builtins.append(get_library(module_name))
     916
     917add_to_builtins('django.core.template.defaulttags')
     918add_to_builtins('django.core.template.defaultfilters')
     919 No newline at end of file
  • django/core/template/defaulttags.py

    === django/core/template/defaulttags.py
    ==================================================================
     
    11"Default tags used by the template system, available to all templates."
    22
    3 from django.core.template import Node, NodeList, Template, Context, resolve_variable, resolve_variable_with_filters, get_filters_from_token, registered_filters
    4 from django.core.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, register_tag
     3from django.core.template import Node, NodeList, Template, Context, resolve_variable
     4from django.core.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END
     5from django.core.template import get_library, Library, InvalidTemplateLibraryException
    56import sys
    67
     8register = Library()
     9
    710class CommentNode(Node):
    811    def render(self, context):
    912        return ''
     
    2730        return ''.join(output)
    2831
    2932class FilterNode(Node):
    30     def __init__(self, filters, nodelist):
    31         self.filters, self.nodelist = filters, nodelist
     33    def __init__(self, filter_expr, nodelist):
     34        self.filter_expr, self.nodelist = filter_expr, nodelist
    3235
    3336    def render(self, context):
    3437        output = self.nodelist.render(context)
    3538        # apply filters
    36         for f in self.filters:
    37             output = registered_filters[f[0]][0](output, f[1])
    38         return output
     39        return filter_expr.resolve(Context({'var': output}))
    3940
    4041class FirstOfNode(Node):
    4142    def __init__(self, vars):
     
    8182            parentloop = {}
    8283        context.push()
    8384        try:
    84             values = resolve_variable_with_filters(self.sequence, context)
     85            values = self.sequence.resolve(context)
    8586        except VariableDoesNotExist:
    8687            values = []
    8788        if values is None:
     
    147148        return self.nodelist_false.render(context)
    148149
    149150class IfNode(Node):
    150     def __init__(self, boolvars, nodelist_true, nodelist_false):
    151         self.boolvars = boolvars
     151    def __init__(self, bool_exprs, nodelist_true, nodelist_false):
     152        self.bool_exprs = bool_exprs
    152153        self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
    153154
    154155    def __repr__(self):
     
    169170        return nodes
    170171
    171172    def render(self, context):
    172         for ifnot, boolvar in self.boolvars:
     173        for ifnot, bool_expr in self.bool_exprs:
    173174            try:
    174                 value = resolve_variable_with_filters(boolvar, context)
     175                value = bool_expr.resolve(context)
    175176            except VariableDoesNotExist:
    176177                value = None
    177178            if (value and not ifnot) or (ifnot and not value):
     
    179180        return self.nodelist_false.render(context)
    180181
    181182class RegroupNode(Node):
    182     def __init__(self, target_var, expression, var_name):
    183         self.target_var, self.expression = target_var, expression
     183    def __init__(self, target, expression, var_name):
     184        self.target, self.expression = target, expression
    184185        self.var_name = var_name
    185186
    186187    def render(self, context):
    187         obj_list = resolve_variable_with_filters(self.target_var, context)
     188        obj_list = self.target.resolve(context)
    188189        if obj_list == '': # target_var wasn't found in context; fail silently
    189190            context[self.var_name] = []
    190191            return ''
    191192        output = [] # list of dictionaries in the format {'grouper': 'key', 'list': [list of contents]}
    192193        for obj in obj_list:
    193             grouper = resolve_variable_with_filters('var.%s' % self.expression, \
    194                 Context({'var': obj}))
     194            grouper = self.expression.resolve(Context({'var': obj}))
    195195            # TODO: Is this a sensible way to determine equality?
    196196            if output and repr(output[-1]['grouper']) == repr(grouper):
    197197                output[-1]['list'].append(obj)
     
    236236        return output
    237237
    238238class LoadNode(Node):
    239     def __init__(self, taglib):
    240         self.taglib = taglib
    241 
    242     def load_taglib(taglib):
    243         mod = __import__("django.templatetags.%s" % taglib.split('.')[-1], '', '', [''])
    244         reload(mod)
    245         return mod
    246     load_taglib = staticmethod(load_taglib)
    247 
    248239    def render(self, context):
    249         "Import the relevant module"
    250         try:
    251             self.__class__.load_taglib(self.taglib)
    252         except ImportError:
    253             pass # Fail silently for invalid loads.
    254240        return ''
    255241
    256242class NowNode(Node):
     
    276262        return self.mapping.get(self.tagtype, '')
    277263
    278264class WidthRatioNode(Node):
    279     def __init__(self, val_var, max_var, max_width):
    280         self.val_var = val_var
    281         self.max_var = max_var
     265    def __init__(self, val_expr, max_expr, max_width):
     266        self.val_expr = val_expr
     267        self.max_expr = max_expr
    282268        self.max_width = max_width
    283269
    284270    def render(self, context):
    285271        try:
    286             value = resolve_variable_with_filters(self.val_var, context)
    287             maxvalue = resolve_variable_with_filters(self.max_var, context)
     272            value = self.val_expr.resolve(context)
     273            maxvalue = self.max_expr.resolve(context)
    288274        except VariableDoesNotExist:
    289275            return ''
    290276        try:
     
    295281            return ''
    296282        return str(int(round(ratio)))
    297283
    298 def do_comment(parser, token):
     284#@register.tag
     285def comment(parser, token):
    299286    """
    300287    Ignore everything between ``{% comment %}`` and ``{% endcomment %}``
    301288    """
    302289    nodelist = parser.parse(('endcomment',))
    303290    parser.delete_first_token()
    304291    return CommentNode()
     292comment = register.tag(comment)
    305293
    306 def do_cycle(parser, token):
     294#@register.tag
     295def cycle(parser, token):
    307296    """
    308297    Cycle among the given strings each time this tag is encountered
    309298
     
    369358
    370359    else:
    371360        raise TemplateSyntaxError("Invalid arguments to 'cycle': %s" % args)
     361cycle = register.tag(cycle)
    372362
    373 def do_debug(parser, token):
    374     "Print a whole load of debugging information, including the context and imported modules"
    375     return DebugNode()
    376 
     363#@register.tag(name="filter")
    377364def do_filter(parser, token):
    378365    """
    379366    Filter the contents of the blog through variable filters.
     
    388375        {% endfilter %}
    389376    """
    390377    _, rest = token.contents.split(None, 1)
    391     _, filters = get_filters_from_token('var|%s' % rest)
     378    filter_expr = parser.compile_filter("var|%s" % (rest))
    392379    nodelist = parser.parse(('endfilter',))
    393380    parser.delete_first_token()
    394     return FilterNode(filters, nodelist)
     381    return FilterNode(filter_expr, nodelist)
     382filter = register.tag("filter", do_filter)
    395383
    396 def do_firstof(parser, token):
     384#@register.tag
     385def firstof(parser, token):
    397386    """
    398387    Outputs the first variable passed that is not False.
    399388
     
    419408    if len(bits) < 1:
    420409        raise TemplateSyntaxError, "'firstof' statement requires at least one argument"
    421410    return FirstOfNode(bits)
     411firstof = register.tag(firstof)
    422412
    423 
     413#@register.tag(name="for")
    424414def do_for(parser, token):
    425415    """
    426416    Loop over each item in an array.
     
    462452    if bits[2] != 'in':
    463453        raise TemplateSyntaxError, "'for' statement must contain 'in' as the second word: %s" % token.contents
    464454    loopvar = bits[1]
    465     sequence = bits[3]
     455    sequence = parser.compile_filter(bits[3])
    466456    reversed = (len(bits) == 5)
    467457    nodelist_loop = parser.parse(('endfor',))
    468458    parser.delete_first_token()
    469459    return ForNode(loopvar, sequence, reversed, nodelist_loop)
     460do_for = register.tag("for", do_for)
    470461
    471462def do_ifequal(parser, token, negate):
    472463    """
     
    497488        nodelist_false = NodeList()
    498489    return IfEqualNode(bits[1], bits[2], nodelist_true, nodelist_false, negate)
    499490
     491#@register.tag
     492def ifequal(parser, token):
     493    return do_ifequal(parser, token, False)
     494ifequal = register.tag(ifequal)
     495
     496#@register.tag
     497def ifnotequal(parser, token):
     498    return do_ifequal(parser, token, True)
     499ifnotequal = register.tag(ifnotequal)
     500
     501#@register.tag(name="if")
    500502def do_if(parser, token):
    501503    """
    502504    The ``{% if %}`` tag evaluates a variable, and if that variable is "true"
     
    554556            not_, boolvar = boolpair.split()
    555557            if not_ != 'not':
    556558                raise TemplateSyntaxError, "Expected 'not' in if statement"
    557             boolvars.append((True, boolvar))
     559            boolvars.append((True, parser.compile_filter(boolvar)))
    558560        else:
    559             boolvars.append((False, boolpair))
     561            boolvars.append((False, parser.compile_filter(boolpair)))
    560562    nodelist_true = parser.parse(('else', 'endif'))
    561563    token = parser.next_token()
    562564    if token.contents == 'else':
     
    565567    else:
    566568        nodelist_false = NodeList()
    567569    return IfNode(boolvars, nodelist_true, nodelist_false)
     570do_if = register.tag("if", do_if)
    568571
    569 def do_ifchanged(parser, token):
     572#@register.tag
     573def ifchanged(parser, token):
    570574    """
    571575    Check if a value has changed from the last iteration of a loop.
    572576
     
    587591    nodelist = parser.parse(('endifchanged',))
    588592    parser.delete_first_token()
    589593    return IfChangedNode(nodelist)
     594ifchanged = register.tag(ifchanged)
    590595
    591 def do_ssi(parser, token):
     596#@register.tag
     597def ssi(parser, token):
    592598    """
    593599    Output the contents of a given file into the page.
    594600
     
    613619        else:
    614620            raise TemplateSyntaxError, "Second (optional) argument to %s tag must be 'parsed'" % bits[0]
    615621    return SsiNode(bits[1], parsed)
     622ssi = register.tag(ssi)
    616623
    617 def do_load(parser, token):
     624#@register.tag
     625def load(parser, token):
    618626    """
    619627    Load a custom template tag set.
    620628
     
    623631        {% load news.photos %}
    624632    """
    625633    bits = token.contents.split()
    626     if len(bits) != 2:
    627         raise TemplateSyntaxError, "'load' statement takes one argument"
    628     taglib = bits[1]
    629     # check at compile time that the module can be imported
    630     try:
    631         LoadNode.load_taglib(taglib)
    632     except ImportError, e:
    633         raise TemplateSyntaxError, "'%s' is not a valid tag library: %s" % (taglib, e)
    634     return LoadNode(taglib)
     634    for taglib in bits[1:]:
     635        # add the library to the parser
     636        try:
     637            lib = get_library("django.templatetags.%s" % taglib.split('.')[-1])
     638            parser.add_library(lib)
     639        except InvalidTemplateLibraryException, e:
     640            raise TemplateSyntaxError, "'%s' is not a valid tag library: %s" % (taglib, e)
     641    return LoadNode()
     642load = register.tag(load)
    635643
    636 def do_now(parser, token):
     644#@register.tag
     645def now(parser, token):
    637646    """
    638647    Display the date, formatted according to the given string.
    639648
     
    649658        raise TemplateSyntaxError, "'now' statement takes one argument"
    650659    format_string = bits[1]
    651660    return NowNode(format_string)
     661now = register.tag(now)
    652662
    653 def do_regroup(parser, token):
     663#@register.tag
     664def regroup(parser, token):
    654665    """
    655666    Regroup a list of alike objects by a common attribute.
    656667
     
    699710    firstbits = token.contents.split(None, 3)
    700711    if len(firstbits) != 4:
    701712        raise TemplateSyntaxError, "'regroup' tag takes five arguments"
    702     target_var = firstbits[1]
     713    target = parser.compile_filter(firstbits[1])
    703714    if firstbits[2] != 'by':
    704715        raise TemplateSyntaxError, "second argument to 'regroup' tag must be 'by'"
    705716    lastbits_reversed = firstbits[3][::-1].split(None, 2)
    706717    if lastbits_reversed[1][::-1] != 'as':
    707718        raise TemplateSyntaxError, "next-to-last argument to 'regroup' tag must be 'as'"
    708     expression = lastbits_reversed[2][::-1]
     719   
     720    expression = parser.compile_filters('var.%s' % lastbits_reversed[2][::-1])
     721   
    709722    var_name = lastbits_reversed[0][::-1]
    710     return RegroupNode(target_var, expression, var_name)
     723    return RegroupNode(target, expression, var_name)
     724regroup = register.tag(regroup)
    711725
    712 def do_templatetag(parser, token):
     726#@register.tag
     727def templatetag(parser, token):
    713728    """
    714729    Output one of the bits used to compose template tags.
    715730
     
    735750        raise TemplateSyntaxError, "Invalid templatetag argument: '%s'. Must be one of: %s" % \
    736751            (tag, TemplateTagNode.mapping.keys())
    737752    return TemplateTagNode(tag)
     753templatetag = register.tag(templatetag)
    738754
    739 def do_widthratio(parser, token):
     755@register.tag
     756def widthratio(parser, token):
    740757    """
    741758    For creating bar charts and such, this tag calculates the ratio of a given
    742759    value to a maximum value, and then applies that ratio to a constant.
     
    752769    bits = token.contents.split()
    753770    if len(bits) != 4:
    754771        raise TemplateSyntaxError("widthratio takes three arguments")
    755     tag, this_value_var, max_value_var, max_width = bits
     772    tag, this_value_expr, max_value_expr, max_width = bits
    756773    try:
    757774        max_width = int(max_width)
    758775    except ValueError:
    759776        raise TemplateSyntaxError("widthratio final argument must be an integer")
    760     return WidthRatioNode(this_value_var, max_value_var, max_width)
    761 
    762 register_tag('comment', do_comment)
    763 register_tag('cycle', do_cycle)
    764 register_tag('debug', do_debug)
    765 register_tag('filter', do_filter)
    766 register_tag('firstof', do_firstof)
    767 register_tag('for', do_for)
    768 register_tag('ifequal', lambda parser, token: do_ifequal(parser, token, False))
    769 register_tag('ifnotequal', lambda parser, token: do_ifequal(parser, token, True))
    770 register_tag('if', do_if)
    771 register_tag('ifchanged', do_ifchanged)
    772 register_tag('regroup', do_regroup)
    773 register_tag('ssi', do_ssi)
    774 register_tag('load', do_load)
    775 register_tag('now', do_now)
    776 register_tag('templatetag', do_templatetag)
    777 register_tag('widthratio', do_widthratio)
     777    return WidthRatioNode(parser.compile_filter(this_value_expr),
     778                          parser.compile_filter(max_value_expr), max_width)
     779widthratio = register.tag(widthratio)
  • django/core/template/loader_tags.py

    === django/core/template/loader_tags.py
    ==================================================================
     
     1from django.core.template import TemplateSyntaxError, TemplateDoesNotExist, resolve_variable
     2from django.core.template import Library, Context, Node
     3from django.core.template.loader import get_template, get_template_from_string, find_template_source
     4from django.conf.settings import TEMPLATE_DEBUG
     5register = Library()
     6
     7class ExtendsError(Exception):
     8    pass
     9
     10class BlockNode(Node):
     11    def __init__(self, name, nodelist, parent=None):
     12        self.name, self.nodelist, self.parent = name, nodelist, parent
     13
     14    def __repr__(self):
     15        return "<Block Node: %s. Contents: %r>" % (self.name, self.nodelist)
     16
     17    def render(self, context):
     18        context.push()
     19        # Save context in case of block.super().
     20        self.context = context
     21        context['block'] = self
     22        result = self.nodelist.render(context)
     23        context.pop()
     24        return result
     25
     26    def super(self):
     27        if self.parent:
     28            return self.parent.render(self.context)
     29        return ''
     30
     31    def add_parent(self, nodelist):
     32        if self.parent:
     33            self.parent.add_parent(nodelist)
     34        else:
     35            self.parent = BlockNode(self.name, nodelist)
     36
     37class ExtendsNode(Node):
     38    def __init__(self, nodelist, parent_name, parent_name_expr, template_dirs=None):
     39        self.nodelist = nodelist
     40        self.parent_name, self.parent_name_expr = parent_name, parent_name_expr
     41        self.template_dirs = template_dirs
     42
     43    def get_parent(self, context):
     44        if self.parent_name_expr:
     45            self.parent_name = self.parent_name_expr.resolve(context)
     46        parent = self.parent_name
     47        if not parent:
     48            error_msg = "Invalid template name in 'extends' tag: %r." % parent
     49            if self.parent_name_expr:
     50                error_msg += " Got this from the %r variable." % self.parent_name_expr #TODO nice repr.
     51            raise TemplateSyntaxError, error_msg
     52        try:
     53            return get_template_from_string(*find_template_source(parent, self.template_dirs))
     54        except TemplateDoesNotExist:
     55            raise TemplateSyntaxError, "Template %r cannot be extended, because it doesn't exist" % parent
     56
     57    def render(self, context):
     58        compiled_parent = self.get_parent(context)
     59        parent_is_child = isinstance(compiled_parent.nodelist[0], ExtendsNode)
     60        parent_blocks = dict([(n.name, n) for n in compiled_parent.nodelist.get_nodes_by_type(BlockNode)])
     61        for block_node in self.nodelist.get_nodes_by_type(BlockNode):
     62            # Check for a BlockNode with this node's name, and replace it if found.
     63            try:
     64                parent_block = parent_blocks[block_node.name]
     65            except KeyError:
     66                # This BlockNode wasn't found in the parent template, but the
     67                # parent block might be defined in the parent's *parent*, so we
     68                # add this BlockNode to the parent's ExtendsNode nodelist, so
     69                # it'll be checked when the parent node's render() is called.
     70                if parent_is_child:
     71                    compiled_parent.nodelist[0].nodelist.append(block_node)
     72            else:
     73                # Keep any existing parents and add a new one. Used by BlockNode.
     74                parent_block.parent = block_node.parent
     75                parent_block.add_parent(parent_block.nodelist)
     76                parent_block.nodelist = block_node.nodelist
     77        return compiled_parent.render(context)
     78
     79class ConstantIncludeNode(Node):
     80    def __init__(self, template_path):
     81        try:
     82            t = get_template(template_path)
     83            self.template = t
     84        except Exception, e:
     85            if TEMPLATE_DEBUG:
     86                raise
     87            self.template = None
     88
     89    def render(self, context):
     90        if self.template:
     91            return self.template.render(context)
     92        else:
     93            return ''
     94
     95class IncludeNode(Node):
     96    def __init__(self, template_name):
     97        self.template_name = template_name
     98
     99    def render(self, context):
     100         try:
     101             template_name = resolve_variable(self.template_name, context)
     102             t = get_template(template_name)
     103             return t.render(context)
     104         except TemplateSyntaxError, e:
     105             if TEMPLATE_DEBUG:
     106                 raise
     107             return ''
     108         except:
     109             return '' # Fail silently for invalid included templates.
     110
     111def do_block(parser, token):
     112    """
     113    Define a block that can be overridden by child templates.
     114    """
     115    bits = token.contents.split()
     116    if len(bits) != 2:
     117        raise TemplateSyntaxError, "'%s' tag takes only one argument" % bits[0]
     118    block_name = bits[1]
     119    # Keep track of the names of BlockNodes found in this template, so we can
     120    # check for duplication.
     121    try:
     122        if block_name in parser.__loaded_blocks:
     123            raise TemplateSyntaxError, "'%s' tag with name '%s' appears more than once" % (bits[0], block_name)
     124        parser.__loaded_blocks.append(block_name)
     125    except AttributeError: # parser._loaded_blocks isn't a list yet
     126        parser.__loaded_blocks = [block_name]
     127    nodelist = parser.parse(('endblock',))
     128    parser.delete_first_token()
     129    return BlockNode(block_name, nodelist)
     130
     131def do_extends(parser, token):
     132    """
     133    Signal that this template extends a parent template.
     134
     135    This tag may be used in two ways: ``{% extends "base" %}`` (with quotes)
     136    uses the literal value "base" as the name of the parent template to extend,
     137    or ``{% extends variable %}`` uses the value of ``variable`` as the name
     138    of the parent template to extend.
     139    """
     140    bits = token.contents.split()
     141    if len(bits) != 2:
     142        raise TemplateSyntaxError, "'%s' takes one argument" % bits[0]
     143    parent_name, parent_name_expr = None, None
     144    if bits[1][0] in ('"', "'") and bits[1][-1] == bits[1][0]:
     145        parent_name = bits[1][1:-1]
     146    else:
     147        parent_name_expr = parser.compile_filter(bits[1])
     148    nodelist = parser.parse()
     149    if nodelist.get_nodes_by_type(ExtendsNode):
     150        raise TemplateSyntaxError, "'%s' cannot appear more than once in the same template" % bits[0]
     151    return ExtendsNode(nodelist, parent_name, parent_name_expr)
     152
     153def do_include(parser, token):
     154    """
     155    Loads a template and renders it with the current context.
     156
     157    Example::
     158
     159        {% include "foo/some_include" %}
     160    """
     161
     162    bits = token.contents.split()
     163    if len(bits) != 2:
     164        raise TemplateSyntaxError, "%r tag takes one argument: the name of the template to be included" % bits[0]
     165    path = bits[1]
     166    if path[0] in ('"', "'") and path[-1] == path[0]:
     167        return ConstantIncludeNode(path[1:-1])
     168    return IncludeNode(bits[1])
     169
     170register.tag('block', do_block)
     171register.tag('extends', do_extends)
     172register.tag('include', do_include)
     173 No newline at end of file
  • django/core/template/loader.py

    === django/core/template/loader.py
    ==================================================================
     
    2121# installed, because pkg_resources is necessary to read eggs.
    2222
    2323from django.core.exceptions import ImproperlyConfigured
    24 from django.core.template import Origin, StringOrigin, Template, Context, Node, TemplateDoesNotExist, TemplateSyntaxError, resolve_variable_with_filters, resolve_variable, register_tag
     24from django.core.template import Origin, StringOrigin, Template,  TemplateDoesNotExist, add_to_builtins
    2525from django.conf.settings import TEMPLATE_LOADERS, TEMPLATE_DEBUG
    2626
    2727template_source_loaders = []
     
    6868def load_template_source(name, dirs=None):
    6969    find_template_source(name, dirs)[0]
    7070
    71 class ExtendsError(Exception):
    72     pass
    73 
    7471def get_template(template_name):
    7572    """
    7673    Returns a compiled Template object for the given template name,
     
    113110    # If we get here, none of the templates could be loaded
    114111    raise TemplateDoesNotExist, ', '.join(template_name_list)
    115112
    116 class BlockNode(Node):
    117     def __init__(self, name, nodelist, parent=None):
    118         self.name, self.nodelist, self.parent = name, nodelist, parent
    119 
    120     def __repr__(self):
    121         return "<Block Node: %s. Contents: %r>" % (self.name, self.nodelist)
    122 
    123     def render(self, context):
    124         context.push()
    125         # Save context in case of block.super().
    126         self.context = context
    127         context['block'] = self
    128         result = self.nodelist.render(context)
    129         context.pop()
    130         return result
    131 
    132     def super(self):
    133         if self.parent:
    134             return self.parent.render(self.context)
    135         return ''
    136 
    137     def add_parent(self, nodelist):
    138         if self.parent:
    139             self.parent.add_parent(nodelist)
    140         else:
    141             self.parent = BlockNode(self.name, nodelist)
    142 
    143 class ExtendsNode(Node):
    144     def __init__(self, nodelist, parent_name, parent_name_var, template_dirs=None):
    145         self.nodelist = nodelist
    146         self.parent_name, self.parent_name_var = parent_name, parent_name_var
    147         self.template_dirs = template_dirs
    148 
    149     def get_parent(self, context):
    150         if self.parent_name_var:
    151             self.parent_name = resolve_variable_with_filters(self.parent_name_var, context)
    152         parent = self.parent_name
    153         if not parent:
    154             error_msg = "Invalid template name in 'extends' tag: %r." % parent
    155             if self.parent_name_var:
    156                 error_msg += " Got this from the %r variable." % self.parent_name_var
    157             raise TemplateSyntaxError, error_msg
    158         try:
    159             return get_template_from_string(*find_template_source(parent, self.template_dirs))
    160         except TemplateDoesNotExist:
    161             raise TemplateSyntaxError, "Template %r cannot be extended, because it doesn't exist" % parent
    162 
    163     def render(self, context):
    164         compiled_parent = self.get_parent(context)
    165         parent_is_child = isinstance(compiled_parent.nodelist[0], ExtendsNode)
    166         parent_blocks = dict([(n.name, n) for n in compiled_parent.nodelist.get_nodes_by_type(BlockNode)])
    167         for block_node in self.nodelist.get_nodes_by_type(BlockNode):
    168             # Check for a BlockNode with this node's name, and replace it if found.
    169             try:
    170                 parent_block = parent_blocks[block_node.name]
    171             except KeyError:
    172                 # This BlockNode wasn't found in the parent template, but the
    173                 # parent block might be defined in the parent's *parent*, so we
    174                 # add this BlockNode to the parent's ExtendsNode nodelist, so
    175                 # it'll be checked when the parent node's render() is called.
    176                 if parent_is_child:
    177                     compiled_parent.nodelist[0].nodelist.append(block_node)
    178             else:
    179                 # Keep any existing parents and add a new one. Used by BlockNode.
    180                 parent_block.parent = block_node.parent
    181                 parent_block.add_parent(parent_block.nodelist)
    182                 parent_block.nodelist = block_node.nodelist
    183         return compiled_parent.render(context)
    184 
    185 class ConstantIncludeNode(Node):
    186     def __init__(self, template_path):
    187         try:
    188             t = get_template(template_path)
    189             self.template = t
    190         except Exception, e:
    191             if TEMPLATE_DEBUG:
    192                 raise
    193             self.template = None
    194 
    195     def render(self, context):
    196         if self.template:
    197             return self.template.render(context)
    198         else:
    199             return ''
    200 
    201 class IncludeNode(Node):
    202     def __init__(self, template_name):
    203         self.template_name = template_name
    204 
    205     def render(self, context):
    206          try:
    207              template_name = resolve_variable(self.template_name, context)
    208              t = get_template(template_name)
    209              return t.render(context)
    210          except TemplateSyntaxError, e:
    211              if TEMPLATE_DEBUG:
    212                  raise
    213              return ''
    214          except:
    215              return '' # Fail silently for invalid included templates.
    216 
    217 def do_block(parser, token):
    218     """
    219     Define a block that can be overridden by child templates.
    220     """
    221     bits = token.contents.split()
    222     if len(bits) != 2:
    223         raise TemplateSyntaxError, "'%s' tag takes only one argument" % bits[0]
    224     block_name = bits[1]
    225     # Keep track of the names of BlockNodes found in this template, so we can
    226     # check for duplication.
    227     try:
    228         if block_name in parser.__loaded_blocks:
    229             raise TemplateSyntaxError, "'%s' tag with name '%s' appears more than once" % (bits[0], block_name)
    230         parser.__loaded_blocks.append(block_name)
    231     except AttributeError: # parser._loaded_blocks isn't a list yet
    232         parser.__loaded_blocks = [block_name]
    233     nodelist = parser.parse(('endblock',))
    234     parser.delete_first_token()
    235     return BlockNode(block_name, nodelist)
    236 
    237 def do_extends(parser, token):
    238     """
    239     Signal that this template extends a parent template.
    240 
    241     This tag may be used in two ways: ``{% extends "base" %}`` (with quotes)
    242     uses the literal value "base" as the name of the parent template to extend,
    243     or ``{% extends variable %}`` uses the value of ``variable`` as the name
    244     of the parent template to extend.
    245     """
    246     bits = token.contents.split()
    247     if len(bits) != 2:
    248         raise TemplateSyntaxError, "'%s' takes one argument" % bits[0]
    249     parent_name, parent_name_var = None, None
    250     if bits[1][0] in ('"', "'") and bits[1][-1] == bits[1][0]:
    251         parent_name = bits[1][1:-1]
    252     else:
    253         parent_name_var = bits[1]
    254     nodelist = parser.parse()
    255     if nodelist.get_nodes_by_type(ExtendsNode):
    256         raise TemplateSyntaxError, "'%s' cannot appear more than once in the same template" % bits[0]
    257     return ExtendsNode(nodelist, parent_name, parent_name_var)
    258 
    259 def do_include(parser, token):
    260     """
    261     Loads a template and renders it with the current context.
    262 
    263     Example::
    264 
    265         {% include "foo/some_include" %}
    266     """
    267 
    268     bits = token.contents.split()
    269     if len(bits) != 2:
    270         raise TemplateSyntaxError, "%r tag takes one argument: the name of the template to be included" % bits[0]
    271     path = bits[1]
    272     if path[0] in ('"', "'") and path[-1] == path[0]:
    273         return ConstantIncludeNode(path[1:-1])
    274     return IncludeNode(bits[1])
    275 
    276 register_tag('block', do_block)
    277 register_tag('extends', do_extends)
    278 register_tag('include', do_include)
     113   
     114add_to_builtins('django.core.template.loader_tags')
     115 No newline at end of file
  • django/core/template/decorators.py

    === django/core/template/decorators.py
    ==================================================================
     
    1 from django.core.template import Context, Node, TemplateSyntaxError, register_tag, resolve_variable
    2 from django.core.template_loader import get_template
    3 from django.utils.functional import curry
    4 from inspect import getargspec
    5 
    6 def generic_tag_compiler(params, defaults, name, node_class, parser, token):
    7     "Returns a template.Node subclass."
    8     bits = token.contents.split()[1:]
    9     bmax = len(params)
    10     def_len = defaults and len(defaults) or 0
    11     bmin = bmax - def_len
    12     if(len(bits) < bmin or len(bits) > bmax):
    13         if bmin == bmax:
    14             message = "%s takes %s arguments" % (name, bmin)
    15         else:
    16             message = "%s takes between %s and %s arguments" % (name, bmin, bmax)
    17         raise TemplateSyntaxError, message
    18     return node_class(bits)
    19 
    20 def simple_tag(func):
    21     (params, xx, xxx, defaults) = getargspec(func)
    22 
    23     class SimpleNode(Node):
    24         def __init__(self, vars_to_resolve):
    25             self.vars_to_resolve = vars_to_resolve
    26 
    27         def render(self, context):
    28             resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve]
    29             return func(*resolved_vars)
    30 
    31     compile_func = curry(generic_tag_compiler, params, defaults, func.__name__, SimpleNode)
    32     compile_func.__doc__ = func.__doc__
    33     register_tag(func.__name__, compile_func)
    34     return func
    35 
    36 def inclusion_tag(file_name, context_class=Context, takes_context=False):
    37     def dec(func):
    38         (params, xx, xxx, defaults) = getargspec(func)
    39         if takes_context:
    40             if params[0] == 'context':
    41                 params = params[1:]
    42             else:
    43                 raise TemplateSyntaxError, "Any tag function decorated with takes_context=True must have a first argument of 'context'"
    44 
    45         class InclusionNode(Node):
    46             def __init__(self, vars_to_resolve):
    47                 self.vars_to_resolve = vars_to_resolve
    48 
    49             def render(self, context):
    50                 resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve]
    51                 if takes_context:
    52                     args = [context] + resolved_vars
    53                 else:
    54                     args = resolved_vars
    55 
    56                 dict = func(*args)
    57 
    58                 if not getattr(self, 'nodelist', False):
    59                     t = get_template(file_name)
    60                     self.nodelist = t.nodelist
    61                 return self.nodelist.render(context_class(dict))
    62 
    63         compile_func = curry(generic_tag_compiler, params, defaults, func.__name__, InclusionNode)
    64         compile_func.__doc__ = func.__doc__
    65         register_tag(func.__name__, compile_func)
    66         return func
    67     return dec
  • django/templatetags/i18n.py

    Property changes on: 
    ___________________________________________________________________
    Name: svk:merge
     -bcc190cf-cafb-0310-a4f2-bffc1f526a37:/django/trunk:1440
     +bcc190cf-cafb-0310-a4f2-bffc1f526a37:/django/trunk:1054
    
    === django/templatetags/i18n.py
    ==================================================================
     
    1 from django.core.template import Node, NodeList, Template, Context, resolve_variable, resolve_variable_with_filters, registered_filters
    2 from django.core.template import TemplateSyntaxError, register_tag, TokenParser
     1from django.core.template import Node, NodeList, Template, Context, resolve_variable
     2from django.core.template import TemplateSyntaxError, TokenParser, Library
    33from django.core.template import TOKEN_BLOCK, TOKEN_TEXT, TOKEN_VAR
    44from django.utils import translation
    55import re, sys
    66
     7register = Library()
     8
    79class GetAvailableLanguagesNode(Node):
    810    def __init__(self, variable):
    911        self.variable = variable
     
    5355    def render(self, context):
    5456        context.push()
    5557        for var,val in self.extra_context.items():
    56             context[var] = resolve_variable_with_filters(val, context)
     58            context[var] = val.resolve(context)
    5759        singular = self.render_token_list(self.singular)
    5860        if self.plural and self.countervar and self.counter:
    59             count = resolve_variable_with_filters(self.counter, context)
     61            count = self.counter.resolve(context)
    6062            context[self.countervar] = count
    6163            plural = self.render_token_list(self.plural)
    6264            result = translation.ngettext(singular, plural, count) % context
     
    179181                    value = self.value()
    180182                    if self.tag() != 'as':
    181183                        raise TemplateSyntaxError, "variable bindings in 'blocktrans' must be 'with value as variable'"
    182                     extra_context[self.tag()] = value
     184                    extra_context[self.tag()] = parser.compile_filter(value)
    183185                elif tag == 'count':
    184                     counter = self.value()
     186                    counter = parser.compile_filter(self.value())
    185187                    if self.tag() != 'as':
    186188                        raise TemplateSyntaxError, "counter specification in 'blocktrans' must be 'count value as variable'"
    187189                    countervar = self.tag()
     
    191193
    192194    (countervar, counter, extra_context) = BlockTranslateParser(token.contents).top()
    193195
     196
    194197    singular = []
    195198    plural = []
    196199    while parser.tokens:
     
    213216
    214217    return BlockTranslateNode(extra_context, singular, plural, countervar, counter)
    215218
    216 register_tag('get_available_languages', do_get_available_languages)
    217 register_tag('get_current_language', do_get_current_language)
    218 register_tag('trans', do_translate)
    219 register_tag('blocktrans', do_block_translate)
     219register.tag('get_available_languages', do_get_available_languages)
     220register.tag('get_current_language', do_get_current_language)
     221register.tag('trans', do_translate)
     222register.tag('blocktrans', do_block_translate)
  • tests/testapp/templatetags/testtags.py

    === tests/testapp/templatetags/testtags.py
    ==================================================================
     
    22
    33from django.core import template
    44
     5register = template.Library()
     6
    57class EchoNode(template.Node):
    68    def __init__(self, contents):
    79        self.contents = contents
     
    1113       
    1214def do_echo(parser, token):
    1315    return EchoNode(token.contents.split()[1:])
    14    
    15 template.register_tag("echo", do_echo)
    16  No newline at end of file
     16
     17register.tag("echo", do_echo)
     18 No newline at end of file
  • tests/othertests/defaultfilters.py

    === tests/othertests/defaultfilters.py
    ==================================================================
     
    11"""
    2 >>> floatformat(7.7, None)
     2>>> floatformat(7.7)
    33'7.7'
    4 >>> floatformat(7.0, None)
     4>>> floatformat(7.0)
    55'7'
    6 >>> floatformat(0.7, None)
     6>>> floatformat(0.7)
    77'0.7'
    8 >>> floatformat(0.07, None)
     8>>> floatformat(0.07)
    99'0.1'
    10 >>> floatformat(0.007, None)
     10>>> floatformat(0.007)
    1111'0.0'
    12 >>> floatformat(0.0, None)
     12>>> floatformat(0.0)
    1313'0'
    1414"""
    1515
  • tests/othertests/templates.py

    === tests/othertests/templates.py
    ==================================================================
     
    102102    #Escaped string as argument
    103103    'basic-syntax30': (r"""{{ var|default_if_none:" endquote\" hah" }}""", {"var": None}, ' endquote" hah'),
    104104
     105    # Variable as argument
     106    'basic-syntax31': (r"""{{ var|default_if_none:var2 }}""", {"var":None, "var2": "happy"  }, 'happy'),
     107
     108    #Default argument testing
     109    'basic-syntax32' : (r"""{{ var|yesno:"yup,nup,mup" }} {{var|yesno}}""", {"var": True}, 'yup yes'),
     110
    105111    ### IF TAG ################################################################
    106112    'if-tag01': ("{% if foo %}yes{% else %}no{% endif %}", {"foo": True}, "yes"),
    107113    'if-tag02': ("{% if foo %}yes{% else %}no{% endif %}", {"foo": False}, "no"),
  • tests/othertests/markup.py

    === tests/othertests/markup.py
    ==================================================================
     
    11# Quick tests for the markup templatetags (django.contrib.markup)
    22
    3 from django.core.template import Template, Context
    4 import django.contrib.markup.templatetags.markup # this registers the filters
     3from django.core.template import Template, Context, add_to_builtins
    54
     5add_to_builtins('django.contrib.markup.templatetags.markup')
     6
    67# find out if markup modules are installed and tailor the test appropriately
    78try:
    89    import textile
  • docs/templates_python.txt

    === docs/templates_python.txt
    ==================================================================
     
    444444Once you've created that Python module, you'll just have to write a bit of
    445445Python code, depending on whether you're writing filters or tags.
    446446
     447To be a valid tag library, a module must have a member that is a template.Library
     448instance, in which all the tags and filters are registered. So at the top of your
     449module, put the following:
     450
     451        from django.core import template
     452        register = template.Library()
     453
     454It is convention to call this instance ``register``.
     455
    447456.. admonition:: Behind the scenes
    448457
    449458    For a ton of examples, read the source code for Django's default filters
     
    453462Writing custom template filters
    454463-------------------------------
    455464
    456 Custom filters are just Python functions that take two arguments:
     465Custom filters are just Python functions that take one or two arguments:
    457466
    458467    * The value of the variable (input) -- not necessarily a string
    459     * The value of the argument -- always a string
     468    * The value of the argument -- this can have a default value, or be left
     469                                   out altogether
    460470
    461471Filter functions should always return something. They shouldn't raise
    462472exceptions. They should fail silently. In case of error, they should return
     
    468478        "Removes all values of arg from the given string"
    469479        return value.replace(arg, '')
    470480
    471 Most filters don't take arguments. For filters that don't take arguments, the
    472 convention is to use a single underscore as the second argument to the filter
    473 definition. Example::
     481Most filters don't take arguments. Example::
    474482
    475     def lower(value, _):
     483    def lower(value):
    476484        "Converts a string into all lowercase"
    477485        return value.lower()
    478486
    479 When you've written your filter definition, you need to register it, to make it
    480 available to Django's template language::
     487When you've written your filter definition, you need to register it with
     488your Library instance, to make it available to Django's template language::
    481489
    482     from django.core import template
    483     template.register_filter('cut', cut, True)
    484     template.register_filter('lower', lower, False)
     490    register.filter('cut', cut)
     491    register.filter('lower', lower)
    485492
    486 ``register_filter`` takes three arguments:
     493        this takes two arguments:
    487494
    488495    1. The name of the filter -- a string.
    489496    2. The compilation function -- a Python function (not the name of the
    490497       function as a string).
    491     3. A boolean, designating whether the filter requires an argument. This
    492        tells Django's template parser whether to throw ``TemplateSyntaxError``
    493        when filter arguments are given (or missing).
    494498
    495 The convention is to put all ``register_filter`` calls at the bottom of your
    496 template-library module.
     499It can also be used as a decorator if you are using python 2.4 or above:
     500 
     501        @register.filter(name="cheese")
     502        def cheese_filter(value, arg="Wensleydale"):
     503                return "%s likes %s" % (value, arg)
     504 
     505        @register.filter
     506        def lower(value):
     507                return value.lower()   
    497508
     509If you leave out the name of the filter, as in the second example above, the functions
     510name will be used as the filter name.
     511
    498512Writing custom template tags
    499513----------------------------
    500514
     
    525539function with the tag contents and the parser object itself. This function is
    526540responsible for returning a ``Node`` instance based on the contents of the tag.
    527541
    528 By convention, the name of each compilation function should start with ``do_``.
    529 
    530542For example, let's write a template tag, ``{% current_time %}``, that displays
    531543the current date/time, formatted according to a parameter given in the tag, in
    532544`strftime syntax`_. It's a good idea to decide the tag syntax before anything
     
    612624Registering the tag
    613625~~~~~~~~~~~~~~~~~~~
    614626
    615 Finally, use a ``register_tag`` call, as in ``register_filter`` above. Example::
     627Finally, register the tag with your modules Library instance. Example::
    616628
    617     from django.core import template
    618     template.register_tag('current_time', do_current_time)
     629    register.tag('current_time', do_current_time)
    619630
    620 ``register_tag`` takes two arguments:
     631        this takes two arguments:
    621632
    622     1. The name of the template tag -- a string.
     633    1. The name of the template tag -- a string. If this is left out, the
     634       name of the compilation function will be used.
    623635    2. The compilation function -- a Python function (not the name of the
    624636       function as a string).
    625637
     638As with filter registration, it is also possible to use this as a decorator in
     639Python 2.4 and above:
     640
     641        @register.tag
     642        def shout(parser, token):
     643                ...
     644
     645        @register.tag(name="milk")
     646        def do_milk(parser, token):
     647                ...
     648
    626649Setting a variable in the context
    627650~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    628651
Back to Top