~ubuntu-branches/ubuntu/quantal/python-django/quantal

« back to all changes in this revision

Viewing changes to django/template/__init__.py

  • Committer: Bazaar Package Importer
  • Author(s): Chris Lamb
  • Date: 2009-07-29 11:26:28 UTC
  • mfrom: (1.1.8 upstream) (4.1.5 sid)
  • Revision ID: james.westby@ubuntu.com-20090729112628-pg09ino8sz0sj21t
Tags: 1.1-1
* New upstream release.
* Merge from experimental:
  - Ship FastCGI initscript and /etc/default file in python-django's examples
    directory (Closes: #538863)
  - Drop "05_10539-sphinx06-compatibility.diff"; it has been applied
    upstream.
  - Bump Standards-Version to 3.8.2.

Show diffs side-by-side

added added

removed removed

Lines of Context:
50
50
"""
51
51
import re
52
52
from inspect import getargspec
 
53
 
53
54
from django.conf import settings
54
55
from django.template.context import Context, RequestContext, ContextPopException
 
56
from django.utils.importlib import import_module
55
57
from django.utils.itercompat import is_iterable
56
58
from django.utils.functional import curry, Promise
57
 
from django.utils.text import smart_split
58
 
from django.utils.encoding import smart_unicode, force_unicode
 
59
from django.utils.text import smart_split, unescape_string_literal
 
60
from django.utils.encoding import smart_unicode, force_unicode, smart_str
59
61
from django.utils.translation import ugettext as _
60
62
from django.utils.safestring import SafeData, EscapeData, mark_safe, mark_for_escaping
61
63
from django.utils.html import escape
443
445
            self.pointer = i
444
446
            return s
445
447
 
 
448
# This only matches constant *strings* (things in quotes or marked for
 
449
# translation). Numbers are treated as variables for implementation reasons
 
450
# (so that they retain their type when passed to filters).
 
451
constant_string = r"""
 
452
(?:%(i18n_open)s%(strdq)s%(i18n_close)s|
 
453
%(i18n_open)s%(strsq)s%(i18n_close)s|
 
454
%(strdq)s|
 
455
%(strsq)s)
 
456
""" % {
 
457
    'strdq': r'"[^"\\]*(?:\\.[^"\\]*)*"', # double-quoted string
 
458
    'strsq': r"'[^'\\]*(?:\\.[^'\\]*)*'", # single-quoted string
 
459
    'i18n_open' : re.escape("_("),
 
460
    'i18n_close' : re.escape(")"),
 
461
    }
 
462
constant_string = constant_string.replace("\n", "")
 
463
 
446
464
filter_raw_string = r"""
447
 
^%(i18n_open)s"(?P<i18n_constant>%(str)s)"%(i18n_close)s|
448
 
^"(?P<constant>%(str)s)"|
449
 
^(?P<var>[%(var_chars)s]+)|
 
465
^(?P<constant>%(constant)s)|
 
466
^(?P<var>[%(var_chars)s]+|%(num)s)|
450
467
 (?:%(filter_sep)s
451
468
     (?P<filter_name>\w+)
452
469
         (?:%(arg_sep)s
453
470
             (?:
454
 
              %(i18n_open)s"(?P<i18n_arg>%(str)s)"%(i18n_close)s|
455
 
              "(?P<constant_arg>%(str)s)"|
456
 
              (?P<var_arg>[%(var_chars)s]+)
 
471
              (?P<constant_arg>%(constant)s)|
 
472
              (?P<var_arg>[%(var_chars)s]+|%(num)s)
457
473
             )
458
474
         )?
459
475
 )""" % {
460
 
    'str': r"""[^"\\]*(?:\\.[^"\\]*)*""",
 
476
    'constant': constant_string,
 
477
    'num': r'[-+\.]?\d[\d\.e]*',
461
478
    'var_chars': "\w\." ,
462
479
    'filter_sep': re.escape(FILTER_SEPARATOR),
463
480
    'arg_sep': re.escape(FILTER_ARGUMENT_SEPARATOR),
464
 
    'i18n_open' : re.escape("_("),
465
 
    'i18n_close' : re.escape(")"),
466
481
  }
467
482
 
468
 
filter_raw_string = filter_raw_string.replace("\n", "").replace(" ", "")
469
 
filter_re = re.compile(filter_raw_string, re.UNICODE)
 
483
filter_re = re.compile(filter_raw_string, re.UNICODE|re.VERBOSE)
470
484
 
471
485
class FilterExpression(object):
472
 
    """
 
