Ticket #3453: patch

File patch, 16.5 KB (added by (removed), 17 years ago)

resolve_variable refactoring

  • django/contrib/admin/templatetags/admin_modify.py

    diff -urN django-orig/django/contrib/admin/templatetags/admin_modify.py django-new/django/contrib/admin/templatetags/admin_modify.py
    old new  
    6969    default = None
    7070
    7171    def __init__(self, bound_field_var):
    72         self.bound_field_var = bound_field_var
     72        self.bound_field_var = template.Variable(bound_field_var)
    7373
    7474    def get_nodelist(cls, klass):
    7575        if not cls.nodelists.has_key(klass):
     
    9393    get_nodelist = classmethod(get_nodelist)
    9494
    9595    def render(self, context):
    96         bound_field = template.resolve_variable(self.bound_field_var, context)
     96        bound_field = self.bound_field_var.resolve(context)
    9797
    9898        context.push()
    9999        context['bound_field'] = bound_field
     
    153153
    154154class EditInlineNode(template.Node):
    155155    def __init__(self, rel_var):
    156         self.rel_var = rel_var
     156        self.rel_var = template.Variable(rel_var)
    157157
    158158    def render(self, context):
    159         relation = template.resolve_variable(self.rel_var, context)
     159        relation = self.rel_var.resolve(context)
    160160        context.push()
    161161        if relation.field.rel.edit_inline == models.TABULAR:
    162162            bound_related_object_class = TabularBoundRelatedObject
  • django/contrib/comments/templatetags/comments.py

    diff -urN django-orig/django/contrib/comments/templatetags/comments.py django-new/django/contrib/comments/templatetags/comments.py
    old new  
    1818        ratings_optional=False, ratings_required=False, rating_options='',
    1919        is_public=True):
    2020        self.content_type = content_type
     21        if obj_id_lookup_var is not None:
     22            obj_id_lookup_var = template.Variable(obj_id_lookup_var)
    2123        self.obj_id_lookup_var, self.obj_id, self.free = obj_id_lookup_var, obj_id, free
    2224        self.photos_optional, self.photos_required = photos_optional, photos_required
    2325        self.ratings_optional, self.ratings_required = ratings_optional, ratings_required
     
    3032        context.push()
    3133        if self.obj_id_lookup_var is not None:
    3234            try:
    33                 self.obj_id = template.resolve_variable(self.obj_id_lookup_var, context)
     35                self.obj_id = self.obj_id_lookup_var.resolve(context)
    3436            except template.VariableDoesNotExist:
    3537                return ''
    3638            # Validate that this object ID is valid for this content-type.
     
    7375    def __init__(self, package, module, context_var_name, obj_id, var_name, free):
    7476        self.package, self.module = package, module
    7577        self.context_var_name, self.obj_id = context_var_name, obj_id
     78        if var_name is not None:
     79            var_name = template.Variable(var_name)
    7680        self.var_name, self.free = var_name, free
    7781
    7882    def render(self, context):
    7983        from django.conf import settings
    8084        manager = self.free and FreeComment.objects or Comment.objects
    8185        if self.context_var_name is not None:
    82             self.obj_id = template.resolve_variable(self.context_var_name, context)
     86            self.obj_id = self.var_name.resolve(context)
    8387        comment_count = manager.filter(object_id__exact=self.obj_id,
    8488            content_type__app_label__exact=self.package,
    8589            content_type__model__exact=self.module, site__id__exact=settings.SITE_ID).count()
     
    8993class CommentListNode(template.Node):
    9094    def __init__(self, package, module, context_var_name, obj_id, var_name, free, ordering, extra_kwargs=None):
    9195        self.package, self.module = package, module
     96        if context_var_name is not None:
     97            context_var_name = template.Variable(context_var_name)
    9298        self.context_var_name, self.obj_id = context_var_name, obj_id
    9399        self.var_name, self.free = var_name, free
    94100        self.ordering = ordering
     
    99105        get_list_function = self.free and FreeComment.objects.filter or Comment.objects.get_list_with_karma
    100106        if self.context_var_name is not None:
    101107            try:
    102                 self.obj_id = template.resolve_variable(self.context_var_name, context)
     108                self.obj_id = self.context_var_name.resolve(context)
    103109            except template.VariableDoesNotExist:
    104110                return ''
    105111        kwargs = {
  • django/template/__init__.py

    diff -urN django-orig/django/template/__init__.py django-new/django/template/__init__.py
    old new  
    554554                upto = match.end()
    555555        if upto != len(token):
    556556            raise TemplateSyntaxError, "Could not parse the remainder: %s" % token[upto:]
    557         self.var, self.filters = var, filters
     557        self.var, self.filters = Variable(var), filters
    558558
    559559    def resolve(self, context, ignore_failures=False):
    560560        try:
    561             obj = resolve_variable(self.var, context)
     561            obj = self.var.resolve(context)
    562562        except VariableDoesNotExist:
    563563            if ignore_failures:
    564564                obj = None
     
    610610    def __str__(self):
    611611        return self.token
    612612
    613 def resolve_variable(path, context):
     613
     614class Variable(object):
    614615    """
    615616    Returns the resolved variable, which may contain attribute syntax, within
    616617    the given context. The variable may be a hard-coded string (if it begins
    617618    and ends with single or double quote marks).
    618619
    619620    >>> c = {'article': {'section':'News'}}
    620     >>> resolve_variable('article.section', c)
     621    >>> v = Variable('article.section')
     622    >>> v.resolve(c)
    621623    'News'
    622     >>> resolve_variable('article', c)
     624    >>> Variable('article').resolve(c)
    623625    {'section': 'News'}
    624626    >>> class AClass: pass
    625627    >>> c = AClass()
    626628    >>> c.article = AClass()
    627629    >>> c.article.section = 'News'
    628     >>> resolve_variable('article.section', c)
     630    >>> v.resolve(c)
    629631    'News'
    630632
    631633    (The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.')
    632634    """
    633     if path[0].isdigit():
    634         number_type = '.' in path and float or int
    635         try:
    636             current = number_type(path)
    637         except ValueError:
    638             current = settings.TEMPLATE_STRING_IF_INVALID
    639     elif path[0] in ('"', "'") and path[0] == path[-1]:
    640         current = path[1:-1]
    641     else:
     635
     636    def __init__(self, path):
     637        if path[0].isdigit():
     638            number_type = ('.' in path and float) or int
     639            try:
     640                self.forced_value = number_type(path)
     641            except ValueError:
     642                self.forced_value = settings.TEMPLATE_STRING_IF_INVALID
     643        elif path[0] in ('"', "'") and path[0] == path[-1]:
     644            self.forced_value = path[1:-1]
     645        else:
     646            self.forced_value = None
     647            self.split_path = path.split(VARIABLE_ATTRIBUTE_SEPARATOR)
     648
     649    def resolve(self, context):
     650        if self.forced_value is not None:
     651            return self.forced_value
    642652        current = context
    643         bits = path.split(VARIABLE_ATTRIBUTE_SEPARATOR)
    644         while bits:
     653        for bit in self.split_path:
    645654            try: # dictionary lookup
    646                 current = current[bits[0]]
     655                current = current[bit]
    647656            except (TypeError, AttributeError, KeyError):
    648657                try: # attribute lookup
    649                     current = getattr(current, bits[0])
     658                    current = getattr(current, bit)
    650659                    if callable(current):
    651660                        if getattr(current, 'alters_data', False):
    652661                            current = settings.TEMPLATE_STRING_IF_INVALID
     
    658667                                # raised in the function itself.
    659668                                current = settings.TEMPLATE_STRING_IF_INVALID # invalid method call
    660669                            except Exception, e:
    661                                 if getattr(e, 'silent_variable_failure', False):
    662                                     current = settings.TEMPLATE_STRING_IF_INVALID
    663                                 else:
     670                                if not getattr(e, 'silent_variable_failure', False):
    664671                                    raise
     672                                current = settings.TEMPLATE_STRING_IF_INVALID
    665673                except (TypeError, AttributeError):
    666674                    try: # list-index lookup
    667                         current = current[int(bits[0])]
     675                        current = current[int(bit)]
    668676                    except (IndexError, ValueError, KeyError):
    669                         raise VariableDoesNotExist("Failed lookup for key [%s] in %r", (bits[0], current)) # missing attribute
     677                        raise VariableDoesNotExist("Failed lookup for key [%s] in %r", (bit, current)) # missing attribute
    670678                except Exception, e:
    671                     if getattr(e, 'silent_variable_failure', False):
    672                         current = settings.TEMPLATE_STRING_IF_INVALID
    673                     else:
     679                    if not getattr(e, 'silent_variable_failure', False):
    674680                        raise
    675             del bits[0]
    676     return current
     681                    current = settings.TEMPLATE_STRING_IF_INVALID
     682        return current
     683
     684def resolve_variable(path, context):
     685    """
     686    Shortcut to resolve a variable- primary usage should be for resolving a
     687    variable once.  If the variable may be resolved multiple times, use the
     688    Variable class.
     689   
     690    See Variable class for information on allowed variable names
     691
     692    >>> c = {'article': {'section':'News'}}
     693    >>> resolve_variable('article.section', c)
     694    'News'
     695    >>> resolve_variable('article', c)
     696    {'section': 'News'}
     697    >>> class AClass: pass
     698    >>> c = AClass()
     699    >>> c.article = AClass()
     700    >>> c.article.section = 'News'
     701    >>> resolve_variable('article.section', c)
     702    'News'
     703
     704    (The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.')
     705    """
     706    return Variable(path).resolve(context)
    677707
    678708class Node(object):
    679709    def render(self, context):
     
    710740        return nodes
    711741
    712742    def render_node(self, node, context):
    713         return(node.render(context))
     743        return node.render(context)
    714744
    715745class DebugNodeList(NodeList):
    716746    def render_node(self, node, context):
  • django/template/defaultfilters.py

    diff -urN django-orig/django/template/defaultfilters.py django-new/django/template/defaultfilters.py
    old new  
    11"Default variable filters"
    22
    3 from django.template import resolve_variable, Library
     3from django.template import Variable, Library
    44from django.conf import settings
    55from django.utils.translation import gettext
    66import re
     
    224224    Takes a list of dicts, returns that list sorted by the property given in
    225225    the argument.
    226226    """
    227     decorated = [(resolve_variable('var.' + arg, {'var' : item}), item) for item in value]
     227    var = Variable('var.%s' % arg)
     228    decorated = [(var.resolve({'var' : item}), item) for item in value]
    228229    decorated.sort()
    229230    return [item[1] for item in decorated]
    230231
     
    233234    Takes a list of dicts, returns that list sorted in reverse order by the
    234235    property given in the argument.
    235236    """
    236     decorated = [(resolve_variable('var.' + arg, {'var' : item}), item) for item in value]
    237     decorated.sort()
    238     decorated.reverse()
     237    var = Variable('var.%s' % arg)
     238    decorated = [(var.resolve({'var' : item}), item) for item in value]
     239    decorated.sort(reverse=True)
    239240    return [item[1] for item in decorated]
    240241
    241242def first(value):
  • django/template/defaulttags.py

    diff -urN django-orig/django/template/defaulttags.py django-new/django/template/defaulttags.py
    old new  
    11"Default tags used by the template system, available to all templates."
    22
    3 from django.template import Node, NodeList, Template, Context, resolve_variable
     3from django.template import Node, NodeList, Template, Context, Variable
    44from 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
    55from django.template import get_library, Library, InvalidTemplateLibrary
    66from django.conf import settings
     
    4545
    4646class FirstOfNode(Node):
    4747    def __init__(self, vars):
    48         self.vars = vars
     48        self.vars = map(Variable, vars)
    4949
    5050    def render(self, context):
    5151        for var in self.vars:
    5252            try:
    53                 value = resolve_variable(var, context)
     53                value = var.resolve(context)
    5454            except VariableDoesNotExist:
    5555                continue
    5656            if value:
     
    127127    def __init__(self, nodelist, *varlist):
    128128        self.nodelist = nodelist
    129129        self._last_seen = None
    130         self._varlist = varlist
     130        self._varlist = map(Variable, varlist)
    131131
    132132    def render(self, context):
    133133        if context.has_key('forloop') and context['forloop']['first']:
     
    136136            if self._varlist:
    137137                # Consider multiple parameters.
    138138                # This automatically behaves like a OR evaluation of the multiple variables.
    139                 compare_to = [resolve_variable(var, context) for var in self._varlist]
     139                compare_to = [x.resolve(context) for x in self._varlist]
    140140            else:
    141141                compare_to = self.nodelist.render(context)
    142142        except VariableDoesNotExist:
     
    155155
    156156class IfEqualNode(Node):
    157157    def __init__(self, var1, var2, nodelist_true, nodelist_false, negate):
    158         self.var1, self.var2 = var1, var2
     158        self.var1, self.var2 = Variable(var1), Variable(var2)
    159159        self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
    160160        self.negate = negate
    161161
     
    164164
    165165    def render(self, context):
    166166        try:
    167             val1 = resolve_variable(self.var1, context)
     167            val1 = self.var1.resolve(context)
    168168        except VariableDoesNotExist:
    169169            val1 = None
    170170        try:
    171             val2 = resolve_variable(self.var2, context)
     171            val2 = self.var2.resolve(context)
    172172        except VariableDoesNotExist:
    173173            val2 = None
    174174        if (self.negate and val1 != val2) or (not self.negate and val1 == val2):
  • django/template/loader_tags.py

    diff -urN django-orig/django/template/loader_tags.py django-new/django/template/loader_tags.py
    old new  
    1 from django.template import TemplateSyntaxError, TemplateDoesNotExist, resolve_variable
     1from django.template import TemplateSyntaxError, TemplateDoesNotExist, Variable
    22from django.template import Library, Node
    33from django.template.loader import get_template, get_template_from_string, find_template_source
    44from django.conf import settings
     
    9999
    100100class IncludeNode(Node):
    101101    def __init__(self, template_name):
    102         self.template_name = template_name
     102        self.template_name = Variable(template_name)
    103103
    104104    def render(self, context):
    105105        try:
    106             template_name = resolve_variable(self.template_name, context)
     106            template_name = self.template_name.resolve(context)
    107107            t = get_template(template_name)
    108108            return t.render(context)
    109109        except TemplateSyntaxError, e:
  • django/templatetags/i18n.py

    diff -urN django-orig/django/templatetags/i18n.py django-new/django/templatetags/i18n.py
    old new  
    1 from django.template import Node, resolve_variable
     1from django.template import Node, Variable
    22from django.template import TemplateSyntaxError, TokenParser, Library
    33from django.template import TOKEN_TEXT, TOKEN_VAR
    44from django.utils import translation
     
    3232
    3333class TranslateNode(Node):
    3434    def __init__(self, value, noop):
    35         self.value = value
     35        self.value = Variable(value)
    3636        self.noop = noop
    3737
    3838    def render(self, context):
    39         value = resolve_variable(self.value, context)
     39        value = self.value.resolve(context)
    4040        if self.noop:
    4141            return value
    4242        else:
Back to Top