Ticket #7295: 7295.1.diff
File 7295.1.diff, 10.6 KB (added by , 16 years ago) |
---|
-
../django/template/__init__.py
100 100 # uninitialised. 101 101 invalid_var_format_string = None 102 102 103 def unescape_string_literal(s): 104 r""" 105 Convert quoted string literals to unquoted strings with escaped quotes and 106 backslashes unquoted:: 107 108 >>> unescape_string_literal('"abc"') 109 'abc' 110 >>> unescape_string_literal("'abc'") 111 'abc' 112 >>> unescape_string_literal('"a \"bc\""') 113 'a "bc"' 114 >>> unescape_string_literal("'\'ab\' c'") 115 "'ab' c" 116 117 Would this function be more appropriate in the django.utils.text module? 118 """ 119 if s[0] not in "\"'" or s[-1] != s[0]: 120 raise ValueError("Not a string literal: %r" % s) 121 quote = s[0] 122 return s[1:-1].replace(r'\%s' % quote, quote).replace(r'\\', '\\') 123 124 def resolve_string_literal(s): 125 r""" 126 Un-escape, translate and unquote string literals. Handle single and double 127 quoted strings with corresponding quotes and backslashes escaped with 128 prepending backslashes:: 129 130 >>> resolve_string_literal(ur'"Some \"Good\" \\ News"') 131 u'Some "Good" \\ News' 132 >>> resolve_string_literal(ur"'Some \'Good\' \\ News'") 133 u"Some 'Good' \\ News" 134 >>> resolve_string_literal(ur'_("Some \"Good\" \\ News")') 135 u'Some "Good" \\ News' 136 >>> resolve_string_literal(ur"_('Some \'Good\' \\ News')") 137 u"Some 'Good' \\ News" 138 """ 139 if s.startswith('_(') and s.endswith(')'): 140 return mark_safe(_(unescape_string_literal(s[2:-1]))) 141 return mark_safe(unescape_string_literal(s)) 142 103 143 class TemplateSyntaxError(Exception): 104 144 def __str__(self): 105 145 try: … … 431 471 self.pointer = i 432 472 return s 433 473 474 constant_string = r""" 475 (?:%(i18n_open)s%(strdq)s%(i18n_close)s| 476 %(i18n_open)s%(strsq)s%(i18n_close)s| 477 %(strdq)s| 478 %(strsq)s) 479 """ % { 480 'strdq': r'''"[^"\\]*(?:\\.[^"\\]*)*"''', # double-quoted string 481 'strsq': r"""'[^'\\]*(?:\\.[^'\\]*)*'""", # single-quoted string 482 'i18n_open' : re.escape("_("), 483 'i18n_close' : re.escape(")"), 484 } 485 constant_string = constant_string.replace("\n", "") 486 434 487 filter_raw_string = r""" 435 ^%(i18n_open)s"(?P<i18n_constant>%(str)s)"%(i18n_close)s| 436 ^"(?P<constant>%(str)s)"| 488 ^(?P<constant>%(constant)s)| 437 489 ^(?P<var>[%(var_chars)s]+)| 438 490 (?:%(filter_sep)s 439 491 (?P<filter_name>\w+) 440 492 (?:%(arg_sep)s 441 493 (?: 442 %(i18n_open)s"(?P<i18n_arg>%(str)s)"%(i18n_close)s| 443 "(?P<constant_arg>%(str)s)"| 494 (?P<constant_arg>%(constant)s)| 444 495 (?P<var_arg>[%(var_chars)s]+) 445 496 ) 446 497 )? 447 498 )""" % { 448 ' str': r"""[^"\\]*(?:\\.[^"\\]*)*""",499 'constant': constant_string, 449 500 'var_chars': "\w\." , 450 501 'filter_sep': re.escape(FILTER_SEPARATOR), 451 502 'arg_sep': re.escape(FILTER_ARGUMENT_SEPARATOR), 452 'i18n_open' : re.escape("_("),453 'i18n_close' : re.escape(")"),454 503 } 455 504 456 505 filter_raw_string = filter_raw_string.replace("\n", "").replace(" ", "") 457 506 filter_re = re.compile(filter_raw_string, re.UNICODE) 458 507 459 508 class FilterExpression(object): 460 """509 r""" 461 510 Parses a variable token and its optional filters (all as a single string), 462 511 and return a list of tuples of the filter name and arguments. 463 512 Sample: … … 469 518 >>> fe.var 470 519 'variable' 471 520 521 >>> c = {'article': {'section': u'News'}} 522 >>> def fe_test(s): return FilterExpression(s, p).resolve(c) 523 >>> fe_test('article.section') 524 u'News' 525 >>> fe_test('article.section|upper') 526 u'NEWS' 527 >>> fe_test(u'"News"') 528 u'News' 529 >>> fe_test(u"'News'") 530 u'News' 531 >>> fe_test(ur'"Some \"Good\" News"') 532 u'Some "Good" News' 533 >>> fe_test(ur"'Some \'Bad\' News'") 534 u"Some 'Bad' News" 535 536 >>> fe = FilterExpression(ur'"Some \"Good\" News"', p) 537 >>> fe.filters 538 [] 539 >>> fe.var 540 u'Some "Good" News' 541 472 542 This class should never be instantiated outside of the 473 543 get_filters_from_token helper function. 544 545 The filter_re regular expression is responsible for tokenizing the filter 546 expression:: 547 548 >>> def fre_test(s): 549 ... print '|'.join(','.join("%s=%s"%(key, val) for key, val in sorted(match.groupdict().items()) if val is not None) for match in filter_re.finditer(s)) 550 >>> fre_test('myvar') 551 var=myvar 552 >>> fre_test('myvar|myfilt:myarg') 553 var=myvar|filter_name=myfilt,var_arg=myarg 554 >>> fre_test(r'"Some \"Good\" News"|myfilt:"Some \"Bad\" News"') 555 constant="Some \"Good\" News"|constant_arg="Some \"Bad\" News",filter_name=myfilt 556 >>> fre_test(r"'More \'Good\' News'|myfilt:'More \'Bad\' News'") 557 constant='More \'Good\' News'|constant_arg='More \'Bad\' News',filter_name=myfilt 474 558 """ 475 559 def __init__(self, token, parser): 476 560 self.token = token … … 484 568 raise TemplateSyntaxError("Could not parse some characters: %s|%s|%s" % \ 485 569 (token[:upto], token[upto:start], token[start:])) 486 570 if var == None: 487 var, constant, i18n_constant = match.group("var", "constant", "i18n_constant") 488 if i18n_constant: 489 var = '"%s"' % _(i18n_constant.replace(r'\"', '"')) 490 elif constant: 491 var = '"%s"' % constant.replace(r'\"', '"') 492 upto = match.end() 493 if var == None: 571 var, constant = match.group("var", "constant") 572 if constant: 573 self.var = resolve_string_literal(constant) 574 elif var == None: 494 575 raise TemplateSyntaxError("Could not find variable at start of %s" % token) 495 576 elif var.find(VARIABLE_ATTRIBUTE_SEPARATOR + '_') > -1 or var[0] == '_': 496 577 raise TemplateSyntaxError("Variables and attributes may not begin with underscores: '%s'" % var) 578 else: 579 self.var = Variable(var) 580 upto = match.end() 497 581 else: 498 582 filter_name = match.group("filter_name") 499 583 args = [] 500 constant_arg, i18n_arg, var_arg = match.group("constant_arg", "i18n_arg", "var_arg") 501 if i18n_arg: 502 args.append((False, _(i18n_arg.replace(r'\"', '"')))) 503 elif constant_arg is not None: 504 args.append((False, constant_arg.replace(r'\"', '"'))) 584 constant_arg, var_arg = match.group("constant_arg", "var_arg") 585 if constant_arg: 586 args.append((False, resolve_string_literal(constant_arg))) 505 587 elif var_arg: 506 588 args.append((True, Variable(var_arg))) 507 589 filter_func = parser.find_filter(filter_name) … … 511 593 if upto != len(token): 512 594 raise TemplateSyntaxError("Could not parse the remainder: '%s' from '%s'" % (token[upto:], token)) 513 595 self.filters = filters 514 self.var = Variable(var)515 596 516 597 def resolve(self, context, ignore_failures=False): 517 try: 518 obj = self.var.resolve(context) 519 except VariableDoesNotExist: 520 if ignore_failures: 521 obj = None 522 else: 523 if settings.TEMPLATE_STRING_IF_INVALID: 524 global invalid_var_format_string 525 if invalid_var_format_string is None: 526 invalid_var_format_string = '%s' in settings.TEMPLATE_STRING_IF_INVALID 527 if invalid_var_format_string: 528 return settings.TEMPLATE_STRING_IF_INVALID % self.var 529 return settings.TEMPLATE_STRING_IF_INVALID 598 if isinstance(self.var, Variable): 599 try: 600 obj = self.var.resolve(context) 601 except VariableDoesNotExist: 602 if ignore_failures: 603 obj = None 530 604 else: 531 obj = settings.TEMPLATE_STRING_IF_INVALID 605 if settings.TEMPLATE_STRING_IF_INVALID: 606 global invalid_var_format_string 607 if invalid_var_format_string is None: 608 invalid_var_format_string = '%s' in settings.TEMPLATE_STRING_IF_INVALID 609 if invalid_var_format_string: 610 return settings.TEMPLATE_STRING_IF_INVALID % self.var 611 return settings.TEMPLATE_STRING_IF_INVALID 612 else: 613 obj = settings.TEMPLATE_STRING_IF_INVALID 614 else: 615 obj = self.var 532 616 for func, args in self.filters: 533 617 arg_vals = [] 534 618 for lookup, arg in args: … … 593 677 return Variable(path).resolve(context) 594 678 595 679 class Variable(object): 596 """680 r""" 597 681 A template variable, resolvable against a given context. The variable may be 598 682 a hard-coded string (if it begins and ends with single or double quote 599 683 marks):: … … 609 693 >>> c.article.section = 'News' 610 694 >>> Variable('article.section').resolve(c) 611 695 u'News' 696 >>> Variable(u'"News"').resolve(c) 697 u'News' 698 >>> Variable(u"'News'").resolve(c) 699 u'News' 612 700 613 701 (The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.') 702 703 Translated strings are also handled correctly:: 704 705 >>> Variable('_(article.section)').resolve(c) 706 u'News' 707 >>> Variable('_("Good News")').resolve(c) 708 u'Good News' 709 >>> Variable("_('Better News')").resolve(c) 710 u'Better News' 711 712 Escaped quotes work correctly as well:: 713 714 >>> Variable(ur'"Some \"Good\" News"').resolve(c) 715 u'Some "Good" News' 716 >>> Variable(ur"'Some \'Better\' News'").resolve(c) 717 u"Some 'Better' News" 614 718 """ 615 719 616 720 def __init__(self, var): … … 645 749 var = var[2:-1] 646 750 # If it's wrapped with quotes (single or double), then 647 751 # we're also dealing with a literal. 648 if var[0] in "\"'" and var[0] == var[-1]:649 self.literal = mark_safe( var[1:-1])650 e lse:752 try: 753 self.literal = mark_safe(unescape_string_literal(var)) 754 except ValueError: 651 755 # Otherwise we'll set self.lookups so that resolve() knows we're 652 756 # dealing with a bonafide variable 653 757 self.lookups = tuple(var.split(VARIABLE_ATTRIBUTE_SEPARATOR))