486
    r"""
473
487
    Parses a variable token and its optional filters (all as a single string),
474
488
    and return a list of tuples of the filter name and arguments.
475
489
    Sample:
487
501
    def __init__(self, token, parser):
488
502
        self.token = token
489
503
        matches = filter_re.finditer(token)
490
 
        var = None
 
504
        var_obj = None
491
505
        filters = []
492
506
        upto = 0
493
507
        for match in matches:
494
508
            start = match.start()
495
509
            if upto != start:
496
510
                raise TemplateSyntaxError("Could not parse some characters: %s|%s|%s"  % \
497
 
                                           (token[:upto], token[upto:start], token[start:]))
498
 
            if var == None:
499
 
                var, constant, i18n_constant = match.group("var", "constant", "i18n_constant")
500
 
                if i18n_constant is not None:
501
 
                    # Don't pass the empty string to gettext, because the empty
502
 
                    # string translates to meta information.
503
 
                    if i18n_constant == "":
504
 
                        var = '""'
505
 
                    else:
506
 
                        var = '"%s"' %  _(i18n_constant.replace(r'\"', '"'))
507
 
                elif constant is not None:
508
 
                    var = '"%s"' % constant.replace(r'\"', '"')
509
 
                upto = match.end()
510
 
                if var == None:
511
 
                    raise TemplateSyntaxError("Could not find variable at start of %s" % token)
 
511
                        (token[:upto], token[upto:start], token[start:]))
 
512
            if var_obj is None:
 
513
                var, constant = match.group("var", "constant")
 
514
                if constant:
 
515
                    try:
 
516
                        var_obj = Variable(constant).resolve({})
 
517
                    except VariableDoesNotExist:
 
518
                        var_obj = None
 
519
                elif var is None:
 
520
                    raise TemplateSyntaxError("Could not find variable at start of %s." % token)
512
521
                elif var.find(VARIABLE_ATTRIBUTE_SEPARATOR + '_') > -1 or var[0] == '_':
513
522
                    raise TemplateSyntaxError("Variables and attributes may not begin with underscores: '%s'" % var)
 
523
                else:
 
524
                    var_obj = Variable(var)
514
525
            else:
515
526
                filter_name = match.group("filter_name")
516
527
                args = []
517
 
                constant_arg, i18n_arg, var_arg = match.group("constant_arg", "i18n_arg", "var_arg")
518
 
                if i18n_arg:
519
 
                    args.append((False, _(i18n_arg.replace(r'\"', '"'))))
520
 
                elif constant_arg is not None:
521
 
                    args.append((False, constant_arg.replace(r'\"', '"')))
 
528
                constant_arg, var_arg = match.group("constant_arg", "var_arg")
 
529
                if constant_arg:
 
530
                    args.append((False, Variable(constant_arg).resolve({})))
522
531
                elif var_arg:
523
532
                    args.append((True, Variable(var_arg)))
524
533
                filter_func = parser.find_filter(filter_name)
525
534
                self.args_check(filter_name,filter_func, args)
526
535
                filters.append( (filter_func,args))
527
 
                upto = match.end()
 
536
            upto = match.end()
528
537
        if upto != len(token):
529
538
            raise TemplateSyntaxError("Could not parse the remainder: '%s' from '%s'" % (token[upto:], token))
 
539
 
530
540
        self.filters = filters
531
 
        self.var = Variable(var)
 
541
        self.var = var_obj
532
542
 
533
543
    def resolve(self, context, ignore_failures=False):
534
 
        try:
535
 
            obj = self.var.resolve(context)
536
 
        except VariableDoesNotExist:
537
 
            if ignore_failures:
538
 
                obj = None
539
 
            else:
540
 
                if settings.TEMPLATE_STRING_IF_INVALID:
541
 
                    global invalid_var_format_string
542
 
                    if invalid_var_format_string is None:
543
 
                        invalid_var_format_string = '%s' in settings.TEMPLATE_STRING_IF_INVALID
544
 
                    if invalid_var_format_string:
545
 
                        return settings.TEMPLATE_STRING_IF_INVALID % self.var
546
 
                    return settings.TEMPLATE_STRING_IF_INVALID
 
544
        if isinstance(self.var, Variable):
 
545
            try:
 
546
                obj = self.var.resolve(context)
 
547
            except VariableDoesNotExist:
 
548
                if ignore_failures:
 
549
                    obj = None
547
550
                else:
548
 
                    obj = settings.TEMPLATE_STRING_IF_INVALID
 
551
                    if settings.TEMPLATE_STRING_IF_INVALID:
 
552
                        global invalid_var_format_string
 
553
                        if invalid_var_format_string is None:
 
554
                            invalid_var_format_string = '%s' in settings.TEMPLATE_STRING_IF_INVALID
 
555
                        if invalid_var_format_string:
 
556
                            return settings.TEMPLATE_STRING_IF_INVALID % self.var
 
557
                        return settings.TEMPLATE_STRING_IF_INVALID
 
558
                    else:
 
559
                        obj = settings.TEMPLATE_STRING_IF_INVALID
 
560
        else:
 
561
            obj = self.var
549
562
        for func, args in self.filters:
550
563
            arg_vals = []
551
564
            for lookup, arg in args:
610
623
    return Variable(path).resolve(context)
611
624
 
612
625
class Variable(object):
613
 
    """
 
