diff -urN django-orig/django/contrib/admin/templatetags/admin_modify.py django-new/django/contrib/admin/templatetags/admin_modify.py
--- django-orig/django/contrib/admin/templatetags/admin_modify.py	2007-02-07 14:01:02.000000000 -0800
+++ django-new/django/contrib/admin/templatetags/admin_modify.py	2007-02-07 14:05:21.000000000 -0800
@@ -69,7 +69,7 @@
     default = None
 
     def __init__(self, bound_field_var):
-        self.bound_field_var = bound_field_var
+        self.bound_field_var = template.Variable(bound_field_var)
 
     def get_nodelist(cls, klass):
         if not cls.nodelists.has_key(klass):
@@ -93,7 +93,7 @@
     get_nodelist = classmethod(get_nodelist)
 
     def render(self, context):
-        bound_field = template.resolve_variable(self.bound_field_var, context)
+        bound_field = self.bound_field_var.resolve(context)
 
         context.push()
         context['bound_field'] = bound_field
@@ -153,10 +153,10 @@
 
 class EditInlineNode(template.Node):
     def __init__(self, rel_var):
-        self.rel_var = rel_var
+        self.rel_var = template.Variable(rel_var)
 
     def render(self, context):
-        relation = template.resolve_variable(self.rel_var, context)
+        relation = self.rel_var.resolve(context)
         context.push()
         if relation.field.rel.edit_inline == models.TABULAR:
             bound_related_object_class = TabularBoundRelatedObject
diff -urN django-orig/django/contrib/comments/templatetags/comments.py django-new/django/contrib/comments/templatetags/comments.py
--- django-orig/django/contrib/comments/templatetags/comments.py	2007-02-07 14:01:02.000000000 -0800
+++ django-new/django/contrib/comments/templatetags/comments.py	2007-02-07 14:05:21.000000000 -0800
@@ -18,6 +18,8 @@
         ratings_optional=False, ratings_required=False, rating_options='',
         is_public=True):
         self.content_type = content_type
+        if obj_id_lookup_var is not None:
+            obj_id_lookup_var = template.Variable(obj_id_lookup_var)
         self.obj_id_lookup_var, self.obj_id, self.free = obj_id_lookup_var, obj_id, free
         self.photos_optional, self.photos_required = photos_optional, photos_required
         self.ratings_optional, self.ratings_required = ratings_optional, ratings_required
@@ -30,7 +32,7 @@
         context.push()
         if self.obj_id_lookup_var is not None:
             try:
-                self.obj_id = template.resolve_variable(self.obj_id_lookup_var, context)
+                self.obj_id = self.obj_id_lookup_var.resolve(context)
             except template.VariableDoesNotExist:
                 return ''
             # Validate that this object ID is valid for this content-type.
@@ -73,13 +75,15 @@
     def __init__(self, package, module, context_var_name, obj_id, var_name, free):
         self.package, self.module = package, module
         self.context_var_name, self.obj_id = context_var_name, obj_id
+        if var_name is not None:
+            var_name = template.Variable(var_name)
         self.var_name, self.free = var_name, free
 
     def render(self, context):
         from django.conf import settings
         manager = self.free and FreeComment.objects or Comment.objects
         if self.context_var_name is not None:
-            self.obj_id = template.resolve_variable(self.context_var_name, context)
+            self.obj_id = self.var_name.resolve(context)
         comment_count = manager.filter(object_id__exact=self.obj_id,
             content_type__app_label__exact=self.package,
             content_type__model__exact=self.module, site__id__exact=settings.SITE_ID).count()
@@ -89,6 +93,8 @@
 class CommentListNode(template.Node):
     def __init__(self, package, module, context_var_name, obj_id, var_name, free, ordering, extra_kwargs=None):
         self.package, self.module = package, module
+        if context_var_name is not None:
+            context_var_name = template.Variable(context_var_name)
         self.context_var_name, self.obj_id = context_var_name, obj_id
         self.var_name, self.free = var_name, free
         self.ordering = ordering
@@ -99,7 +105,7 @@
         get_list_function = self.free and FreeComment.objects.filter or Comment.objects.get_list_with_karma
         if self.context_var_name is not None:
             try:
-                self.obj_id = template.resolve_variable(self.context_var_name, context)
+                self.obj_id = self.context_var_name.resolve(context)
             except template.VariableDoesNotExist:
                 return ''
         kwargs = {
diff -urN django-orig/django/template/__init__.py django-new/django/template/__init__.py
--- django-orig/django/template/__init__.py	2007-02-07 14:01:03.000000000 -0800
+++ django-new/django/template/__init__.py	2007-02-07 14:04:26.000000000 -0800
@@ -554,11 +554,11 @@
                 upto = match.end()
         if upto != len(token):
             raise TemplateSyntaxError, "Could not parse the remainder: %s" % token[upto:]
-        self.var, self.filters = var, filters
+        self.var, self.filters = Variable(var), filters
 
     def resolve(self, context, ignore_failures=False):
         try:
-            obj = resolve_variable(self.var, context)
+            obj = self.var.resolve(context)
         except VariableDoesNotExist:
             if ignore_failures:
                 obj = None
@@ -610,43 +610,52 @@
     def __str__(self):
         return self.token
 
-def resolve_variable(path, context):
+
+class Variable(object):
     """
     Returns the resolved variable, which may contain attribute syntax, within
     the given context. The variable may be a hard-coded string (if it begins
     and ends with single or double quote marks).
 
     >>> c = {'article': {'section':'News'}}
-    >>> resolve_variable('article.section', c)
+    >>> v = Variable('article.section')
+    >>> v.resolve(c)
     'News'
-    >>> resolve_variable('article', c)
+    >>> Variable('article').resolve(c)
     {'section': 'News'}
     >>> class AClass: pass
     >>> c = AClass()
     >>> c.article = AClass()
     >>> c.article.section = 'News'
-    >>> resolve_variable('article.section', c)
+    >>> v.resolve(c)
     'News'
 
     (The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.')
     """
-    if path[0].isdigit():
-        number_type = '.' in path and float or int
-        try:
-            current = number_type(path)
-        except ValueError:
-            current = settings.TEMPLATE_STRING_IF_INVALID
-    elif path[0] in ('"', "'") and path[0] == path[-1]:
-        current = path[1:-1]
-    else:
+
+    def __init__(self, path):
+        if path[0].isdigit():
+            number_type = ('.' in path and float) or int
+            try:
+                self.forced_value = number_type(path)
+            except ValueError:
+                self.forced_value = settings.TEMPLATE_STRING_IF_INVALID
+        elif path[0] in ('"', "'") and path[0] == path[-1]:
+            self.forced_value = path[1:-1]
+        else:
+            self.forced_value = None
+            self.split_path = path.split(VARIABLE_ATTRIBUTE_SEPARATOR)
+
+    def resolve(self, context):
+        if self.forced_value is not None:
+            return self.forced_value
         current = context
-        bits = path.split(VARIABLE_ATTRIBUTE_SEPARATOR)
-        while bits:
+        for bit in self.split_path:
             try: # dictionary lookup
-                current = current[bits[0]]
+                current = current[bit]
             except (TypeError, AttributeError, KeyError):
                 try: # attribute lookup
-                    current = getattr(current, bits[0])
+                    current = getattr(current, bit)
                     if callable(current):
                         if getattr(current, 'alters_data', False):
                             current = settings.TEMPLATE_STRING_IF_INVALID
@@ -658,22 +667,43 @@
                                 # raised in the function itself.
                                 current = settings.TEMPLATE_STRING_IF_INVALID # invalid method call
                             except Exception, e:
-                                if getattr(e, 'silent_variable_failure', False):
-                                    current = settings.TEMPLATE_STRING_IF_INVALID
-                                else:
+                                if not getattr(e, 'silent_variable_failure', False):
                                     raise
+                                current = settings.TEMPLATE_STRING_IF_INVALID
                 except (TypeError, AttributeError):
                     try: # list-index lookup
-                        current = current[int(bits[0])]
+                        current = current[int(bit)]
                     except (IndexError, ValueError, KeyError):
-                        raise VariableDoesNotExist("Failed lookup for key [%s] in %r", (bits[0], current)) # missing attribute
+                        raise VariableDoesNotExist("Failed lookup for key [%s] in %r", (bit, current)) # missing attribute
                 except Exception, e:
-                    if getattr(e, 'silent_variable_failure', False):
-                        current = settings.TEMPLATE_STRING_IF_INVALID
-                    else:
+                    if not getattr(e, 'silent_variable_failure', False):
                         raise
-            del bits[0]
-    return current
+                    current = settings.TEMPLATE_STRING_IF_INVALID
+        return current
+
+def resolve_variable(path, context):
+    """
+    Shortcut to resolve a variable- primary usage should be for resolving a
+    variable once.  If the variable may be resolved multiple times, use the 
+    Variable class.
+    
+    See Variable class for information on allowed variable names
+
+    >>> c = {'article': {'section':'News'}}
+    >>> resolve_variable('article.section', c)
+    'News'
+    >>> resolve_variable('article', c)
+    {'section': 'News'}
+    >>> class AClass: pass
+    >>> c = AClass()
+    >>> c.article = AClass()
+    >>> c.article.section = 'News'
+    >>> resolve_variable('article.section', c)
+    'News'
+
+    (The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.')
+    """
+    return Variable(path).resolve(context)
 
 class Node(object):
     def render(self, context):
@@ -710,7 +740,7 @@
         return nodes
 
     def render_node(self, node, context):
-        return(node.render(context))
+        return node.render(context)
 
 class DebugNodeList(NodeList):
     def render_node(self, node, context):
diff -urN django-orig/django/template/defaultfilters.py django-new/django/template/defaultfilters.py
--- django-orig/django/template/defaultfilters.py	2007-02-07 14:01:03.000000000 -0800
+++ django-new/django/template/defaultfilters.py	2007-02-07 14:05:14.000000000 -0800
@@ -1,6 +1,6 @@
 "Default variable filters"
 
-from django.template import resolve_variable, Library
+from django.template import Variable, Library
 from django.conf import settings
 from django.utils.translation import gettext
 import re
@@ -224,7 +224,8 @@
     Takes a list of dicts, returns that list sorted by the property given in
     the argument.
     """
-    decorated = [(resolve_variable('var.' + arg, {'var' : item}), item) for item in value]
+    var = Variable('var.%s' % arg)
+    decorated = [(var.resolve({'var' : item}), item) for item in value]
     decorated.sort()
     return [item[1] for item in decorated]
 
@@ -233,9 +234,9 @@
     Takes a list of dicts, returns that list sorted in reverse order by the
     property given in the argument.
     """
-    decorated = [(resolve_variable('var.' + arg, {'var' : item}), item) for item in value]
-    decorated.sort()
-    decorated.reverse()
+    var = Variable('var.%s' % arg)
+    decorated = [(var.resolve({'var' : item}), item) for item in value]
+    decorated.sort(reverse=True)
     return [item[1] for item in decorated]
 
 def first(value):
diff -urN django-orig/django/template/defaulttags.py django-new/django/template/defaulttags.py
--- django-orig/django/template/defaulttags.py	2007-02-07 14:01:03.000000000 -0800
+++ django-new/django/template/defaulttags.py	2007-02-07 14:05:14.000000000 -0800
@@ -1,6 +1,6 @@
 "Default tags used by the template system, available to all templates."
 
-from django.template import Node, NodeList, Template, Context, resolve_variable
+from django.template import Node, NodeList, Template, Context, Variable
 from django.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, SINGLE_BRACE_START, SINGLE_BRACE_END, COMMENT_TAG_START, COMMENT_TAG_END
 from django.template import get_library, Library, InvalidTemplateLibrary
 from django.conf import settings
@@ -45,12 +45,12 @@
 
 class FirstOfNode(Node):
     def __init__(self, vars):
-        self.vars = vars
+        self.vars = map(Variable, vars)
 
     def render(self, context):
         for var in self.vars:
             try:
-                value = resolve_variable(var, context)
+                value = var.resolve(context)
             except VariableDoesNotExist:
                 continue
             if value:
@@ -127,7 +127,7 @@
     def __init__(self, nodelist, *varlist):
         self.nodelist = nodelist
         self._last_seen = None
-        self._varlist = varlist
+        self._varlist = map(Variable, varlist)
 
     def render(self, context):
         if context.has_key('forloop') and context['forloop']['first']:
@@ -136,7 +136,7 @@
             if self._varlist:
                 # Consider multiple parameters.
                 # This automatically behaves like a OR evaluation of the multiple variables.
-                compare_to = [resolve_variable(var, context) for var in self._varlist]
+                compare_to = [x.resolve(context) for x in self._varlist]
             else:
                 compare_to = self.nodelist.render(context)
         except VariableDoesNotExist:
@@ -155,7 +155,7 @@
 
 class IfEqualNode(Node):
     def __init__(self, var1, var2, nodelist_true, nodelist_false, negate):
-        self.var1, self.var2 = var1, var2
+        self.var1, self.var2 = Variable(var1), Variable(var2)
         self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
         self.negate = negate
 
@@ -164,11 +164,11 @@
 
     def render(self, context):
         try:
-            val1 = resolve_variable(self.var1, context)
+            val1 = self.var1.resolve(context)
         except VariableDoesNotExist:
             val1 = None
         try:
-            val2 = resolve_variable(self.var2, context)
+            val2 = self.var2.resolve(context)
         except VariableDoesNotExist:
             val2 = None
         if (self.negate and val1 != val2) or (not self.negate and val1 == val2):
diff -urN django-orig/django/template/loader_tags.py django-new/django/template/loader_tags.py
--- django-orig/django/template/loader_tags.py	2007-02-07 14:01:03.000000000 -0800
+++ django-new/django/template/loader_tags.py	2007-02-07 14:05:14.000000000 -0800
@@ -1,4 +1,4 @@
-from django.template import TemplateSyntaxError, TemplateDoesNotExist, resolve_variable
+from django.template import TemplateSyntaxError, TemplateDoesNotExist, Variable
 from django.template import Library, Node
 from django.template.loader import get_template, get_template_from_string, find_template_source
 from django.conf import settings
@@ -99,11 +99,11 @@
 
 class IncludeNode(Node):
     def __init__(self, template_name):
-        self.template_name = template_name
+        self.template_name = Variable(template_name)
 
     def render(self, context):
         try:
-            template_name = resolve_variable(self.template_name, context)
+            template_name = self.template_name.resolve(context)
             t = get_template(template_name)
             return t.render(context)
         except TemplateSyntaxError, e:
diff -urN django-orig/django/templatetags/i18n.py django-new/django/templatetags/i18n.py
--- django-orig/django/templatetags/i18n.py	2007-02-07 14:01:03.000000000 -0800
+++ django-new/django/templatetags/i18n.py	2007-02-07 14:05:29.000000000 -0800
@@ -1,4 +1,4 @@
-from django.template import Node, resolve_variable
+from django.template import Node, Variable
 from django.template import TemplateSyntaxError, TokenParser, Library
 from django.template import TOKEN_TEXT, TOKEN_VAR
 from django.utils import translation
@@ -32,11 +32,11 @@
 
 class TranslateNode(Node):
     def __init__(self, value, noop):
-        self.value = value
+        self.value = Variable(value)
         self.noop = noop
 
     def render(self, context):
-        value = resolve_variable(self.value, context)
+        value = self.value.resolve(context)
         if self.noop:
             return value
         else:
