=== django/contrib/markup/templatetags/markup.py
==================================================================
--- django/contrib/markup/templatetags/markup.py	(/django/trunk)	(revision 3632)
+++ django/contrib/markup/templatetags/markup.py	(/local/template-library)	(revision 3632)
@@ -16,7 +16,9 @@
 
 from django.core import template
 
-def textile(value, _):
+register = template.Library()
+
+def textile(value):
     try:
         import textile
     except ImportError:
@@ -24,7 +26,7 @@
     else:
         return textile.textile(value)
         
-def markdown(value, _):
+def markdown(value):
     try:
         import markdown
     except ImportError:
@@ -32,7 +34,7 @@
     else:
         return markdown.markdown(value)
         
-def restructuredtext(value, _):
+def restructuredtext(value):
     try:
         from docutils.core import publish_parts
     except ImportError:
@@ -41,6 +43,6 @@
         parts = publish_parts(source=value, writer_name="html4css1")
         return parts["fragment"]
         
-template.register_filter("textile", textile, False)
-template.register_filter("markdown", markdown, False)
-template.register_filter("restructuredtext", restructuredtext, False)
\ No newline at end of file
+register.filter(textile)
+register.filter(markdown)
+register.filter(restructuredtext)
\ No newline at end of file
=== django/contrib/comments/templatetags/comments.py
==================================================================
--- django/contrib/comments/templatetags/comments.py	(/django/trunk)	(revision 3632)
+++ django/contrib/comments/templatetags/comments.py	(/local/template-library)	(revision 3632)
@@ -6,6 +6,8 @@
 from django.models.core import contenttypes
 import re
 
+register = template.Library()
+
 COMMENT_FORM = '''
 {% load i18n %}
 {% if display_form %}
@@ -360,10 +362,10 @@
         return CommentListNode(package, module, var_name, obj_id, tokens[5], self.free, ordering)
 
 # registration comments
-template.register_tag('get_comment_list', DoGetCommentList(False))
-template.register_tag('comment_form', DoCommentForm(False))
-template.register_tag('get_comment_count', DoCommentCount(False))
+register.tag('get_comment_list',DoGetCommentList(False))
+register.tag('comment_form', DoCommentForm(False))
+register.tag('get_comment_count',DoCommentCount(False))
 # free comments
-template.register_tag('get_free_comment_list', DoGetCommentList(True))
-template.register_tag('free_comment_form', DoCommentForm(True))
-template.register_tag('get_free_comment_count', DoCommentCount(True))
+register.tag('get_free_comment_list', DoGetCommentList(True))
+register.tag('free_comment_form', DoCommentForm(True))
+register.tag('get_free_comment_count', DoCommentCount(True))
=== django/contrib/admin/templatetags/adminmedia.py
==================================================================
--- django/contrib/admin/templatetags/adminmedia.py	(/django/trunk)	(revision 3632)
+++ django/contrib/admin/templatetags/adminmedia.py	(/local/template-library)	(revision 3632)
@@ -1,4 +1,5 @@
-from django.core.template.decorators import simple_tag
+from django.core.template import Library
+register = Library()
 
 def admin_media_prefix():
     try:
@@ -6,4 +7,4 @@
     except ImportError:
         return ''
     return ADMIN_MEDIA_PREFIX
-admin_media_prefix = simple_tag(admin_media_prefix)
+admin_media_prefix = register.simple_tag(admin_media_prefix)
\ No newline at end of file
=== django/contrib/admin/templatetags/admin_modify.py
==================================================================
--- django/contrib/admin/templatetags/admin_modify.py	(/django/trunk)	(revision 3632)
+++ django/contrib/admin/templatetags/admin_modify.py	(/local/template-library)	(revision 3632)
@@ -2,24 +2,25 @@
 from django.utils.html import escape
 from django.utils.text import capfirst
 from django.utils.functional import curry
-from django.core.template.decorators import simple_tag, inclusion_tag
 from django.contrib.admin.views.main import AdminBoundField
 from django.core.meta.fields import BoundField, Field
 from django.core.meta import BoundRelatedObject, TABULAR, STACKED
 from django.conf.settings import ADMIN_MEDIA_PREFIX
 import re
 
+register = template.Library()
+
 word_re = re.compile('[A-Z][a-z]+')
 
 def class_name_to_underscored(name):
     return '_'.join([s.lower() for s in word_re.findall(name)[:-1]])
 
-#@simple_tag
+#@register.simple_tag
 def include_admin_script(script_path):
     return '<script type="text/javascript" src="%s%s"></script>' % (ADMIN_MEDIA_PREFIX, script_path)
-include_admin_script = simple_tag(include_admin_script)
+include_admin_script = register.simple_tag(include_admin_script)
 
-#@inclusion_tag('admin/submit_line', takes_context=True)
+#@register.inclusion_tag('admin/submit_line', takes_context=True)
 def submit_row(context, bound_manipulator):
     change = context['change']
     add = context['add']
@@ -36,9 +37,9 @@
         'show_save_and_continue': not is_popup,
         'show_save': True
     }
-submit_row = inclusion_tag('admin/submit_line', takes_context=True)(submit_row)
+submit_row = register.inclusion_tag('admin/submit_line', takes_context=True)(submit_row)
 
-#@simple_tag
+#@register.simple_tag
 def field_label(bound_field):
     class_names = []
     if isinstance(bound_field.field, meta.BooleanField):
@@ -53,7 +54,7 @@
     class_str = class_names and ' class="%s"' % ' '.join(class_names) or ''
     return '<label for="%s"%s>%s%s</label> ' % (bound_field.element_id, class_str, \
         capfirst(bound_field.field.verbose_name), colon)
-field_label = simple_tag(field_label)
+field_label = register.simple_tag(field_label)
 
 class FieldWidgetNode(template.Node):
     nodelists = {}
@@ -170,12 +171,12 @@
         context.pop()
         return output
 
-#@simple_tag
+#@register.simple_tag
 def output_all(form_fields):
     return ''.join([str(f) for f in form_fields])
-output_all = simple_tag(output_all)
+output_all = register.simple_tag(output_all)
 
-#@simple_tag
+#@register.simple_tag
 def auto_populated_field_script(auto_pop_fields, change = False):
     for field in auto_pop_fields:
         t = []
@@ -191,9 +192,9 @@
                      ' if(!e._changed) { e.value = URLify(%s, %s);} }; ' % (
                      f, field.name, add_values, field.maxlength))
     return ''.join(t)
-auto_populated_field_script = simple_tag(auto_populated_field_script)
+auto_populated_field_script = register.simple_tag(auto_populated_field_script)
 
-#@simple_tag
+#@register.simple_tag
 def filter_interface_script_maybe(bound_field):
     f = bound_field.field
     if f.rel and isinstance(f.rel, meta.ManyToMany) and f.rel.filter_interface:
@@ -202,7 +203,7 @@
               f.name, f.verbose_name, f.rel.filter_interface-1, ADMIN_MEDIA_PREFIX)
     else:
         return ''
-filter_interface_script_maybe = simple_tag(filter_interface_script_maybe)
+filter_interface_script_maybe = register.simple_tag(filter_interface_script_maybe)
 
 def do_one_arg_tag(node_factory, parser,token):
     tokens = token.contents.split()
@@ -213,7 +214,7 @@
 def register_one_arg_tag(node):
     tag_name = class_name_to_underscored(node.__name__)
     parse_func = curry(do_one_arg_tag, node)
-    template.register_tag(tag_name, parse_func)
+    register.tag(tag_name, parse_func)
 
 one_arg_tag_nodes = (
     FieldWidgetNode,
@@ -223,7 +224,7 @@
 for node in one_arg_tag_nodes:
     register_one_arg_tag(node)
 
-#@inclusion_tag('admin/field_line', takes_context=True)
+#@register.inclusion_tag('admin/field_line', takes_context=True)
 def admin_field_line(context, argument_val):
     if (isinstance(argument_val, BoundField)):
         bound_fields = [argument_val]
@@ -249,10 +250,10 @@
         'bound_fields':  bound_fields,
         'class_names': " ".join(class_names),
     }
-admin_field_line = inclusion_tag('admin/field_line', takes_context=True)(admin_field_line)
+admin_field_line = register.inclusion_tag('admin/field_line', takes_context=True)(admin_field_line)
 
-#@simple_tag
+#@register.simple_tag
 def object_pk(bound_manip, ordered_obj):
     return bound_manip.get_ordered_object_pk(ordered_obj)
 
-object_pk = simple_tag(object_pk)
+object_pk = register.simple_tag(object_pk)
=== django/contrib/admin/templatetags/log.py
==================================================================
--- django/contrib/admin/templatetags/log.py	(/django/trunk)	(revision 3632)
+++ django/contrib/admin/templatetags/log.py	(/local/template-library)	(revision 3632)
@@ -1,6 +1,8 @@
 from django.models.admin import log
 from django.core import template
 
+register = template.Library()
+
 class AdminLogNode(template.Node):
     def __init__(self, limit, varname, user):
         self.limit, self.varname, self.user = limit, varname, user
@@ -48,4 +50,4 @@
                 raise template.TemplateSyntaxError, "Fourth argument in '%s' must be 'for_user'" % self.tag_name
         return AdminLogNode(limit=tokens[1], varname=tokens[3], user=(len(tokens) > 5 and tokens[5] or None))
 
-template.register_tag('get_admin_log', DoGetAdminLog('get_admin_log'))
+register.tag('get_admin_log', DoGetAdminLog('get_admin_log'))
=== django/contrib/admin/templatetags/admin_list.py
==================================================================
--- django/contrib/admin/templatetags/admin_list.py	(/django/trunk)	(revision 3632)
+++ django/contrib/admin/templatetags/admin_list.py	(/local/template-library)	(revision 3632)
@@ -3,16 +3,18 @@
 from django.contrib.admin.views.main import IS_POPUP_VAR, EMPTY_CHANGELIST_VALUE, MONTHS
 from django.core import meta, template
 from django.core.exceptions import ObjectDoesNotExist
-from django.core.template.decorators import simple_tag, inclusion_tag
 from django.utils import dateformat
 from django.utils.html import strip_tags, escape
 from django.utils.text import capfirst
 from django.utils.translation import get_date_formats
 from django.conf.settings import ADMIN_MEDIA_PREFIX
+from django.core.template import Library
 
+register = Library()
+
 DOT = '.'
 
-#@simple_tag
+#@register.simple_tag
 def paginator_number(cl,i):
     if i == DOT:
        return '... '
@@ -20,9 +22,9 @@
        return '<span class="this-page">%d</span> ' % (i+1)
     else:
        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)
-paginator_number = simple_tag(paginator_number)
+paginator_number = register.simple_tag(paginator_number)
 
-#@inclusion_tag('admin/pagination')
+#@register.inclusion_tag('admin/pagination')
 def pagination(cl):
     paginator, page_num = cl.paginator, cl.page_num
 
@@ -64,7 +66,7 @@
         'ALL_VAR': ALL_VAR,
         '1': 1,
     }
-pagination = inclusion_tag('admin/pagination')(pagination)
+pagination = register.inclusion_tag('admin/pagination')(pagination)
 
 def result_headers(cl):
     lookup_opts = cl.lookup_opts
@@ -177,15 +179,15 @@
     for res in cl.result_list:
         yield list(items_for_result(cl,res))
 
-#@inclusion_tag("admin/change_list_results")
+#@register.inclusion_tag("admin/change_list_results")
 def result_list(cl):
     res = list(results(cl))
     return {'cl': cl,
             'result_headers': list(result_headers(cl)),
             'results': list(results(cl))}
-result_list = inclusion_tag("admin/change_list_results")(result_list)
+result_list = register.inclusion_tag("admin/change_list_results")(result_list)
 
-#@inclusion_tag("admin/date_hierarchy")
+#@register.inclusion_tag("admin/date_hierarchy")
 def date_hierarchy(cl):
     lookup_opts, params, lookup_params, lookup_mod = \
       cl.lookup_opts, cl.params, cl.lookup_params, cl.lookup_mod
@@ -256,23 +258,23 @@
                     'title': year.year
                 } for year in years ]
             }
-date_hierarchy = inclusion_tag('admin/date_hierarchy')(date_hierarchy)
+date_hierarchy = register.inclusion_tag('admin/date_hierarchy')(date_hierarchy)
 
-#@inclusion_tag('admin/search_form')
+#@register.inclusion_tag('admin/search_form')
 def search_form(cl):
     return {
         'cl': cl,
         'show_result_count': cl.result_count != cl.full_result_count and not cl.opts.one_to_one_field,
         'search_var': SEARCH_VAR
     }
-search_form = inclusion_tag('admin/search_form')(search_form)
+search_form = register.inclusion_tag('admin/search_form')(search_form)
 
-#@inclusion_tag('admin/filter')
+#@register.inclusion_tag('admin/filter')
 def filter(cl, spec):
     return {'title': spec.title(), 'choices' : list(spec.choices(cl))}
-filter = inclusion_tag('admin/filter')(filter)
+filter = register.inclusion_tag('admin/filter')(filter)
 
-#@inclusion_tag('admin/filters')
+#@register.inclusion_tag('admin/filters')
 def filters(cl):
     return {'cl': cl}
-filters = inclusion_tag('admin/filters')(filters)
+filters = register.inclusion_tag('admin/filters')(filters)
=== django/contrib/admin/templatetags/adminapplist.py
==================================================================
--- django/contrib/admin/templatetags/adminapplist.py	(/django/trunk)	(revision 3632)
+++ django/contrib/admin/templatetags/adminapplist.py	(/local/template-library)	(revision 3632)
@@ -1,5 +1,7 @@
 from django.core import template
 
+register = template.Library()
+
 class AdminApplistNode(template.Node):
     def __init__(self, varname):
         self.varname = varname
@@ -54,4 +56,4 @@
         raise template.TemplateSyntaxError, "First argument to '%s' tag must be 'as'" % tokens[0]
     return AdminApplistNode(tokens[2])
 
-template.register_tag('get_admin_app_list', get_admin_app_list)
+register.tag('get_admin_app_list', get_admin_app_list)
=== django/contrib/admin/views/template.py
==================================================================
--- django/contrib/admin/views/template.py	(/django/trunk)	(revision 3632)
+++ django/contrib/admin/views/template.py	(/local/template-library)	(revision 3632)
@@ -55,7 +55,9 @@
             node = loader.do_extends(parser, token)
             node.template_dirs = settings_module.TEMPLATE_DIRS
             return node
-        template.register_tag('extends', new_do_extends)
+        register = template.Library()
+        register.tag('extends', new_do_extends)
+        template.builtins.append(register)
 
         # now validate the template using the new template dirs
         # making sure to reset the extends function in any case
@@ -65,6 +67,6 @@
             tmpl.render(template.Context({}))
         except template.TemplateSyntaxError, e:
             error = e
-        template.register_tag('extends', loader.do_extends)
+        template.builtins.remove(register)
         if error:
             raise validators.ValidationError, e.args
=== django/contrib/admin/templates/widget/file.html
==================================================================
--- django/contrib/admin/templates/widget/file.html	(/django/trunk)	(revision 3632)
+++ django/contrib/admin/templates/widget/file.html	(/local/template-library)	(revision 3632)
@@ -1,4 +1,4 @@
-{% if bound_field.original_value %}
+{% load admin_modify %}{% if bound_field.original_value %}
 Currently: <a href="{{ bound_field.original_url }}" > {{ bound_field.original_value }} </a><br />
 Change: {% output_all bound_field.form_fields %}
 {% else %} {% output_all bound_field.form_fields %} {% endif %}
=== django/contrib/admin/templates/widget/default.html
==================================================================
--- django/contrib/admin/templates/widget/default.html	(/django/trunk)	(revision 3632)
+++ django/contrib/admin/templates/widget/default.html	(/local/template-library)	(revision 3632)
@@ -1 +1 @@
-{% output_all bound_field.form_fields %}
+{% load admin_modify %}{% output_all bound_field.form_fields %}
=== django/contrib/admin/templates/widget/foreign.html
==================================================================
--- django/contrib/admin/templates/widget/foreign.html	(/django/trunk)	(revision 3632)
+++ django/contrib/admin/templates/widget/foreign.html	(/local/template-library)	(revision 3632)
@@ -1,3 +1,4 @@
+{% load admin_modify adminmedia %}
 {% output_all bound_field.form_fields %}
 {% if bound_field.raw_id_admin %}
             <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>
@@ -4,4 +5,4 @@
 {% else %}
 {% if bound_field.needs_add_label %}
             <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>
-{% endif %} {% endif %}
+{% endif %}{% endif %}
=== django/contrib/admin/templates/admin/change_list.html
==================================================================
--- django/contrib/admin/templates/admin/change_list.html	(/django/trunk)	(revision 3632)
+++ django/contrib/admin/templates/admin/change_list.html	(/local/template-library)	(revision 3632)
@@ -1,5 +1,4 @@
-{% load admin_list %}
-{% load i18n %}
+{% load adminmedia admin_list i18n %}
 {% extends "admin/base_site" %}
 {% block bodyclass %}change-list{% endblock %}
 {% 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	(/django/trunk)	(revision 3632)
+++ django/contrib/admin/templates/admin/edit_inline_stacked.html	(/local/template-library)	(revision 3632)
@@ -1,3 +1,4 @@
+{% load admin_modify %}
 <fieldset class="module aligned">
    {% for fcw in bound_related_object.form_field_collection_wrappers %}
       <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	(/django/trunk)	(revision 3632)
+++ django/contrib/admin/templates/admin/filter.html	(/local/template-library)	(revision 3632)
@@ -1,3 +1,4 @@
+{% load i18n %}
 <h3>{% blocktrans %} By {{ title }} {% endblocktrans %}</h3>
 <ul>
 {% for choice in choices %}
=== django/contrib/admin/templates/admin/search_form.html
==================================================================
--- django/contrib/admin/templates/admin/search_form.html	(/django/trunk)	(revision 3632)
+++ django/contrib/admin/templates/admin/search_form.html	(/local/template-library)	(revision 3632)
@@ -1,3 +1,4 @@
+{% load adminmedia %}
 {% if cl.lookup_opts.admin.search_fields %}
 <div id="toolbar"><form id="changelist-search" action="" method="get">
 <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	(/django/trunk)	(revision 3632)
+++ django/contrib/admin/templates/admin/pagination.html	(/local/template-library)	(revision 3632)
@@ -1,3 +1,4 @@
+{% load admin_list %}
 <p class="paginator">
 {% if pagination_required %}
 {% for i in page_range %}
=== django/contrib/admin/templates/admin/field_line.html
==================================================================
--- django/contrib/admin/templates/admin/field_line.html	(/django/trunk)	(revision 3632)
+++ django/contrib/admin/templates/admin/field_line.html	(/local/template-library)	(revision 3632)
@@ -1,3 +1,4 @@
+{% load admin_modify %}
 <div class="{{ class_names }}" >
 {% for bound_field in bound_fields %}{{ bound_field.html_error_list }}{% endfor %}
 {% for bound_field in bound_fields %}
=== django/contrib/admin/templates/admin/edit_inline_tabular.html
==================================================================
--- django/contrib/admin/templates/admin/edit_inline_tabular.html	(/django/trunk)	(revision 3632)
+++ django/contrib/admin/templates/admin/edit_inline_tabular.html	(/local/template-library)	(revision 3632)
@@ -1,3 +1,4 @@
+{% load admin_modify %}
 <fieldset class="module">
    <h2>{{ bound_related_object.relation.opts.verbose_name_plural|capfirst }}</h2><table>
    <thead><tr>
=== django/contrib/admin/templates/admin/filters.html
==================================================================
--- django/contrib/admin/templates/admin/filters.html	(/django/trunk)	(revision 3632)
+++ django/contrib/admin/templates/admin/filters.html	(/local/template-library)	(revision 3632)
@@ -1,3 +1,4 @@
+{% load admin_list %}
 {% if cl.has_filters %}<div id="changelist-filter">
 <h2>Filter</h2>
 {% for spec in cl.filter_specs %}
=== django/contrib/admin/templates/admin/change_form.html
==================================================================
--- django/contrib/admin/templates/admin/change_form.html	(/django/trunk)	(revision 3632)
+++ django/contrib/admin/templates/admin/change_form.html	(/local/template-library)	(revision 3632)
@@ -1,7 +1,5 @@
 {% extends "admin/base_site" %}
-{% load i18n %}
-{% load admin_modify %}
-{% load adminmedia %}
+{% load i18n admin_modify adminmedia %}
 {% block extrahead %}
 {% for js in bound_manipulator.javascript_imports %}{% include_admin_script js %}{% endfor %}
 {% endblock %}
=== django/core/template/defaultfilters.py
==================================================================
--- django/core/template/defaultfilters.py	(/django/trunk)	(revision 3632)
+++ django/core/template/defaultfilters.py	(/local/template-library)	(revision 3632)
@@ -1,28 +1,30 @@
 "Default variable filters"
 
-from django.core.template import register_filter, resolve_variable
+from django.core.template import resolve_variable, Library
 import re
 import random as random_module
 
+register = Library()
 ###################
 # STRINGS         #
 ###################
 
-def addslashes(value, _):
+
+def addslashes(value):
     "Adds slashes - useful for passing strings to JavaScript, for example."
     return value.replace('"', '\\"').replace("'", "\\'")
 
-def capfirst(value, _):
+def capfirst(value):
     "Capitalizes the first character of the value"
     value = str(value)
     return value and value[0].upper() + value[1:]
 
-def fix_ampersands(value, _):
+def fix_ampersands(value):
     "Replaces ampersands with ``&amp;`` entities"
     from django.utils.html import fix_ampersands
     return fix_ampersands(value)
 
-def floatformat(text, _):
+def floatformat(text):
     """
     Displays a floating point number as 34.2 (with one decimal place) -- but
     only if there's a point to be displayed
@@ -37,7 +39,7 @@
     else:
         return '%d' % int(f)
 
-def linenumbers(value, _):
+def linenumbers(value):
     "Displays text with line numbers"
     from django.utils.html import escape
     lines = value.split('\n')
@@ -47,18 +49,18 @@
         lines[i] = ("%0" + width  + "d. %s") % (i + 1, escape(line))
     return '\n'.join(lines)
 
-def lower(value, _):
+def lower(value):
     "Converts a string into all lowercase"
     return value.lower()
 
-def make_list(value, _):
+def make_list(value):
     """
     Returns the value turned into a list. For an integer, it's a list of
     digits. For a string, it's a list of characters.
     """
     return list(str(value))
 
-def slugify(value, _):
+def slugify(value):
     "Converts to lowercase, removes non-alpha chars and converts spaces to hyphens"
     value = re.sub('[^\w\s-]', '', value).strip().lower()
     return re.sub('\s+', '-', value)
@@ -77,7 +79,7 @@
     except (ValueError, TypeError):
         return ""
 
-def title(value, _):
+def title(value):
     "Converts a string into titlecase"
     return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title())
 
@@ -96,16 +98,16 @@
         value = str(value)
     return truncate_words(value, length)
 
-def upper(value, _):
+def upper(value):
     "Converts a string into all uppercase"
     return value.upper()
 
-def urlencode(value, _):
+def urlencode(value):
     "Escapes a value for use in a URL"
     import urllib
     return urllib.quote(value)
 
-def urlize(value, _):
+def urlize(value):
     "Converts URLs in plain text into clickable links"
     from django.utils.html import urlize
     return urlize(value, nofollow=True)
@@ -119,7 +121,7 @@
     from django.utils.html import urlize
     return urlize(value, trim_url_limit=int(limit), nofollow=True)
 
-def wordcount(value, _):
+def wordcount(value):
     "Returns the number of words"
     return len(value.split())
 
@@ -160,17 +162,17 @@
 # HTML STRINGS    #
 ###################
 
-def escape(value, _):
+def escape(value):
     "Escapes a string's HTML"
     from django.utils.html import escape
     return escape(value)
 
-def linebreaks(value, _):
+def linebreaks(value):
     "Converts newlines into <p> and <br />s"
     from django.utils.html import linebreaks
     return linebreaks(value)
 
-def linebreaksbr(value, _):
+def linebreaksbr(value):
     "Converts newlines into <br />s"
     return value.replace('\n', '<br />')
 
@@ -184,7 +186,7 @@
     value = endtag_re.sub('', value)
     return value
 
-def striptags(value, _):
+def striptags(value):
     "Strips all [X]HTML tags"
     from django.utils.html import strip_tags
     if not isinstance(value, basestring):
@@ -214,7 +216,7 @@
     decorated.reverse()
     return [item[1] for item in decorated]
 
-def first(value, _):
+def first(value):
     "Returns the first item in a list"
     try:
         return value[0]
@@ -228,7 +230,7 @@
     except AttributeError: # fail silently but nicely
         return value
 
-def length(value, _):
+def length(value):
     "Returns the length of the value - useful for lists"
     return len(value)
 
@@ -236,7 +238,7 @@
     "Returns a boolean of whether the value's length is the argument"
     return len(value) == int(arg)
 
-def random(value, _):
+def random(value):
     "Returns a random item from the list"
     return random_module.choice(value)
 
@@ -253,7 +255,7 @@
     except (ValueError, TypeError):
         return value # Fail silently.
 
-def unordered_list(value, _):
+def unordered_list(value):
     """
     Recursively takes a self-nested list and returns an HTML unordered list --
     WITHOUT opening and closing <ul> tags.
@@ -313,18 +315,19 @@
 ###################
 # DATES           #
 ###################
+from django.conf.settings import DATE_FORMAT, TIME_FORMAT 
 
-def date(value, arg):
+def date(value, arg = DATE_FORMAT):
     "Formats a date according to the given format"
     from django.utils.dateformat import format
     return format(value, arg)
 
-def time(value, arg):
+def time(value, arg = TIME_FORMAT):
     "Formats a time according to the given format"
     from django.utils.dateformat import time_format
     return time_format(value, arg)
 
-def timesince(value, _):
+def timesince(value):
     'Formats a date as the time since that date (i.e. "4 days, 6 hours")'
     from django.utils.timesince import timesince
     return timesince(value)
@@ -347,7 +350,7 @@
     "Returns true if the value is devisible by the argument"
     return int(value) % int(arg) == 0
 
-def yesno(value, arg):
+def yesno(value, arg=_("yes,no,maybe")):
     """
     Given a string mapping values for true, false and (optionally) None,
     returns one of those strings accoding to the value:
@@ -379,7 +382,7 @@
 # MISC            #
 ###################
 
-def filesizeformat(bytes, _):
+def filesizeformat(bytes):
     """
     Format the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB, 102
     bytes, etc).
@@ -393,7 +396,7 @@
         return "%.1f MB" % (bytes / (1024 * 1024))
     return "%.1f GB" % (bytes / (1024 * 1024 * 1024))
 
-def pluralize(value, _):
+def pluralize(value):
     "Returns 's' if the value is not 1, for '1 vote' vs. '2 votes'"
     try:
         if int(value) != 1:
@@ -408,62 +411,62 @@
             pass
     return ''
 
-def phone2numeric(value, _):
+def phone2numeric(value):
     "Takes a phone number and converts it in to its numerical equivalent"
     from django.utils.text import phone2numeric
     return phone2numeric(value)
 
-def pprint(value, _):
+def pprint(value):
     "A wrapper around pprint.pprint -- for debugging, really"
     from pprint import pformat
     return pformat(value)
 
-# Syntax: register_filter(name of filter, callback, has_argument)
-register_filter('add', add, True)
-register_filter('addslashes', addslashes, False)
-register_filter('capfirst', capfirst, False)
-register_filter('center', center, True)
-register_filter('cut', cut, True)
-register_filter('date', date, True)
-register_filter('default', default, True)
-register_filter('default_if_none', default_if_none, True)
-register_filter('dictsort', dictsort, True)
-register_filter('dictsortreversed', dictsortreversed, True)
-register_filter('divisibleby', divisibleby, True)
-register_filter('escape', escape, False)
-register_filter('filesizeformat', filesizeformat, False)
-register_filter('first', first, False)
-register_filter('fix_ampersands', fix_ampersands, False)
-register_filter('floatformat', floatformat, False)
-register_filter('get_digit', get_digit, True)
-register_filter('join', join, True)
-register_filter('length', length, False)
-register_filter('length_is', length_is, True)
-register_filter('linebreaks', linebreaks, False)
-register_filter('linebreaksbr', linebreaksbr, False)
-register_filter('linenumbers', linenumbers, False)
-register_filter('ljust', ljust, True)
-register_filter('lower', lower, False)
-register_filter('make_list', make_list, False)
-register_filter('phone2numeric', phone2numeric, False)
-register_filter('pluralize', pluralize, False)
-register_filter('pprint', pprint, False)
-register_filter('removetags', removetags, True)
-register_filter('random', random, False)
-register_filter('rjust', rjust, True)
-register_filter('slice', slice_, True)
-register_filter('slugify', slugify, False)
-register_filter('stringformat', stringformat, True)
-register_filter('striptags', striptags, False)
-register_filter('time', time, True)
-register_filter('timesince', timesince, False)
-register_filter('title', title, False)
-register_filter('truncatewords', truncatewords, True)
-register_filter('unordered_list', unordered_list, False)
-register_filter('upper', upper, False)
-register_filter('urlencode', urlencode, False)
-register_filter('urlize', urlize, False)
-register_filter('urlizetrunc', urlizetrunc, True)
-register_filter('wordcount', wordcount, False)
-register_filter('wordwrap', wordwrap, True)
-register_filter('yesno', yesno, True)
+# Syntax: register.filter(name of filter, callback)
+register.filter(add)
+register.filter(addslashes)
+register.filter(capfirst)
+register.filter(center)
+register.filter(cut)
+register.filter(date)
+register.filter(default)
+register.filter(default_if_none)
+register.filter(dictsort)
+register.filter(dictsortreversed)
+register.filter(divisibleby)
+register.filter(escape)
+register.filter(filesizeformat)
+register.filter(first)
+register.filter(fix_ampersands)
+register.filter(floatformat)
+register.filter(get_digit)
+register.filter(join)
+register.filter(length)
+register.filter(length_is)
+register.filter(linebreaks)
+register.filter(linebreaksbr)
+register.filter(linenumbers)
+register.filter(ljust)
+register.filter(lower)
+register.filter(make_list)
+register.filter(phone2numeric)
+register.filter(pluralize)
+register.filter(pprint)
+register.filter(removetags)
+register.filter(random)
+register.filter(rjust)
+register.filter(slice_)
+register.filter(slugify)
+register.filter(stringformat)
+register.filter(striptags)
+register.filter(time)
+register.filter(timesince)
+register.filter(title)
+register.filter(truncatewords)
+register.filter(unordered_list)
+register.filter(upper)
+register.filter(urlencode)
+register.filter(urlize)
+register.filter(urlizetrunc)
+register.filter(wordcount)
+register.filter(wordwrap)
+register.filter(yesno)
\ No newline at end of file
=== django/core/template/__init__.py
==================================================================
--- django/core/template/__init__.py	(/django/trunk)	(revision 3632)
+++ django/core/template/__init__.py	(/local/template-library)	(revision 3632)
@@ -3,7 +3,7 @@
 
 How it works:
 
-The tokenize() function converts a template string (i.e., a string containing
+The Lexer.tokenize() function converts a template string (i.e., a string containing
 markup with custom template tags) to tokens, which can be either plain text
 (TOKEN_TEXT), variables (TOKEN_VAR) or block statements (TOKEN_BLOCK).
 
@@ -55,6 +55,8 @@
 '\n<html>\n\n</html>\n'
 """
 import re
+from inspect import getargspec
+from django.utils.functional import curry
 from django.conf.settings import DEFAULT_CHARSET, TEMPLATE_DEBUG
 
 __all__ = ('Template','Context','compile_string')
@@ -82,12 +84,11 @@
 tag_re = re.compile('(%s.*?%s|%s.*?%s)' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END),
                                           re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END)))
 
-# global dict used by register_tag; maps custom tags to callback functions
-registered_tags = {}
+# global dictionary of libraries that have been loaded using get_library
+libraries = {}
+# global list of libraries to load by default for a new parser
+builtins = []
 
-# global dict used by register_filter; maps custom filters to callback functions
-registered_filters = {}
-
 class TemplateSyntaxError(Exception):
     pass
 
@@ -105,6 +106,9 @@
     "Any function raising this exception will be ignored by resolve_variable"
     pass
 
+class InvalidTemplateLibraryException(Exception):
+    pass
+
 class Origin(object):
     def __init__(self, name):
         self.name = name
@@ -264,6 +268,10 @@
 class Parser(object):
     def __init__(self, tokens):
         self.tokens = tokens
+        self.tags = {}
+        self.filters = {}
+        for lib in builtins:
+            self.add_library(lib)
 
     def parse(self, parse_until=[]):
         nodelist = self.create_nodelist()
@@ -274,7 +282,8 @@
             elif token.token_type == TOKEN_VAR:
                 if not token.contents:
                     self.empty_variable(token)
-                var_node = self.create_variable_node(token.contents)
+                filter_expression = self.compile_filter(token.contents)
+                var_node = self.create_variable_node(filter_expression)
                 self.extend_nodelist(nodelist, var_node,token)
             elif token.token_type == TOKEN_BLOCK:
                 if token.contents in parse_until:
@@ -288,7 +297,7 @@
                 # execute callback function for this tag and append resulting node
                 self.enter_command(command, token)
                 try:
-                    compile_func = registered_tags[command]
+                    compile_func = self.tags[command]
                 except KeyError:
                     self.invalid_block_tag(token, command)
                 try:
@@ -302,8 +311,8 @@
             self.unclosed_block_tag(parse_until)
         return nodelist
 
-    def create_variable_node(self, contents):
-        return VariableNode(contents)
+    def create_variable_node(self, filter_expression):
+        return VariableNode(filter_expression)
 
     def create_nodelist(self):
         return NodeList()
@@ -344,6 +353,20 @@
     def delete_first_token(self):
         del self.tokens[0]
 
+    def add_library(self, lib):
+        self.tags.update(lib.tags)
+        self.filters.update(lib.filters)
+
+    def compile_filter(self,token):
+        "Convenient wrapper for FilterExpression"
+        return FilterExpression(token, self)
+
+    def find_filter(self, filter_name):
+        if self.filters.has_key(filter_name):
+            return self.filters[filter_name]
+        else:
+            raise TemplateSyntaxError, "Invalid filter: '%s'" % filter_name
+
 class DebugParser(Parser):
     def __init__(self, lexer):
         super(DebugParser, self).__init__(lexer)
@@ -483,7 +506,8 @@
          (?:%(arg_sep)s
              (?:
               %(i18n_open)s"(?P<i18n_arg>%(str)s)"%(i18n_close)s|
-              "(?P<arg>%(str)s)"
+              "(?P<constant_arg>%(str)s)"|
+              (?P<var_arg>[%(var_chars)s]+)
              )
          )?
  )""" % {
@@ -498,7 +522,7 @@
 filter_raw_string = filter_raw_string.replace("\n", "").replace(" ", "")
 filter_re = re.compile(filter_raw_string)
 
-class FilterParser(object):
+class FilterExpression(object):
     """
     Parses a variable token and its optional filters (all as a single string),
     and return a list of tuples of the filter name and arguments.
@@ -513,7 +537,8 @@
     This class should never be instantiated outside of the
     get_filters_from_token helper function.
     """
-    def __init__(self, token):
+    def __init__(self, token, parser):
+        self.token = token
         matches = filter_re.finditer(token)
         var = None
         filters = []
@@ -536,27 +561,79 @@
                     raise TemplateSyntaxError, "Variables and attributes may not begin with underscores: '%s'" % var
             else:
                 filter_name = match.group("filter_name")
-                arg, i18n_arg = match.group("arg","i18n_arg")
+                args = []
+                constant_arg, i18n_arg, var_arg = match.group("constant_arg","i18n_arg", "var_arg")
                 if i18n_arg:
-                    arg =_(i18n_arg.replace('\\', ''))
-                if arg:
-                    arg = arg.replace('\\', '')
-                if not registered_filters.has_key(filter_name):
-                    raise TemplateSyntaxError, "Invalid filter: '%s'" % filter_name
-                if registered_filters[filter_name][1] == True and arg is None:
-                    raise TemplateSyntaxError, "Filter '%s' requires an argument" % filter_name
-                if registered_filters[filter_name][1] == False and arg is not None:
-                    raise TemplateSyntaxError, "Filter '%s' should not have an argument (argument is %r)" % (filter_name, arg)
-                filters.append( (filter_name,arg) )
+                    args.append((False, _(i18n_arg.replace('\\', ''))))
+                elif constant_arg:
+                    args.append((False, constant_arg.replace('\\', '')))
+                elif var_arg:
+                    args.append((True, var_arg))
+                    
+                filter_func = parser.find_filter(filter_name)
+                #if needs_arg == True and arg is None:
+                #   raise TemplateSyntaxError, "Filter '%s' requires an argument" % filter_name
+                #if needs_arg == False and arg is not None:
+                #   raise TemplateSyntaxError, "Filter '%s' should not have an argument (argument is %r)" % (filter_name, arg)
+                
+                self.args_check(filter_name,filter_func, args)
+                filters.append( (filter_func,args))
                 upto = match.end()
         if upto != len(token):
             raise TemplateSyntaxError, "Could not parse the remainder: %s" % token[upto:]
         self.var , self.filters = var, filters
 
-def get_filters_from_token(token):
-    "Convenient wrapper for FilterParser"
-    p = FilterParser(token)
-    return (p.var, p.filters)
+    def resolve(self, context):
+        try:
+            obj = resolve_variable(self.var, context)
+        except VariableDoesNotExist:
+            obj = ''
+        for func, args in self.filters:
+            arg_vals = []
+            for lookup, arg in args:
+                if not lookup:
+                    arg_vals.append(arg) 
+                else:
+                    arg_vals.append(resolve_variable(arg, context))
+            obj = func(obj, *arg_vals)
+        return obj
+    
+    def args_check(name, func, provided):
+        provided = list(provided)
+        plen = len(provided)
+        (args, varargs, varkw, defaults) = getargspec(func)
+        # first argument is filter input
+        args.pop(0)
+        if defaults:
+            nondefs = args[:-len(defaults)] 
+        else:
+            nondefs = args
+        # args without defaults must be provided
+        #print args, nondefs
+        try:
+            for arg in nondefs:
+                provided.pop(0)
+        except IndexError:
+            #Not enough
+            raise TemplateSyntaxError, "%s requires %d arguments, %d provided" % (name, len(nondefs), plen) 
+        
+        
+        #defaults can be overridden
+        defaults = defaults and list(defaults) or []
+        try:
+            for parg in provided:
+                defaults.pop(0)
+        except IndexError:
+            #Too many
+            raise TemplateSyntaxError, "%s requires %d arguments, %d provided" % (name, len(nondefs), plen) 
+        
+        return True
+                
+        
+    args_check = staticmethod(args_check)
+    
+    def __str__(self):
+        return self.token
 
 def resolve_variable(path, context):
     """
@@ -607,22 +684,6 @@
             del bits[0]
     return current
 
-def resolve_variable_with_filters(var_string, context):
-    """
-    var_string is a full variable expression with optional filters, like:
-        a.b.c|lower|date:"y/m/d"
-    This function resolves the variable in the context, applies all filters and
-    returns the object.
-    """
-    var, filters = get_filters_from_token(var_string)
-    try:
-        obj = resolve_variable(var, context)
-    except VariableDoesNotExist:
-        obj = ''
-    for name, arg in filters:
-        obj = registered_filters[name][0](obj, arg)
-    return obj
-
 class Node:
     def render(self, context):
         "Return the node rendered as a string"
@@ -687,11 +748,11 @@
         return self.s
 
 class VariableNode(Node):
-    def __init__(self, var_string):
-        self.var_string = var_string
+    def __init__(self, filter_expression):
+        self.filter_expression = filter_expression
 
     def __repr__(self):
-        return "<Variable Node: %s>" % self.var_string
+        return "<Variable Node: %s>" % self.filter_expression
 
     def encode_output(self, output):
         # Check type so that we don't run str() on a Unicode object
@@ -703,30 +764,155 @@
             return output
 
     def render(self, context):
-        output = resolve_variable_with_filters(self.var_string, context)
+        output = self.filter_expression.resolve(context)
         return self.encode_output(output)
 
 class DebugVariableNode(VariableNode):
     def render(self, context):
         try:
-             output = resolve_variable_with_filters(self.var_string, context)
+             output = self.filter_expression.resolve(context)
         except TemplateSyntaxError, e:
             if not hasattr(e, 'source'):
                 e.source = self.source
             raise
         return self.encode_output(output)
 
-def register_tag(token_command, callback_function):
-    registered_tags[token_command] = callback_function
+def generic_tag_compiler(params, defaults, name, node_class, parser, token):
+    "Returns a template.Node subclass."
+    bits = token.contents.split()[1:]
+    bmax = len(params)
+    def_len = defaults and len(defaults) or 0
+    bmin = bmax - def_len
+    if(len(bits) < bmin or len(bits) > bmax):
+        if bmin == bmax:
+            message = "%s takes %s arguments" % (name, bmin)
+        else:
+            message = "%s takes between %s and %s arguments" % (name, bmin, bmax)
+        raise TemplateSyntaxError, message
+    return node_class(bits)
 
-def unregister_tag(token_command):
-    del registered_tags[token_command]
+class Library(object):
+    def __init__(self):
+        self.filters = {}
+        self.tags = {}
+    
+    def tag(self, name = None, compile_function = None):
+        if name == None and compile_function == None:
+            #@register.tag()
+            return self.tag_function
+        elif name != None and compile_function == None:
+            if(callable(name)): 
+                #@register.tag
+                return self.tag_function(name)
+            else:
+                #@register.tag('somename') or @register.tag(name='somename')
+                def dec(func):
+                    return self.tag(name, func)
+                return dec
+        elif name != None and compile_function != None:
+            #register.tag('somename', somefunc)
+            self.tags[name] = compile_function
+            return compile_function
+        else:
+            raise InvalidTemplateLibraryException, "Unsupported arguments to Library.tag,(%r, %r)", (name, compile_function)
+    
+    def tag_function(self,func):
+        self.tags[func.__name__] = func
+        return func
+    
+    def filter(self, name = None, filter_func = None):
+        if name == None and filter_func == None:
+            #@register.filter()
+            return self.filter_function
+        elif filter_func == None:
+            if(callable(name)):
+                #@register.filter 
+                return self.filter_function(name)
+            else:
+                #@register.filter('somename') or @register.filter(name='somename')
+                def dec(func):
+                    return self.filter(name, func)
+                return dec
+        elif name != None and filter_func != None:
+            #register.filter('somename', somefunc)
+            self.filters[name] = filter_func
+        else:
+            raise InvalidTemplateLibraryException, "Unsupported arguments to Library.filter,(%r, %r, %r)", (name, compile_function, has_arg)
 
-def register_filter(filter_name, callback_function, has_arg):
-    registered_filters[filter_name] = (callback_function, has_arg)
+    def filter_function(self, func):
+        self.filters[func.__name__] = func
+        return func
+    
+    def simple_tag(self,func):
+        (params, xx, xxx, defaults) = getargspec(func)
+    
+        class SimpleNode(Node):
+            def __init__(self, vars_to_resolve):
+                self.vars_to_resolve = vars_to_resolve
+    
+            def render(self, context):
+                resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve]
+                return func(*resolved_vars)
+    
+        compile_func = curry(generic_tag_compiler, params, defaults, func.__name__, SimpleNode)
+        compile_func.__doc__ = func.__doc__
+        self.tag(func.__name__, compile_func)
+        return func
 
-def unregister_filter(filter_name):
-    del registered_filters[filter_name]
+    def inclusion_tag(self, file_name, context_class=Context, takes_context=False):
+        def dec(func):
+            (params, xx, xxx, defaults) = getargspec(func)
+            if takes_context:
+                if params[0] == 'context':
+                    params = params[1:]
+                else:
+                    raise TemplateSyntaxError, "Any tag function decorated with takes_context=True must have a first argument of 'context'"
+    
+            class InclusionNode(Node):
+                def __init__(self, vars_to_resolve):
+                    self.vars_to_resolve = vars_to_resolve
+    
+                def render(self, context):
+                    resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve]
+                    if takes_context:
+                        args = [context] + resolved_vars
+                    else:
+                        args = resolved_vars
+    
+                    dict = func(*args)
+    
+                    if not getattr(self, 'nodelist', False):
+                        from django.core.template_loader import get_template
+                        t = get_template(file_name)
+                        self.nodelist = t.nodelist
+                    return self.nodelist.render(context_class(dict))
+    
+            compile_func = curry(generic_tag_compiler, params, defaults, func.__name__, InclusionNode)
+            compile_func.__doc__ = func.__doc__
+            self.tag(func.__name__, compile_func)
+            return func
+        return dec
 
-import defaulttags
-import defaultfilters
+def get_library(module_name):
+    lib = libraries.get(module_name, None)
+    if not lib:
+        try:
+            mod = __import__(module_name, '', '', [''])
+        except ImportError, e:
+            raise InvalidTemplateLibraryException, \
+              "Could not load template library from %s, %s"  % (module_name, e)
+        for k, v in mod.__dict__.items():
+            if isinstance(v, Library):
+                lib = v
+                libraries[module_name] = lib
+                break
+    if not lib:
+        raise InvalidTemplateLibraryException, \
+              "Template library %s does not have a Library member"  % module_name
+    return lib
+
+def add_to_builtins(module_name):
+    builtins.append(get_library(module_name))
+
+add_to_builtins('django.core.template.defaulttags')
+add_to_builtins('django.core.template.defaultfilters')
\ No newline at end of file
=== django/core/template/defaulttags.py
==================================================================
--- django/core/template/defaulttags.py	(/django/trunk)	(revision 3632)
+++ django/core/template/defaulttags.py	(/local/template-library)	(revision 3632)
@@ -1,9 +1,12 @@
 "Default tags used by the template system, available to all templates."
 
-from django.core.template import Node, NodeList, Template, Context, resolve_variable, resolve_variable_with_filters, get_filters_from_token, registered_filters
-from django.core.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, register_tag
+from django.core.template import Node, NodeList, Template, Context, resolve_variable
+from django.core.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END
+from django.core.template import get_library, Library, InvalidTemplateLibraryException
 import sys
 
+register = Library()
+
 class CommentNode(Node):
     def render(self, context):
         return ''
@@ -27,15 +30,13 @@
         return ''.join(output)
 
 class FilterNode(Node):
-    def __init__(self, filters, nodelist):
-        self.filters, self.nodelist = filters, nodelist
+    def __init__(self, filter_expr, nodelist):
+        self.filter_expr, self.nodelist = filter_expr, nodelist
 
     def render(self, context):
         output = self.nodelist.render(context)
         # apply filters
-        for f in self.filters:
-            output = registered_filters[f[0]][0](output, f[1])
-        return output
+        return filter_expr.resolve(Context({'var': output})) 
 
 class FirstOfNode(Node):
     def __init__(self, vars):
@@ -81,7 +82,7 @@
             parentloop = {}
         context.push()
         try:
-            values = resolve_variable_with_filters(self.sequence, context)
+            values = self.sequence.resolve(context)
         except VariableDoesNotExist:
             values = []
         if values is None:
@@ -147,8 +148,8 @@
         return self.nodelist_false.render(context)
 
 class IfNode(Node):
-    def __init__(self, boolvars, nodelist_true, nodelist_false):
-        self.boolvars = boolvars
+    def __init__(self, bool_exprs, nodelist_true, nodelist_false):
+        self.bool_exprs = bool_exprs
         self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
 
     def __repr__(self):
@@ -169,9 +170,9 @@
         return nodes
 
     def render(self, context):
-        for ifnot, boolvar in self.boolvars:
+        for ifnot, bool_expr in self.bool_exprs:
             try:
-                value = resolve_variable_with_filters(boolvar, context)
+                value = bool_expr.resolve(context)
             except VariableDoesNotExist:
                 value = None
             if (value and not ifnot) or (ifnot and not value):
@@ -179,19 +180,18 @@
         return self.nodelist_false.render(context)
 
 class RegroupNode(Node):
-    def __init__(self, target_var, expression, var_name):
-        self.target_var, self.expression = target_var, expression
+    def __init__(self, target, expression, var_name):
+        self.target, self.expression = target, expression
         self.var_name = var_name
 
     def render(self, context):
-        obj_list = resolve_variable_with_filters(self.target_var, context)
+        obj_list = self.target.resolve(context)
         if obj_list == '': # target_var wasn't found in context; fail silently
             context[self.var_name] = []
             return ''
         output = [] # list of dictionaries in the format {'grouper': 'key', 'list': [list of contents]}
         for obj in obj_list:
-            grouper = resolve_variable_with_filters('var.%s' % self.expression, \
-                Context({'var': obj}))
+            grouper = self.expression.resolve(Context({'var': obj}))
             # TODO: Is this a sensible way to determine equality?
             if output and repr(output[-1]['grouper']) == repr(grouper):
                 output[-1]['list'].append(obj)
@@ -236,21 +236,7 @@
         return output
 
 class LoadNode(Node):
-    def __init__(self, taglib):
-        self.taglib = taglib
-
-    def load_taglib(taglib):
-        mod = __import__("django.templatetags.%s" % taglib.split('.')[-1], '', '', [''])
-        reload(mod)
-        return mod
-    load_taglib = staticmethod(load_taglib)
-
     def render(self, context):
-        "Import the relevant module"
-        try:
-            self.__class__.load_taglib(self.taglib)
-        except ImportError:
-            pass # Fail silently for invalid loads.
         return ''
 
 class NowNode(Node):
@@ -276,15 +262,15 @@
         return self.mapping.get(self.tagtype, '')
 
 class WidthRatioNode(Node):
-    def __init__(self, val_var, max_var, max_width):
-        self.val_var = val_var
-        self.max_var = max_var
+    def __init__(self, val_expr, max_expr, max_width):
+        self.val_expr = val_expr
+        self.max_expr = max_expr
         self.max_width = max_width
 
     def render(self, context):
         try:
-            value = resolve_variable_with_filters(self.val_var, context)
-            maxvalue = resolve_variable_with_filters(self.max_var, context)
+            value = self.val_expr.resolve(context)
+            maxvalue = self.max_expr.resolve(context)
         except VariableDoesNotExist:
             return ''
         try:
@@ -295,15 +281,18 @@
             return ''
         return str(int(round(ratio)))
 
-def do_comment(parser, token):
+#@register.tag
+def comment(parser, token):
     """
     Ignore everything between ``{% comment %}`` and ``{% endcomment %}``
     """
     nodelist = parser.parse(('endcomment',))
     parser.delete_first_token()
     return CommentNode()
+comment = register.tag(comment)
 
-def do_cycle(parser, token):
+#@register.tag
+def cycle(parser, token):
     """
     Cycle among the given strings each time this tag is encountered
 
@@ -369,11 +358,9 @@
 
     else:
         raise TemplateSyntaxError("Invalid arguments to 'cycle': %s" % args)
+cycle = register.tag(cycle)
 
-def do_debug(parser, token):
-    "Print a whole load of debugging information, including the context and imported modules"
-    return DebugNode()
-
+#@register.tag(name="filter")
 def do_filter(parser, token):
     """
     Filter the contents of the blog through variable filters.
@@ -388,12 +375,14 @@
         {% endfilter %}
     """
     _, rest = token.contents.split(None, 1)
-    _, filters = get_filters_from_token('var|%s' % rest)
+    filter_expr = parser.compile_filter("var|%s" % (rest))
     nodelist = parser.parse(('endfilter',))
     parser.delete_first_token()
-    return FilterNode(filters, nodelist)
+    return FilterNode(filter_expr, nodelist)
+filter = register.tag("filter", do_filter)
 
-def do_firstof(parser, token):
+#@register.tag
+def firstof(parser, token):
     """
     Outputs the first variable passed that is not False.
 
@@ -419,8 +408,9 @@
     if len(bits) < 1:
         raise TemplateSyntaxError, "'firstof' statement requires at least one argument"
     return FirstOfNode(bits)
+firstof = register.tag(firstof)
 
-
+#@register.tag(name="for")
 def do_for(parser, token):
     """
     Loop over each item in an array.
@@ -462,11 +452,12 @@
     if bits[2] != 'in':
         raise TemplateSyntaxError, "'for' statement must contain 'in' as the second word: %s" % token.contents
     loopvar = bits[1]
-    sequence = bits[3]
+    sequence = parser.compile_filter(bits[3])
     reversed = (len(bits) == 5)
     nodelist_loop = parser.parse(('endfor',))
     parser.delete_first_token()
     return ForNode(loopvar, sequence, reversed, nodelist_loop)
+do_for = register.tag("for", do_for)
 
 def do_ifequal(parser, token, negate):
     """
@@ -497,6 +488,17 @@
         nodelist_false = NodeList()
     return IfEqualNode(bits[1], bits[2], nodelist_true, nodelist_false, negate)
 
+#@register.tag
+def ifequal(parser, token):
+    return do_ifequal(parser, token, False)
+ifequal = register.tag(ifequal)
+
+#@register.tag
+def ifnotequal(parser, token):
+    return do_ifequal(parser, token, True)
+ifnotequal = register.tag(ifnotequal)
+
+#@register.tag(name="if")
 def do_if(parser, token):
     """
     The ``{% if %}`` tag evaluates a variable, and if that variable is "true"
@@ -554,9 +556,9 @@
             not_, boolvar = boolpair.split()
             if not_ != 'not':
                 raise TemplateSyntaxError, "Expected 'not' in if statement"
-            boolvars.append((True, boolvar))
+            boolvars.append((True, parser.compile_filter(boolvar)))
         else:
-            boolvars.append((False, boolpair))
+            boolvars.append((False, parser.compile_filter(boolpair)))
     nodelist_true = parser.parse(('else', 'endif'))
     token = parser.next_token()
     if token.contents == 'else':
@@ -565,8 +567,10 @@
     else:
         nodelist_false = NodeList()
     return IfNode(boolvars, nodelist_true, nodelist_false)
+do_if = register.tag("if", do_if)
 
-def do_ifchanged(parser, token):
+#@register.tag
+def ifchanged(parser, token):
     """
     Check if a value has changed from the last iteration of a loop.
 
@@ -587,8 +591,10 @@
     nodelist = parser.parse(('endifchanged',))
     parser.delete_first_token()
     return IfChangedNode(nodelist)
+ifchanged = register.tag(ifchanged)
 
-def do_ssi(parser, token):
+#@register.tag
+def ssi(parser, token):
     """
     Output the contents of a given file into the page.
 
@@ -613,8 +619,10 @@
         else:
             raise TemplateSyntaxError, "Second (optional) argument to %s tag must be 'parsed'" % bits[0]
     return SsiNode(bits[1], parsed)
+ssi = register.tag(ssi)
 
-def do_load(parser, token):
+#@register.tag
+def load(parser, token):
     """
     Load a custom template tag set.
 
@@ -623,17 +631,18 @@
         {% load news.photos %}
     """
     bits = token.contents.split()
-    if len(bits) != 2:
-        raise TemplateSyntaxError, "'load' statement takes one argument"
-    taglib = bits[1]
-    # check at compile time that the module can be imported
-    try:
-        LoadNode.load_taglib(taglib)
-    except ImportError, e:
-        raise TemplateSyntaxError, "'%s' is not a valid tag library: %s" % (taglib, e)
-    return LoadNode(taglib)
+    for taglib in bits[1:]:
+        # add the library to the parser
+        try:
+            lib = get_library("django.templatetags.%s" % taglib.split('.')[-1])
+            parser.add_library(lib)
+        except InvalidTemplateLibraryException, e:
+            raise TemplateSyntaxError, "'%s' is not a valid tag library: %s" % (taglib, e)
+    return LoadNode()
+load = register.tag(load)
 
-def do_now(parser, token):
+#@register.tag
+def now(parser, token):
     """
     Display the date, formatted according to the given string.
 
@@ -649,8 +658,10 @@
         raise TemplateSyntaxError, "'now' statement takes one argument"
     format_string = bits[1]
     return NowNode(format_string)
+now = register.tag(now)
 
-def do_regroup(parser, token):
+#@register.tag
+def regroup(parser, token):
     """
     Regroup a list of alike objects by a common attribute.
 
@@ -699,17 +710,21 @@
     firstbits = token.contents.split(None, 3)
     if len(firstbits) != 4:
         raise TemplateSyntaxError, "'regroup' tag takes five arguments"
-    target_var = firstbits[1]
+    target = parser.compile_filter(firstbits[1])
     if firstbits[2] != 'by':
         raise TemplateSyntaxError, "second argument to 'regroup' tag must be 'by'"
     lastbits_reversed = firstbits[3][::-1].split(None, 2)
     if lastbits_reversed[1][::-1] != 'as':
         raise TemplateSyntaxError, "next-to-last argument to 'regroup' tag must be 'as'"
-    expression = lastbits_reversed[2][::-1]
+    
+    expression = parser.compile_filters('var.%s' % lastbits_reversed[2][::-1])
+    
     var_name = lastbits_reversed[0][::-1]
-    return RegroupNode(target_var, expression, var_name)
+    return RegroupNode(target, expression, var_name)
+regroup = register.tag(regroup)
 
-def do_templatetag(parser, token):
+#@register.tag
+def templatetag(parser, token):
     """
     Output one of the bits used to compose template tags.
 
@@ -735,8 +750,10 @@
         raise TemplateSyntaxError, "Invalid templatetag argument: '%s'. Must be one of: %s" % \
             (tag, TemplateTagNode.mapping.keys())
     return TemplateTagNode(tag)
+templatetag = register.tag(templatetag)
 
-def do_widthratio(parser, token):
+@register.tag
+def widthratio(parser, token):
     """
     For creating bar charts and such, this tag calculates the ratio of a given
     value to a maximum value, and then applies that ratio to a constant.
@@ -752,26 +769,11 @@
     bits = token.contents.split()
     if len(bits) != 4:
         raise TemplateSyntaxError("widthratio takes three arguments")
-    tag, this_value_var, max_value_var, max_width = bits
+    tag, this_value_expr, max_value_expr, max_width = bits
     try:
         max_width = int(max_width)
     except ValueError:
         raise TemplateSyntaxError("widthratio final argument must be an integer")
-    return WidthRatioNode(this_value_var, max_value_var, max_width)
-
-register_tag('comment', do_comment)
-register_tag('cycle', do_cycle)
-register_tag('debug', do_debug)
-register_tag('filter', do_filter)
-register_tag('firstof', do_firstof)
-register_tag('for', do_for)
-register_tag('ifequal', lambda parser, token: do_ifequal(parser, token, False))
-register_tag('ifnotequal', lambda parser, token: do_ifequal(parser, token, True))
-register_tag('if', do_if)
-register_tag('ifchanged', do_ifchanged)
-register_tag('regroup', do_regroup)
-register_tag('ssi', do_ssi)
-register_tag('load', do_load)
-register_tag('now', do_now)
-register_tag('templatetag', do_templatetag)
-register_tag('widthratio', do_widthratio)
+    return WidthRatioNode(parser.compile_filter(this_value_expr), 
+                          parser.compile_filter(max_value_expr), max_width)
+widthratio = register.tag(widthratio)
=== django/core/template/loader_tags.py
==================================================================
--- django/core/template/loader_tags.py	(/django/trunk)	(revision 3632)
+++ django/core/template/loader_tags.py	(/local/template-library)	(revision 3632)
@@ -0,0 +1,172 @@
+from django.core.template import TemplateSyntaxError, TemplateDoesNotExist, resolve_variable
+from django.core.template import Library, Context, Node
+from django.core.template.loader import get_template, get_template_from_string, find_template_source
+from django.conf.settings import TEMPLATE_DEBUG
+register = Library()
+
+class ExtendsError(Exception):
+    pass
+
+class BlockNode(Node):
+    def __init__(self, name, nodelist, parent=None):
+        self.name, self.nodelist, self.parent = name, nodelist, parent
+
+    def __repr__(self):
+        return "<Block Node: %s. Contents: %r>" % (self.name, self.nodelist)
+
+    def render(self, context):
+        context.push()
+        # Save context in case of block.super().
+        self.context = context
+        context['block'] = self
+        result = self.nodelist.render(context)
+        context.pop()
+        return result
+
+    def super(self):
+        if self.parent:
+            return self.parent.render(self.context)
+        return ''
+
+    def add_parent(self, nodelist):
+        if self.parent:
+            self.parent.add_parent(nodelist)
+        else:
+            self.parent = BlockNode(self.name, nodelist)
+
+class ExtendsNode(Node):
+    def __init__(self, nodelist, parent_name, parent_name_expr, template_dirs=None):
+        self.nodelist = nodelist
+        self.parent_name, self.parent_name_expr = parent_name, parent_name_expr
+        self.template_dirs = template_dirs
+
+    def get_parent(self, context):
+        if self.parent_name_expr:
+            self.parent_name = self.parent_name_expr.resolve(context)
+        parent = self.parent_name
+        if not parent:
+            error_msg = "Invalid template name in 'extends' tag: %r." % parent
+            if self.parent_name_expr:
+                error_msg += " Got this from the %r variable." % self.parent_name_expr #TODO nice repr. 
+            raise TemplateSyntaxError, error_msg
+        try:
+            return get_template_from_string(*find_template_source(parent, self.template_dirs))
+        except TemplateDoesNotExist:
+            raise TemplateSyntaxError, "Template %r cannot be extended, because it doesn't exist" % parent
+
+    def render(self, context):
+        compiled_parent = self.get_parent(context)
+        parent_is_child = isinstance(compiled_parent.nodelist[0], ExtendsNode)
+        parent_blocks = dict([(n.name, n) for n in compiled_parent.nodelist.get_nodes_by_type(BlockNode)])
+        for block_node in self.nodelist.get_nodes_by_type(BlockNode):
+            # Check for a BlockNode with this node's name, and replace it if found.
+            try:
+                parent_block = parent_blocks[block_node.name]
+            except KeyError:
+                # This BlockNode wasn't found in the parent template, but the
+                # parent block might be defined in the parent's *parent*, so we
+                # add this BlockNode to the parent's ExtendsNode nodelist, so
+                # it'll be checked when the parent node's render() is called.
+                if parent_is_child:
+                    compiled_parent.nodelist[0].nodelist.append(block_node)
+            else:
+                # Keep any existing parents and add a new one. Used by BlockNode.
+                parent_block.parent = block_node.parent
+                parent_block.add_parent(parent_block.nodelist)
+                parent_block.nodelist = block_node.nodelist
+        return compiled_parent.render(context)
+
+class ConstantIncludeNode(Node):
+    def __init__(self, template_path):
+        try:
+            t = get_template(template_path)
+            self.template = t
+        except Exception, e:
+            if TEMPLATE_DEBUG:
+                raise
+            self.template = None
+
+    def render(self, context):
+        if self.template:
+            return self.template.render(context)
+        else:
+            return ''
+
+class IncludeNode(Node):
+    def __init__(self, template_name):
+        self.template_name = template_name
+
+    def render(self, context):
+         try:
+             template_name = resolve_variable(self.template_name, context)
+             t = get_template(template_name)
+             return t.render(context)
+         except TemplateSyntaxError, e:
+             if TEMPLATE_DEBUG:
+                 raise
+             return ''
+         except:
+             return '' # Fail silently for invalid included templates.
+
+def do_block(parser, token):
+    """
+    Define a block that can be overridden by child templates.
+    """
+    bits = token.contents.split()
+    if len(bits) != 2:
+        raise TemplateSyntaxError, "'%s' tag takes only one argument" % bits[0]
+    block_name = bits[1]
+    # Keep track of the names of BlockNodes found in this template, so we can
+    # check for duplication.
+    try:
+        if block_name in parser.__loaded_blocks:
+            raise TemplateSyntaxError, "'%s' tag with name '%s' appears more than once" % (bits[0], block_name)
+        parser.__loaded_blocks.append(block_name)
+    except AttributeError: # parser._loaded_blocks isn't a list yet
+        parser.__loaded_blocks = [block_name]
+    nodelist = parser.parse(('endblock',))
+    parser.delete_first_token()
+    return BlockNode(block_name, nodelist)
+
+def do_extends(parser, token):
+    """
+    Signal that this template extends a parent template.
+
+    This tag may be used in two ways: ``{% extends "base" %}`` (with quotes)
+    uses the literal value "base" as the name of the parent template to extend,
+    or ``{% extends variable %}`` uses the value of ``variable`` as the name
+    of the parent template to extend.
+    """
+    bits = token.contents.split()
+    if len(bits) != 2:
+        raise TemplateSyntaxError, "'%s' takes one argument" % bits[0]
+    parent_name, parent_name_expr = None, None
+    if bits[1][0] in ('"', "'") and bits[1][-1] == bits[1][0]:
+        parent_name = bits[1][1:-1]
+    else:
+        parent_name_expr = parser.compile_filter(bits[1])
+    nodelist = parser.parse()
+    if nodelist.get_nodes_by_type(ExtendsNode):
+        raise TemplateSyntaxError, "'%s' cannot appear more than once in the same template" % bits[0]
+    return ExtendsNode(nodelist, parent_name, parent_name_expr)
+
+def do_include(parser, token):
+    """
+    Loads a template and renders it with the current context.
+
+    Example::
+
+        {% include "foo/some_include" %}
+    """
+
+    bits = token.contents.split()
+    if len(bits) != 2:
+        raise TemplateSyntaxError, "%r tag takes one argument: the name of the template to be included" % bits[0]
+    path = bits[1]
+    if path[0] in ('"', "'") and path[-1] == path[0]:
+        return ConstantIncludeNode(path[1:-1])
+    return IncludeNode(bits[1])
+
+register.tag('block', do_block)
+register.tag('extends', do_extends)
+register.tag('include', do_include)
\ No newline at end of file
=== django/core/template/loader.py
==================================================================
--- django/core/template/loader.py	(/django/trunk)	(revision 3632)
+++ django/core/template/loader.py	(/local/template-library)	(revision 3632)
@@ -21,7 +21,7 @@
 # installed, because pkg_resources is necessary to read eggs.
 
 from django.core.exceptions import ImproperlyConfigured
-from django.core.template import Origin, StringOrigin, Template, Context, Node, TemplateDoesNotExist, TemplateSyntaxError, resolve_variable_with_filters, resolve_variable, register_tag
+from django.core.template import Origin, StringOrigin, Template,  TemplateDoesNotExist, add_to_builtins
 from django.conf.settings import TEMPLATE_LOADERS, TEMPLATE_DEBUG
 
 template_source_loaders = []
@@ -68,9 +68,6 @@
 def load_template_source(name, dirs=None):
     find_template_source(name, dirs)[0]
 
-class ExtendsError(Exception):
-    pass
-
 def get_template(template_name):
     """
     Returns a compiled Template object for the given template name,
@@ -113,166 +110,5 @@
     # If we get here, none of the templates could be loaded
     raise TemplateDoesNotExist, ', '.join(template_name_list)
 
-class BlockNode(Node):
-    def __init__(self, name, nodelist, parent=None):
-        self.name, self.nodelist, self.parent = name, nodelist, parent
-
-    def __repr__(self):
-        return "<Block Node: %s. Contents: %r>" % (self.name, self.nodelist)
-
-    def render(self, context):
-        context.push()
-        # Save context in case of block.super().
-        self.context = context
-        context['block'] = self
-        result = self.nodelist.render(context)
-        context.pop()
-        return result
-
-    def super(self):
-        if self.parent:
-            return self.parent.render(self.context)
-        return ''
-
-    def add_parent(self, nodelist):
-        if self.parent:
-            self.parent.add_parent(nodelist)
-        else:
-            self.parent = BlockNode(self.name, nodelist)
-
-class ExtendsNode(Node):
-    def __init__(self, nodelist, parent_name, parent_name_var, template_dirs=None):
-        self.nodelist = nodelist
-        self.parent_name, self.parent_name_var = parent_name, parent_name_var
-        self.template_dirs = template_dirs
-
-    def get_parent(self, context):
-        if self.parent_name_var:
-            self.parent_name = resolve_variable_with_filters(self.parent_name_var, context)
-        parent = self.parent_name
-        if not parent:
-            error_msg = "Invalid template name in 'extends' tag: %r." % parent
-            if self.parent_name_var:
-                error_msg += " Got this from the %r variable." % self.parent_name_var
-            raise TemplateSyntaxError, error_msg
-        try:
-            return get_template_from_string(*find_template_source(parent, self.template_dirs))
-        except TemplateDoesNotExist:
-            raise TemplateSyntaxError, "Template %r cannot be extended, because it doesn't exist" % parent
-
-    def render(self, context):
-        compiled_parent = self.get_parent(context)
-        parent_is_child = isinstance(compiled_parent.nodelist[0], ExtendsNode)
-        parent_blocks = dict([(n.name, n) for n in compiled_parent.nodelist.get_nodes_by_type(BlockNode)])
-        for block_node in self.nodelist.get_nodes_by_type(BlockNode):
-            # Check for a BlockNode with this node's name, and replace it if found.
-            try:
-                parent_block = parent_blocks[block_node.name]
-            except KeyError:
-                # This BlockNode wasn't found in the parent template, but the
-                # parent block might be defined in the parent's *parent*, so we
-                # add this BlockNode to the parent's ExtendsNode nodelist, so
-                # it'll be checked when the parent node's render() is called.
-                if parent_is_child:
-                    compiled_parent.nodelist[0].nodelist.append(block_node)
-            else:
-                # Keep any existing parents and add a new one. Used by BlockNode.
-                parent_block.parent = block_node.parent
-                parent_block.add_parent(parent_block.nodelist)
-                parent_block.nodelist = block_node.nodelist
-        return compiled_parent.render(context)
-
-class ConstantIncludeNode(Node):
-    def __init__(self, template_path):
-        try:
-            t = get_template(template_path)
-            self.template = t
-        except Exception, e:
-            if TEMPLATE_DEBUG:
-                raise
-            self.template = None
-
-    def render(self, context):
-        if self.template:
-            return self.template.render(context)
-        else:
-            return ''
-
-class IncludeNode(Node):
-    def __init__(self, template_name):
-        self.template_name = template_name
-
-    def render(self, context):
-         try:
-             template_name = resolve_variable(self.template_name, context)
-             t = get_template(template_name)
-             return t.render(context)
-         except TemplateSyntaxError, e:
-             if TEMPLATE_DEBUG:
-                 raise
-             return ''
-         except:
-             return '' # Fail silently for invalid included templates.
-
-def do_block(parser, token):
-    """
-    Define a block that can be overridden by child templates.
-    """
-    bits = token.contents.split()
-    if len(bits) != 2:
-        raise TemplateSyntaxError, "'%s' tag takes only one argument" % bits[0]
-    block_name = bits[1]
-    # Keep track of the names of BlockNodes found in this template, so we can
-    # check for duplication.
-    try:
-        if block_name in parser.__loaded_blocks:
-            raise TemplateSyntaxError, "'%s' tag with name '%s' appears more than once" % (bits[0], block_name)
-        parser.__loaded_blocks.append(block_name)
-    except AttributeError: # parser._loaded_blocks isn't a list yet
-        parser.__loaded_blocks = [block_name]
-    nodelist = parser.parse(('endblock',))
-    parser.delete_first_token()
-    return BlockNode(block_name, nodelist)
-
-def do_extends(parser, token):
-    """
-    Signal that this template extends a parent template.
-
-    This tag may be used in two ways: ``{% extends "base" %}`` (with quotes)
-    uses the literal value "base" as the name of the parent template to extend,
-    or ``{% extends variable %}`` uses the value of ``variable`` as the name
-    of the parent template to extend.
-    """
-    bits = token.contents.split()
-    if len(bits) != 2:
-        raise TemplateSyntaxError, "'%s' takes one argument" % bits[0]
-    parent_name, parent_name_var = None, None
-    if bits[1][0] in ('"', "'") and bits[1][-1] == bits[1][0]:
-        parent_name = bits[1][1:-1]
-    else:
-        parent_name_var = bits[1]
-    nodelist = parser.parse()
-    if nodelist.get_nodes_by_type(ExtendsNode):
-        raise TemplateSyntaxError, "'%s' cannot appear more than once in the same template" % bits[0]
-    return ExtendsNode(nodelist, parent_name, parent_name_var)
-
-def do_include(parser, token):
-    """
-    Loads a template and renders it with the current context.
-
-    Example::
-
-        {% include "foo/some_include" %}
-    """
-
-    bits = token.contents.split()
-    if len(bits) != 2:
-        raise TemplateSyntaxError, "%r tag takes one argument: the name of the template to be included" % bits[0]
-    path = bits[1]
-    if path[0] in ('"', "'") and path[-1] == path[0]:
-        return ConstantIncludeNode(path[1:-1])
-    return IncludeNode(bits[1])
-
-register_tag('block', do_block)
-register_tag('extends', do_extends)
-register_tag('include', do_include)
+    
+add_to_builtins('django.core.template.loader_tags')
\ No newline at end of file
=== django/core/template/decorators.py
==================================================================
--- django/core/template/decorators.py	(/django/trunk)	(revision 3632)
+++ django/core/template/decorators.py	(/local/template-library)	(revision 3632)
@@ -1,67 +0,0 @@
-from django.core.template import Context, Node, TemplateSyntaxError, register_tag, resolve_variable
-from django.core.template_loader import get_template
-from django.utils.functional import curry
-from inspect import getargspec
-
-def generic_tag_compiler(params, defaults, name, node_class, parser, token):
-    "Returns a template.Node subclass."
-    bits = token.contents.split()[1:]
-    bmax = len(params)
-    def_len = defaults and len(defaults) or 0
-    bmin = bmax - def_len
-    if(len(bits) < bmin or len(bits) > bmax):
-        if bmin == bmax:
-            message = "%s takes %s arguments" % (name, bmin)
-        else:
-            message = "%s takes between %s and %s arguments" % (name, bmin, bmax)
-        raise TemplateSyntaxError, message
-    return node_class(bits)
-
-def simple_tag(func):
-    (params, xx, xxx, defaults) = getargspec(func)
-
-    class SimpleNode(Node):
-        def __init__(self, vars_to_resolve):
-            self.vars_to_resolve = vars_to_resolve
-
-        def render(self, context):
-            resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve]
-            return func(*resolved_vars)
-
-    compile_func = curry(generic_tag_compiler, params, defaults, func.__name__, SimpleNode)
-    compile_func.__doc__ = func.__doc__
-    register_tag(func.__name__, compile_func)
-    return func
-
-def inclusion_tag(file_name, context_class=Context, takes_context=False):
-    def dec(func):
-        (params, xx, xxx, defaults) = getargspec(func)
-        if takes_context:
-            if params[0] == 'context':
-                params = params[1:]
-            else:
-                raise TemplateSyntaxError, "Any tag function decorated with takes_context=True must have a first argument of 'context'"
-
-        class InclusionNode(Node):
-            def __init__(self, vars_to_resolve):
-                self.vars_to_resolve = vars_to_resolve
-
-            def render(self, context):
-                resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve]
-                if takes_context:
-                    args = [context] + resolved_vars
-                else:
-                    args = resolved_vars
-
-                dict = func(*args)
-
-                if not getattr(self, 'nodelist', False):
-                    t = get_template(file_name)
-                    self.nodelist = t.nodelist
-                return self.nodelist.render(context_class(dict))
-
-        compile_func = curry(generic_tag_compiler, params, defaults, func.__name__, InclusionNode)
-        compile_func.__doc__ = func.__doc__
-        register_tag(func.__name__, compile_func)
-        return func
-    return dec

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
==================================================================
--- django/templatetags/i18n.py	(/django/trunk)	(revision 3632)
+++ django/templatetags/i18n.py	(/local/template-library)	(revision 3632)
@@ -1,9 +1,11 @@
-from django.core.template import Node, NodeList, Template, Context, resolve_variable, resolve_variable_with_filters, registered_filters
-from django.core.template import TemplateSyntaxError, register_tag, TokenParser
+from django.core.template import Node, NodeList, Template, Context, resolve_variable
+from django.core.template import TemplateSyntaxError, TokenParser, Library
 from django.core.template import TOKEN_BLOCK, TOKEN_TEXT, TOKEN_VAR
 from django.utils import translation
 import re, sys
 
+register = Library()
+
 class GetAvailableLanguagesNode(Node):
     def __init__(self, variable):
         self.variable = variable
@@ -53,10 +55,10 @@
     def render(self, context):
         context.push()
         for var,val in self.extra_context.items():
-            context[var] = resolve_variable_with_filters(val, context)
+            context[var] = val.resolve(context)
         singular = self.render_token_list(self.singular)
         if self.plural and self.countervar and self.counter:
-            count = resolve_variable_with_filters(self.counter, context)
+            count = self.counter.resolve(context)
             context[self.countervar] = count
             plural = self.render_token_list(self.plural)
             result = translation.ngettext(singular, plural, count) % context
@@ -179,9 +181,9 @@
                     value = self.value()
                     if self.tag() != 'as':
                         raise TemplateSyntaxError, "variable bindings in 'blocktrans' must be 'with value as variable'"
-                    extra_context[self.tag()] = value
+                    extra_context[self.tag()] = parser.compile_filter(value)
                 elif tag == 'count':
-                    counter = self.value()
+                    counter = parser.compile_filter(self.value())
                     if self.tag() != 'as':
                         raise TemplateSyntaxError, "counter specification in 'blocktrans' must be 'count value as variable'"
                     countervar = self.tag()
@@ -191,6 +193,7 @@
 
     (countervar, counter, extra_context) = BlockTranslateParser(token.contents).top()
 
+
     singular = []
     plural = []
     while parser.tokens:
@@ -213,7 +216,7 @@
 
     return BlockTranslateNode(extra_context, singular, plural, countervar, counter)
 
-register_tag('get_available_languages', do_get_available_languages)
-register_tag('get_current_language', do_get_current_language)
-register_tag('trans', do_translate)
-register_tag('blocktrans', do_block_translate)
+register.tag('get_available_languages', do_get_available_languages)
+register.tag('get_current_language', do_get_current_language)
+register.tag('trans', do_translate)
+register.tag('blocktrans', do_block_translate)
=== tests/testapp/templatetags/testtags.py
==================================================================
--- tests/testapp/templatetags/testtags.py	(/django/trunk)	(revision 3632)
+++ tests/testapp/templatetags/testtags.py	(/local/template-library)	(revision 3632)
@@ -2,6 +2,8 @@
 
 from django.core import template
 
+register = template.Library()
+
 class EchoNode(template.Node):
     def __init__(self, contents):
         self.contents = contents
@@ -11,5 +13,5 @@
         
 def do_echo(parser, token):
     return EchoNode(token.contents.split()[1:])
-    
-template.register_tag("echo", do_echo)
\ No newline at end of file
+
+register.tag("echo", do_echo)
\ No newline at end of file
=== tests/othertests/defaultfilters.py
==================================================================
--- tests/othertests/defaultfilters.py	(/django/trunk)	(revision 3632)
+++ tests/othertests/defaultfilters.py	(/local/template-library)	(revision 3632)
@@ -1,15 +1,15 @@
 """
->>> floatformat(7.7, None)
+>>> floatformat(7.7)
 '7.7'
->>> floatformat(7.0, None)
+>>> floatformat(7.0)
 '7'
->>> floatformat(0.7, None)
+>>> floatformat(0.7)
 '0.7'
->>> floatformat(0.07, None)
+>>> floatformat(0.07)
 '0.1'
->>> floatformat(0.007, None)
+>>> floatformat(0.007)
 '0.0'
->>> floatformat(0.0, None)
+>>> floatformat(0.0)
 '0'
 """
 
=== tests/othertests/templates.py
==================================================================
--- tests/othertests/templates.py	(/django/trunk)	(revision 3632)
+++ tests/othertests/templates.py	(/local/template-library)	(revision 3632)
@@ -102,6 +102,12 @@
     #Escaped string as argument
     'basic-syntax30': (r"""{{ var|default_if_none:" endquote\" hah" }}""", {"var": None}, ' endquote" hah'),
 
+    # Variable as argument
+    'basic-syntax31': (r"""{{ var|default_if_none:var2 }}""", {"var":None, "var2": "happy"  }, 'happy'),
+
+    #Default argument testing 
+    'basic-syntax32' : (r"""{{ var|yesno:"yup,nup,mup" }} {{var|yesno}}""", {"var": True}, 'yup yes'),
+
     ### IF TAG ################################################################
     'if-tag01': ("{% if foo %}yes{% else %}no{% endif %}", {"foo": True}, "yes"),
     'if-tag02': ("{% if foo %}yes{% else %}no{% endif %}", {"foo": False}, "no"),
=== tests/othertests/markup.py
==================================================================
--- tests/othertests/markup.py	(/django/trunk)	(revision 3632)
+++ tests/othertests/markup.py	(/local/template-library)	(revision 3632)
@@ -1,8 +1,9 @@
 # Quick tests for the markup templatetags (django.contrib.markup)
 
-from django.core.template import Template, Context
-import django.contrib.markup.templatetags.markup # this registers the filters
+from django.core.template import Template, Context, add_to_builtins
 
+add_to_builtins('django.contrib.markup.templatetags.markup')
+
 # find out if markup modules are installed and tailor the test appropriately
 try:
     import textile
=== docs/templates_python.txt
==================================================================
--- docs/templates_python.txt	(/django/trunk)	(revision 3632)
+++ docs/templates_python.txt	(/local/template-library)	(revision 3632)
@@ -444,6 +444,15 @@
 Once you've created that Python module, you'll just have to write a bit of
 Python code, depending on whether you're writing filters or tags.
 
+To be a valid tag library, a module must have a member that is a template.Library
+instance, in which all the tags and filters are registered. So at the top of your 
+module, put the following:
+
+	from django.core import template
+	register = template.Library()
+
+It is convention to call this instance ``register``. 
+
 .. admonition:: Behind the scenes
 
     For a ton of examples, read the source code for Django's default filters
@@ -453,10 +462,11 @@
 Writing custom template filters
 -------------------------------
 
-Custom filters are just Python functions that take two arguments:
+Custom filters are just Python functions that take one or two arguments:
 
     * The value of the variable (input) -- not necessarily a string
-    * The value of the argument -- always a string
+    * The value of the argument -- this can have a default value, or be left 
+    	                           out altogether 
 
 Filter functions should always return something. They shouldn't raise
 exceptions. They should fail silently. In case of error, they should return
@@ -468,33 +478,37 @@
         "Removes all values of arg from the given string"
         return value.replace(arg, '')
 
-Most filters don't take arguments. For filters that don't take arguments, the
-convention is to use a single underscore as the second argument to the filter
-definition. Example::
+Most filters don't take arguments. Example::
 
-    def lower(value, _):
+    def lower(value):
         "Converts a string into all lowercase"
         return value.lower()
 
-When you've written your filter definition, you need to register it, to make it
-available to Django's template language::
+When you've written your filter definition, you need to register it with 
+your Library instance, to make it available to Django's template language::
 
-    from django.core import template
-    template.register_filter('cut', cut, True)
-    template.register_filter('lower', lower, False)
+    register.filter('cut', cut)
+    register.filter('lower', lower)
 
-``register_filter`` takes three arguments:
+	this takes two arguments:
 
     1. The name of the filter -- a string.
     2. The compilation function -- a Python function (not the name of the
        function as a string).
-    3. A boolean, designating whether the filter requires an argument. This
-       tells Django's template parser whether to throw ``TemplateSyntaxError``
-       when filter arguments are given (or missing).
 
-The convention is to put all ``register_filter`` calls at the bottom of your
-template-library module.
+It can also be used as a decorator if you are using python 2.4 or above:
+  
+  	@register.filter(name="cheese")
+  	def cheese_filter(value, arg="Wensleydale"):
+  		return "%s likes %s" % (value, arg)
+  
+	@register.filter
+	def lower(value):
+		return value.lower() 	
 
+If you leave out the name of the filter, as in the second example above, the functions 
+name will be used as the filter name. 
+
 Writing custom template tags
 ----------------------------
 
@@ -525,8 +539,6 @@
 function with the tag contents and the parser object itself. This function is
 responsible for returning a ``Node`` instance based on the contents of the tag.
 
-By convention, the name of each compilation function should start with ``do_``.
-
 For example, let's write a template tag, ``{% current_time %}``, that displays
 the current date/time, formatted according to a parameter given in the tag, in
 `strftime syntax`_. It's a good idea to decide the tag syntax before anything
@@ -612,17 +624,28 @@
 Registering the tag
 ~~~~~~~~~~~~~~~~~~~
 
-Finally, use a ``register_tag`` call, as in ``register_filter`` above. Example::
+Finally, register the tag with your modules Library instance. Example::
 
-    from django.core import template
-    template.register_tag('current_time', do_current_time)
+    register.tag('current_time', do_current_time)
 
-``register_tag`` takes two arguments:
+	this takes two arguments:
 
-    1. The name of the template tag -- a string.
+    1. The name of the template tag -- a string. If this is left out, the 
+       name of the compilation function will be used. 
     2. The compilation function -- a Python function (not the name of the
        function as a string).
 
+As with filter registration, it is also possible to use this as a decorator in 
+Python 2.4 and above:
+
+	@register.tag
+	def shout(parser, token):
+		...
+
+	@register.tag(name="milk")
+	def do_milk(parser, token):
+		...
+
 Setting a variable in the context
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