626
    r"""
614
627
    A template variable, resolvable against a given context. The variable may be
615
628
    a hard-coded string (if it begins and ends with single or double quote
616
629
    marks)::
624
637
        >>> c = AClass()
625
638
        >>> c.article = AClass()
626
639
        >>> c.article.section = u'News'
627
 
        >>> Variable('article.section').resolve(c)
628
 
        u'News'
629
640
 
630
641
    (The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.')
631
642
    """
662
673
                var = var[2:-1]
663
674
            # If it's wrapped with quotes (single or double), then
664
675
            # we're also dealing with a literal.
665
 
            if var[0] in "\"'" and var[0] == var[-1]:
666
 
                self.literal = mark_safe(var[1:-1])
667
 
            else:
 
676
            try:
 
677
                self.literal = mark_safe(unescape_string_literal(var))
 
678
            except ValueError:
668
679
                # Otherwise we'll set self.lookups so that resolve() knows we're
669
680
                # dealing with a bonafide variable
670
681
                self.lookups = tuple(var.split(VARIABLE_ATTRIBUTE_SEPARATOR))
785
796
        self.s = s
786
797
 
787
798
    def __repr__(self):
788
 
        return "<Text Node: '%s'>" % self.s[:25]
 
799
        return "<Text Node: '%s'>" % smart_str(self.s[:25], 'ascii',
 
800
                errors='replace')
789
801
 
790
802
    def render(self, context):
791
803
        return self.s
 
804
    
 
805
def _render_value_in_context(value, context):
 
806
    """
 
807
    Converts any value to a string to become part of a rendered template. This
 
808
    means escaping, if required, and conversion to a unicode object. If value
 
809
    is a string, it is expected to have already been translated.
 
810
    """
 
811
    value = force_unicode(value)
 
812
    if (context.autoescape and not isinstance(value, SafeData)) or isinstance(value, EscapeData):
 
813
        return escape(value)
 
814
    else:
 
815
        return value
792
816
 
793
817
class VariableNode(Node):
794
818
    def __init__(self, filter_expression):
799
823
 
800
824
    def render(self, context):
801
825
        try:
802
 
            output = force_unicode(self.filter_expression.resolve(context))
 
826
            output = self.filter_expression.resolve(context)
803
827
        except UnicodeDecodeError:
804
828
            # Unicode conversion can fail sometimes for reasons out of our
805
829
            # control (e.g. exception rendering). In that case, we fail quietly.
806
830
            return ''
807
 
        if (context.autoescape and not isinstance(output, SafeData)) or isinstance(output, EscapeData):
808
 
            return force_unicode(escape(output))
809
 
        else:
810
 
            return force_unicode(output)
 
831
        return _render_value_in_context(output, context)
811
832
 
812
833
def generic_tag_compiler(params, defaults, name, node_class, parser, token):
813
834
    "Returns a template.Node subclass."
934
955
    lib = libraries.get(module_name, None)
935
956
    if not lib:
936
957
        try:
937
 
            mod = __import__(module_name, {}, {}, [''])
 
958
            mod = import_module(module_name)
938
959
        except ImportError, e:
939
960
            raise InvalidTemplateLibrary("Could not load template library from %s, %s" % (module_name, e))
940
961
        try: