Ticket #13956: 13956.ttag_helpers_args_kwargs_support.3.diff
File 13956.ttag_helpers_args_kwargs_support.3.diff, 82.5 KB (added by , 13 years ago) |
---|
-
django/template/base.py
diff --git a/django/template/base.py b/django/template/base.py index c94eeb5..8642f4d 100644
a b from functools import partial 3 3 from inspect import getargspec 4 4 5 5 from django.conf import settings 6 from django.template.context import Context, RequestContext, ContextPopException 6 from django.template.context import (Context, RequestContext, 7 ContextPopException) 7 8 from django.utils.importlib import import_module 8 9 from django.utils.itercompat import is_iterable 9 from django.utils.text import smart_split, unescape_string_literal, get_text_list 10 from django.utils.text import (smart_split, unescape_string_literal, 11 get_text_list) 10 12 from django.utils.encoding import smart_unicode, force_unicode, smart_str 11 13 from django.utils.translation import ugettext_lazy 12 from django.utils.safestring import SafeData, EscapeData, mark_safe, mark_for_escaping 14 from django.utils.safestring import (SafeData, EscapeData, mark_safe, 15 mark_for_escaping) 13 16 from django.utils.formats import localize 14 17 from django.utils.html import escape 15 18 from django.utils.module_loading import module_has_submodule … … TOKEN_TEXT = 0 19 22 TOKEN_VAR = 1 20 23 TOKEN_BLOCK = 2 21 24 TOKEN_COMMENT = 3 25 TOKEN_MAPPING = { 26 TOKEN_TEXT: 'Text', 27 TOKEN_VAR: 'Var', 28 TOKEN_BLOCK: 'Block', 29 TOKEN_COMMENT: 'Comment', 30 } 22 31 23 32 # template syntax constants 24 33 FILTER_SEPARATOR = '|' … … TRANSLATOR_COMMENT_MARK = 'Translators' 34 43 SINGLE_BRACE_START = '{' 35 44 SINGLE_BRACE_END = '}' 36 45 37 ALLOWED_VARIABLE_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.' 46 ALLOWED_VARIABLE_CHARS = ('abcdefghijklmnopqrstuvwxyz' 47 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.') 38 48 39 49 # what to report as the origin for templates that come from non-loader sources 40 50 # (e.g. strings) 41 51 UNKNOWN_SOURCE = '<unknown source>' 42 52 43 # match a variable or block tag and capture the entire tag, including start/end delimiters 44 tag_re = re.compile('(%s.*?%s|%s.*?%s|%s.*?%s)' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END), 45 re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END), 46 re.escape(COMMENT_TAG_START), re.escape(COMMENT_TAG_END))) 53 # match a variable or block tag and capture the entire tag, including start/end 54 # delimiters 55 tag_re = (re.compile('(%s.*?%s|%s.*?%s|%s.*?%s)' % 56 (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END), 57 re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END), 58 re.escape(COMMENT_TAG_START), re.escape(COMMENT_TAG_END)))) 47 59 48 60 # global dictionary of libraries that have been loaded using get_library 49 61 libraries = {} … … class VariableDoesNotExist(Exception): 73 85 return unicode(self).encode('utf-8') 74 86 75 87 def __unicode__(self): 76 return self.msg % tuple([force_unicode(p, errors='replace') for p in self.params]) 88 return self.msg % tuple([force_unicode(p, errors='replace') 89 for p in self.params]) 77 90 78 91 class InvalidTemplateLibrary(Exception): 79 92 pass … … class StringOrigin(Origin): 97 110 return self.source 98 111 99 112 class Template(object): 100 def __init__(self, template_string, origin=None, name='<Unknown Template>'): 113 def __init__(self, template_string, origin=None, 114 name='<Unknown Template>'): 101 115 try: 102 116 template_string = smart_unicode(template_string) 103 117 except UnicodeDecodeError: 104 raise TemplateEncodingError("Templates can only be constructed from unicode or UTF-8 strings.") 118 raise TemplateEncodingError("Templates can only be constructed " 119 "from unicode or UTF-8 strings.") 105 120 if settings.TEMPLATE_DEBUG and origin is None: 106 121 origin = StringOrigin(template_string) 107 122 self.nodelist = compile_string(template_string, origin) … … def compile_string(template_string, origin): 136 151 137 152 class Token(object): 138 153 def __init__(self, token_type, contents): 139 # token_type must be TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK or TOKEN_COMMENT. 154 # token_type must be TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK or 155 # TOKEN_COMMENT. 140 156 self.token_type, self.contents = token_type, contents 141 157 self.lineno = None 142 158 143 159 def __str__(self): 144 return '<%s token: "%s...">' % \145 ({TOKEN_TEXT: 'Text', TOKEN_VAR: 'Var', TOKEN_BLOCK: 'Block', TOKEN_COMMENT: 'Comment'}[self.token_type],146 self.contents[:20].replace('\n', ''))160 token_name = TOKEN_MAPPING[self.token_type] 161 return ('<%s token: "%s...">' % 162 (token_name, self.contents[:20].replace('\n', ''))) 147 163 148 164 def split_contents(self): 149 165 split = [] … … class Lexer(object): 167 183 self.lineno = 1 168 184 169 185 def tokenize(self): 170 "Return a list of tokens from a given template_string." 186 """ 187 Return a list of tokens from a given template_string. 188 """ 171 189 in_tag = False 172 190 result = [] 173 191 for bit in tag_re.split(self.template_string): … … class Lexer(object): 184 202 """ 185 203 if in_tag: 186 204 if token_string.startswith(VARIABLE_TAG_START): 187 token = Token(TOKEN_VAR, token_string[len(VARIABLE_TAG_START):-len(VARIABLE_TAG_END)].strip()) 205 token = Token(TOKEN_VAR, 206 token_string[ 207 len(VARIABLE_TAG_START):-len(VARIABLE_TAG_END) 208 ].strip()) 188 209 elif token_string.startswith(BLOCK_TAG_START): 189 token = Token(TOKEN_BLOCK, token_string[len(BLOCK_TAG_START):-len(BLOCK_TAG_END)].strip()) 210 token = Token(TOKEN_BLOCK, 211 token_string[ 212 len(BLOCK_TAG_START):-len(BLOCK_TAG_END) 213 ].strip()) 190 214 elif token_string.startswith(COMMENT_TAG_START): 191 215 content = '' 192 216 if token_string.find(TRANSLATOR_COMMENT_MARK): 193 content = token_string[len(COMMENT_TAG_START):-len(COMMENT_TAG_END)].strip() 217 content = token_string[ 218 len(COMMENT_TAG_START):-len(COMMENT_TAG_END) 219 ].strip() 194 220 token = Token(TOKEN_COMMENT, content) 195 221 else: 196 222 token = Token(TOKEN_TEXT, token_string) … … class Parser(object): 207 233 self.add_library(lib) 208 234 209 235 def parse(self, parse_until=None): 210 if parse_until is None: parse_until = [] 236 if parse_until is None: 237 parse_until = [] 211 238 nodelist = self.create_nodelist() 212 239 while self.tokens: 213 240 token = self.next_token() … … class Parser(object): 218 245 self.empty_variable(token) 219 246 filter_expression = self.compile_filter(token.contents) 220 247 var_node = self.create_variable_node(filter_expression) 221 self.extend_nodelist(nodelist, var_node, token)248 self.extend_nodelist(nodelist, var_node, token) 222 249 elif token.token_type == TOKEN_BLOCK: 223 250 if token.contents in parse_until: 224 # put token back on token list so calling code knows why it terminated 251 # put token back on token list so calling 252 # code knows why it terminated 225 253 self.prepend_token(token) 226 254 return nodelist 227 255 try: 228 256 command = token.contents.split()[0] 229 257 except IndexError: 230 258 self.empty_block_tag(token) 231 # execute callback function for this tag and append resulting node 259 # execute callback function for this tag and append 260 # resulting node 232 261 self.enter_command(command, token) 233 262 try: 234 263 compile_func = self.tags[command] … … class Parser(object): 264 293 if nodelist.contains_nontext: 265 294 raise AttributeError 266 295 except AttributeError: 267 raise TemplateSyntaxError("%r must be the first tag in the template." % node) 296 raise TemplateSyntaxError("%r must be the first tag " 297 "in the template." % node) 268 298 if isinstance(nodelist, NodeList) and not isinstance(node, TextNode): 269 299 nodelist.contains_nontext = True 270 300 nodelist.append(node) … … class Parser(object): 286 316 287 317 def invalid_block_tag(self, token, command, parse_until=None): 288 318 if parse_until: 289 raise self.error(token, "Invalid block tag: '%s', expected %s" % (command, get_text_list(["'%s'" % p for p in parse_until]))) 319 raise self.error(token, "Invalid block tag: '%s', expected %s" % 320 (command, get_text_list(["'%s'" % p for p in parse_until]))) 290 321 raise self.error(token, "Invalid block tag: '%s'" % command) 291 322 292 323 def unclosed_block_tag(self, parse_until): 293 raise self.error(None, "Unclosed tags: %s " % 324 raise self.error(None, "Unclosed tags: %s " % ', '.join(parse_until)) 294 325 295 326 def compile_function_error(self, token, e): 296 327 pass … … class Parser(object): 309 340 self.filters.update(lib.filters) 310 341 311 342 def compile_filter(self, token): 312 "Convenient wrapper for FilterExpression" 343 """ 344 Convenient wrapper for FilterExpression 345 """ 313 346 return FilterExpression(token, self) 314 347 315 348 def find_filter(self, filter_name): … … class Parser(object): 320 353 321 354 class TokenParser(object): 322 355 """ 323 Subclass this and implement the top() method to parse a template line. When 324 instantiating the parser, pass in the line from the Django template parser. 356 Subclass this and implement the top() method to parse a template line. 357 When instantiating the parser, pass in the line from the Django template 358 parser. 325 359 326 360 The parser's "tagname" instance-variable stores the name of the tag that 327 361 the filter was called with. … … class TokenParser(object): 333 367 self.tagname = self.tag() 334 368 335 369 def top(self): 336 "Overload this method to do the actual parsing and return the result." 370 """ 371 Overload this method to do the actual parsing and return the result. 372 """ 337 373 raise NotImplementedError() 338 374 339 375 def more(self): 340 "Returns True if there is more stuff in the tag." 376 """ 377 Returns True if there is more stuff in the tag. 378 """ 341 379 return self.pointer < len(self.subject) 342 380 343 381 def back(self): 344 "Undoes the last microparser. Use this for lookahead and backtracking." 382 """ 383 Undoes the last microparser. Use this for lookahead and backtracking. 384 """ 345 385 if not len(self.backout): 346 raise TemplateSyntaxError("back called without some previous parsing") 386 raise TemplateSyntaxError("back called without some previous " 387 "parsing") 347 388 self.pointer = self.backout.pop() 348 389 349 390 def tag(self): 350 "A microparser that just returns the next tag from the line." 391 """ 392 A microparser that just returns the next tag from the line. 393 """ 351 394 subject = self.subject 352 395 i = self.pointer 353 396 if i >= len(subject): 354 raise TemplateSyntaxError("expected another tag, found end of string: %s" % subject) 397 raise TemplateSyntaxError("expected another tag, found " 398 "end of string: %s" % subject) 355 399 p = i 356 400 while i < len(subject) and subject[i] not in (' ', '\t'): 357 401 i += 1 … … class TokenParser(object): 363 407 return s 364 408 365 409 def value(self): 366 "A microparser that parses for a value: some string constant or variable name." 410 """ 411 A microparser that parses for a value: some string constant or 412 variable name. 413 """ 367 414 subject = self.subject 368 415 i = self.pointer 369 416 370 417 def next_space_index(subject, i): 371 "Increment pointer until a real space (i.e. a space not within quotes) is encountered" 418 """ 419 Increment pointer until a real space (i.e. a space not within 420 quotes) is encountered 421 """ 372 422 while i < len(subject) and subject[i] not in (' ', '\t'): 373 423 if subject[i] in ('"', "'"): 374 424 c = subject[i] … … class TokenParser(object): 376 426 while i < len(subject) and subject[i] != c: 377 427 i += 1 378 428 if i >= len(subject): 379 raise TemplateSyntaxError("Searching for value. Unexpected end of string in column %d: %s" % (i, subject)) 429 raise TemplateSyntaxError("Searching for value. " 430 "Unexpected end of string in column %d: %s" % 431 (i, subject)) 380 432 i += 1 381 433 return i 382 434 383 435 if i >= len(subject): 384 raise TemplateSyntaxError("Searching for value. Expected another value but found end of string: %s" % subject) 436 raise TemplateSyntaxError("Searching for value. Expected another " 437 "value but found end of string: %s" % 438 subject) 385 439 if subject[i] in ('"', "'"): 386 440 p = i 387 441 i += 1 388 442 while i < len(subject) and subject[i] != subject[p]: 389 443 i += 1 390 444 if i >= len(subject): 391 raise TemplateSyntaxError("Searching for value. Unexpected end of string in column %d: %s" % (i, subject)) 445 raise TemplateSyntaxError("Searching for value. Unexpected " 446 "end of string in column %d: %s" % 447 (i, subject)) 392 448 i += 1 393 449 394 # Continue parsing until next "real" space, so that filters are also included 450 # Continue parsing until next "real" space, 451 # so that filters are also included 395 452 i = next_space_index(subject, i) 396 453 397 454 res = subject[p:i] … … constant_string = r""" 419 476 %(strdq)s| 420 477 %(strsq)s) 421 478 """ % { 422 'strdq': r'"[^"\\]*(?:\\.[^"\\]*)*"', # double-quoted string423 'strsq': r"'[^'\\]*(?:\\.[^'\\]*)*'", # single-quoted string424 'i18n_open' 425 'i18n_close' 479 'strdq': r'"[^"\\]*(?:\\.[^"\\]*)*"', # double-quoted string 480 'strsq': r"'[^'\\]*(?:\\.[^'\\]*)*'", # single-quoted string 481 'i18n_open': re.escape("_("), 482 'i18n_close': re.escape(")"), 426 483 } 427 484 constant_string = constant_string.replace("\n", "") 428 485 … … filter_raw_string = r""" 440 497 )""" % { 441 498 'constant': constant_string, 442 499 'num': r'[-+\.]?\d[\d\.e]*', 443 'var_chars': "\w\." 500 'var_chars': "\w\.", 444 501 'filter_sep': re.escape(FILTER_SEPARATOR), 445 502 'arg_sep': re.escape(FILTER_ARGUMENT_SEPARATOR), 446 503 } 447 504 448 filter_re = re.compile(filter_raw_string, re.UNICODE |re.VERBOSE)505 filter_re = re.compile(filter_raw_string, re.UNICODE | re.VERBOSE) 449 506 450 507 class FilterExpression(object): 451 r"""508 """ 452 509 Parses a variable token and its optional filters (all as a single string), 453 510 and return a list of tuples of the filter name and arguments. 454 Sample: 511 Sample:: 512 455 513 >>> token = 'variable|default:"Default value"|date:"Y-m-d"' 456 514 >>> p = Parser('') 457 515 >>> fe = FilterExpression(token, p) … … class FilterExpression(object): 472 530 for match in matches: 473 531 start = match.start() 474 532 if upto != start: 475 raise TemplateSyntaxError("Could not parse some characters: %s|%s|%s" % \ 476 (token[:upto], token[upto:start], token[start:])) 533 raise TemplateSyntaxError("Could not parse some characters: " 534 "%s|%s|%s" % 535 (token[:upto], token[upto:start], 536 token[start:])) 477 537 if var_obj is None: 478 538 var, constant = match.group("var", "constant") 479 539 if constant: … … class FilterExpression(object): 482 542 except VariableDoesNotExist: 483 543 var_obj = None 484 544 elif var is None: 485 raise TemplateSyntaxError("Could not find variable at start of %s." % token) 545 raise TemplateSyntaxError("Could not find variable at " 546 "start of %s." % token) 486 547 else: 487 548 var_obj = Variable(var) 488 549 else: … … class FilterExpression(object): 498 559 filters.append((filter_func, args)) 499 560 upto = match.end() 500 561 if upto != len(token): 501 raise TemplateSyntaxError("Could not parse the remainder: '%s' from '%s'" % (token[upto:], token)) 562 raise TemplateSyntaxError("Could not parse the remainder: '%s' " 563 "from '%s'" % (token[upto:], token)) 502 564 503 565 self.filters = filters 504 566 self.var = var_obj … … class FilterExpression(object): 559 621 provided.pop(0) 560 622 except IndexError: 561 623 # Not enough 562 raise TemplateSyntaxError("%s requires %d arguments, %d provided" % (name, len(nondefs), plen)) 624 raise TemplateSyntaxError("%s requires %d arguments, %d provided" % 625 (name, len(nondefs), plen)) 563 626 564 627 # Defaults can be overridden. 565 628 defaults = defaults and list(defaults) or [] … … class FilterExpression(object): 568 631 defaults.pop(0) 569 632 except IndexError: 570 633 # Too many. 571 raise TemplateSyntaxError("%s requires %d arguments, %d provided" % (name, len(nondefs), plen)) 634 raise TemplateSyntaxError("%s requires %d arguments, %d provided" % 635 (name, len(nondefs), plen)) 572 636 573 637 return True 574 638 args_check = staticmethod(args_check) … … def resolve_variable(path, context): 586 650 return Variable(path).resolve(context) 587 651 588 652 class Variable(object): 589 r"""590 A template variable, resolvable against a given context. The variable may be591 a hard-coded string (if it begins and ends with single or double quote653 """ 654 A template variable, resolvable against a given context. The variable may 655 be a hard-coded string (if it begins and ends with single or double quote 592 656 marks):: 593 657 594 658 >>> c = {'article': {'section':u'News'}} … … class Variable(object): 642 706 # Otherwise we'll set self.lookups so that resolve() knows we're 643 707 # dealing with a bonafide variable 644 708 if var.find(VARIABLE_ATTRIBUTE_SEPARATOR + '_') > -1 or var[0] == '_': 645 raise TemplateSyntaxError("Variables and attributes may not begin with underscores: '%s'" % var) 709 raise TemplateSyntaxError("Variables and attributes may " 710 "not begin with underscores: '%s'" % 711 var) 646 712 self.lookups = tuple(var.split(VARIABLE_ATTRIBUTE_SEPARATOR)) 647 713 648 714 def resolve(self, context): … … class Variable(object): 673 739 instead. 674 740 """ 675 741 current = context 676 try: # catch-all for silent variable failures742 try: # catch-all for silent variable failures 677 743 for bit in self.lookups: 678 try: # dictionary lookup744 try: # dictionary lookup 679 745 current = current[bit] 680 746 except (TypeError, AttributeError, KeyError): 681 try: # attribute lookup747 try: # attribute lookup 682 748 current = getattr(current, bit) 683 749 except (TypeError, AttributeError): 684 try: # list-index lookup750 try: # list-index lookup 685 751 current = current[int(bit)] 686 except (IndexError, # list index out of range 687 ValueError, # invalid literal for int() 688 KeyError, # current is a dict without `int(bit)` key 689 TypeError, # unsubscriptable object 690 ): 691 raise VariableDoesNotExist("Failed lookup for key [%s] in %r", (bit, current)) # missing attribute 752 except (IndexError, # list index out of range 753 ValueError, # invalid literal for int() 754 KeyError, # current is a dict without `int(bit)` key 755 TypeError): # unsubscriptable object 756 raise VariableDoesNotExist("Failed lookup for key " 757 "[%s] in %r", 758 (bit, current)) # missing attribute 692 759 if callable(current): 693 760 if getattr(current, 'do_not_call_in_templates', False): 694 761 pass … … class Variable(object): 700 767 except TypeError: # arguments *were* required 701 768 # GOTCHA: This will also catch any TypeError 702 769 # raised in the function itself. 703 current = settings.TEMPLATE_STRING_IF_INVALID # invalid method call770 current = settings.TEMPLATE_STRING_IF_INVALID # invalid method call 704 771 except Exception, e: 705 772 if getattr(e, 'silent_variable_failure', False): 706 773 current = settings.TEMPLATE_STRING_IF_INVALID … … class Node(object): 716 783 child_nodelists = ('nodelist',) 717 784 718 785 def render(self, context): 719 "Return the node rendered as a string" 786 """ 787 Return the node rendered as a string. 788 """ 720 789 pass 721 790 722 791 def __iter__(self): 723 792 yield self 724 793 725 794 def get_nodes_by_type(self, nodetype): 726 "Return a list of all nodes (within this node and its nodelist) of the given type" 795 """ 796 Return a list of all nodes (within this node and its nodelist) 797 of the given type 798 """ 727 799 nodes = [] 728 800 if isinstance(self, nodetype): 729 801 nodes.append(self) … … def _render_value_in_context(value, context): 776 848 """ 777 849 value = localize(value, use_l10n=context.use_l10n) 778 850 value = force_unicode(value) 779 if (context.autoescape and not isinstance(value, SafeData)) or isinstance(value, EscapeData): 851 if ((context.autoescape and not isinstance(value, SafeData)) or 852 isinstance(value, EscapeData)): 780 853 return escape(value) 781 854 else: 782 855 return value … … class VariableNode(Node): 793 866 output = self.filter_expression.resolve(context) 794 867 except UnicodeDecodeError: 795 868 # Unicode conversion can fail sometimes for reasons out of our 796 # control (e.g. exception rendering). In that case, we fail quietly. 869 # control (e.g. exception rendering). In that case, we fail 870 # quietly. 797 871 return '' 798 872 return _render_value_in_context(output, context) 799 873 800 def generic_tag_compiler(params, defaults, name, node_class, parser, token): 801 "Returns a template.Node subclass." 802 bits = token.split_contents()[1:] 803 bmax = len(params) 804 def_len = defaults and len(defaults) or 0 805 bmin = bmax - def_len 806 if(len(bits) < bmin or len(bits) > bmax): 807 if bmin == bmax: 808 message = "%s takes %s arguments" % (name, bmin) 874 # Regex for token keyword arguments 875 kwarg_re = re.compile(r"(?:(\w+)=)?(.+)") 876 877 def token_kwargs(bits, parser, support_legacy=False): 878 """ 879 A utility method for parsing token keyword arguments. 880 881 :param bits: A list containing remainder of the token (split by spaces) 882 that is to be checked for arguments. Valid arguments will be removed 883 from this list. 884 885 :param support_legacy: If set to true ``True``, the legacy format 886 ``1 as foo`` will be accepted. Otherwise, only the standard ``foo=1`` 887 format is allowed. 888 889 :returns: A dictionary of the arguments retrieved from the ``bits`` token 890 list. 891 892 There is no requirement for all remaining token ``bits`` to be keyword 893 arguments, so the dictionary will be returned as soon as an invalid 894 argument format is reached. 895 """ 896 if not bits: 897 return {} 898 match = kwarg_re.match(bits[0]) 899 kwarg_format = match and match.group(1) 900 if not kwarg_format: 901 if not support_legacy: 902 return {} 903 if len(bits) < 3 or bits[1] != 'as': 904 return {} 905 906 kwargs = {} 907 while bits: 908 if kwarg_format: 909 match = kwarg_re.match(bits[0]) 910 if not match or not match.group(1): 911 return kwargs 912 key, value = match.groups() 913 del bits[:1] 914 else: 915 if len(bits) < 3 or bits[1] != 'as': 916 return kwargs 917 key, value = bits[2], bits[0] 918 del bits[:3] 919 kwargs[key] = parser.compile_filter(value) 920 if bits and not kwarg_format: 921 if bits[0] != 'and': 922 return kwargs 923 del bits[:1] 924 return kwargs 925 926 def parse_bits(parser, bits, params, varargs, varkw, defaults, 927 takes_context, name): 928 """ 929 Parses bits for template tag helpers (simple_tag, include_tag and 930 assignment_tag), in particular by detecting syntax errors and by 931 extracting positional and keyword arguments. 932 """ 933 if takes_context: 934 if params[0] == 'context': 935 params = params[1:] 936 else: 937 raise TemplateSyntaxError( 938 "'%s' is decorated with takes_context=True so it must " 939 "have a first argument of 'context'" % name) 940 args = [] 941 kwargs = {} 942 unhandled_params = list(params) 943 for bit in bits: 944 # First we try to extract a potential kwarg from the bit 945 kwarg = token_kwargs([bit], parser) 946 if kwarg: 947 # The kwarg was successfully extracted 948 param, value = kwarg.items()[0] 949 if param not in params and varkw is None: 950 # An unexpected keyword argument was supplied 951 raise TemplateSyntaxError( 952 "'%s' received unexpected keyword argument '%s'" % 953 (name, param)) 954 elif param in kwargs: 955 # The keyword argument has already been supplied once 956 raise TemplateSyntaxError( 957 "'%s' received multiple values for keyword argument '%s'" % 958 (name, param)) 959 else: 960 # All good, record the keyword argument 961 kwargs[str(param)] = value 962 if param in unhandled_params: 963 # If using the keyword syntax for a positional arg, then 964 # consume it. 965 unhandled_params.remove(param) 809 966 else: 810 message = "%s takes between %s and %s arguments" % (name, bmin, bmax) 811 raise TemplateSyntaxError(message) 812 return node_class(bits) 967 if kwargs: 968 raise TemplateSyntaxError( 969 "'%s' received some positional argument(s) after some " 970 "keyword argument(s)" % name) 971 else: 972 # Record the positional argument 973 args.append(parser.compile_filter(bit)) 974 try: 975 # Consume from the list of expected positional arguments 976 unhandled_params.pop(0) 977 except IndexError: 978 if varargs is None: 979 raise TemplateSyntaxError( 980 "'%s' received too many positional arguments" % 981 name) 982 if defaults is not None: 983 # Consider the last n params handled, where n is the 984 # number of defaults. 985 unhandled_params = unhandled_params[:-len(defaults)] 986 if unhandled_params: 987 # Some positional arguments were not supplied 988 raise TemplateSyntaxError( 989 u"'%s' did not receive value(s) for the argument(s): %s" % 990 (name, u", ".join([u"'%s'" % p for p in unhandled_params]))) 991 return args, kwargs 992 993 def generic_tag_compiler(parser, token, params, varargs, varkw, defaults, 994 name, takes_context, node_class): 995 """ 996 Returns a template.Node subclass. 997 """ 998 bits = token.split_contents()[1:] 999 args, kwargs = parse_bits(parser, bits, params, varargs, varkw, 1000 defaults, takes_context, name) 1001 return node_class(takes_context, args, kwargs) 1002 1003 class TagHelperNode(Node): 1004 """ 1005 Base class for tag helper nodes such as SimpleNode, InclusionNode and 1006 AssignmentNode. Manages the positional and keyword arguments to be passed 1007 to the decorated function. 1008 """ 1009 1010 def __init__(self, takes_context, args, kwargs): 1011 self.takes_context = takes_context 1012 self.args = args 1013 self.kwargs = kwargs 1014 1015 def get_resolved_arguments(self, context): 1016 resolved_args = [var.resolve(context) for var in self.args] 1017 if self.takes_context: 1018 resolved_args = [context] + resolved_args 1019 resolved_kwargs = dict((k, v.resolve(context)) 1020 for k, v in self.kwargs.items()) 1021 return resolved_args, resolved_kwargs 813 1022 814 1023 class Library(object): 815 1024 def __init__(self): … … class Library(object): 817 1026 self.tags = {} 818 1027 819 1028 def tag(self, name=None, compile_function=None): 820 if name == None and compile_function ==None:1029 if name is None and compile_function is None: 821 1030 # @register.tag() 822 1031 return self.tag_function 823 elif name != None and compile_function ==None:1032 elif name is not None and compile_function is None: 824 1033 if callable(name): 825 1034 # @register.tag 826 1035 return self.tag_function(name) … … class Library(object): 829 1038 def dec(func): 830 1039 return self.tag(name, func) 831 1040 return dec 832 elif name != None and compile_function !=None:1041 elif name is not None and compile_function is not None: 833 1042 # register.tag('somename', somefunc) 834 1043 self.tags[name] = compile_function 835 1044 return compile_function 836 1045 else: 837 raise InvalidTemplateLibrary("Unsupported arguments to Library.tag: (%r, %r)", (name, compile_function)) 1046 raise InvalidTemplateLibrary("Unsupported arguments to " 1047 "Library.tag: (%r, %r)", (name, compile_function)) 838 1048 839 def tag_function(self, func):1049 def tag_function(self, func): 840 1050 self.tags[getattr(func, "_decorated_function", func).__name__] = func 841 1051 return func 842 1052 843 1053 def filter(self, name=None, filter_func=None): 844 if name == None and filter_func ==None:1054 if name is None and filter_func is None: 845 1055 # @register.filter() 846 1056 return self.filter_function 847 elif filter_func ==None:1057 elif filter_func is None: 848 1058 if callable(name): 849 1059 # @register.filter 850 1060 return self.filter_function(name) … … class Library(object): 853 1063 def dec(func): 854 1064 return self.filter(name, func) 855 1065 return dec 856 elif name != None and filter_func !=None:1066 elif name is not None and filter_func is not None: 857 1067 # register.filter('somename', somefunc) 858 1068 self.filters[name] = filter_func 859 1069 return filter_func 860 1070 else: 861 raise InvalidTemplateLibrary("Unsupported arguments to Library.filter: (%r, %r)", (name, filter_func)) 1071 raise InvalidTemplateLibrary("Unsupported arguments to " 1072 "Library.filter: (%r, %r)", (name, filter_func)) 862 1073 863 1074 def filter_function(self, func): 864 1075 self.filters[getattr(func, "_decorated_function", func).__name__] = func … … class Library(object): 866 1077 867 1078 def simple_tag(self, func=None, takes_context=None, name=None): 868 1079 def dec(func): 869 params, xx, xxx, defaults = getargspec(func) 870 if takes_context: 871 if params[0] == 'context': 872 params = params[1:] 873 else: 874 raise TemplateSyntaxError("Any tag function decorated with takes_context=True must have a first argument of 'context'") 1080 params, varargs, varkw, defaults = getargspec(func) 875 1081 876 class SimpleNode(Node): 877 def __init__(self, vars_to_resolve): 878 self.vars_to_resolve = map(Variable, vars_to_resolve) 1082 class SimpleNode(TagHelperNode): 879 1083 880 1084 def render(self, context): 881 resolved_ vars = [var.resolve(context) for var in self.vars_to_resolve]882 if takes_context:883 func_args = [context] + resolved_vars 884 else:885 func_args = resolved_vars886 return func(*func_args)887 888 function_name = name or getattr(func, '_decorated_function', func).__name__889 compile_func = partial(generic_tag_compiler, params, defaults, function_name,SimpleNode)1085 resolved_args, resolved_kwargs = self.get_resolved_arguments(context) 1086 return func(*resolved_args, **resolved_kwargs) 1087 1088 function_name = (name or 1089 getattr(func, '_decorated_function', func).__name__) 1090 compile_func = partial(generic_tag_compiler, 1091 params=params, varargs=varargs, varkw=varkw, 1092 defaults=defaults, name=function_name, 1093 takes_context=takes_context, node_class=SimpleNode) 890 1094 compile_func.__doc__ = func.__doc__ 891 1095 self.tag(function_name, compile_func) 892 1096 return func … … class Library(object): 902 1106 903 1107 def assignment_tag(self, func=None, takes_context=None, name=None): 904 1108 def dec(func): 905 params, xx, xxx, defaults = getargspec(func) 906 if takes_context: 907 if params[0] == 'context': 908 params = params[1:] 909 else: 910 raise TemplateSyntaxError("Any tag function decorated with takes_context=True must have a first argument of 'context'") 1109 params, varargs, varkw, defaults = getargspec(func) 911 1110 912 class AssignmentNode( Node):913 def __init__(self, params_vars, target_var):914 s elf.params_vars = map(Variable, params_vars)1111 class AssignmentNode(TagHelperNode): 1112 def __init__(self, takes_context, args, kwargs, target_var): 1113 super(AssignmentNode, self).__init__(takes_context, args, kwargs) 915 1114 self.target_var = target_var 916 1115 917 1116 def render(self, context): 918 resolved_vars = [var.resolve(context) for var in self.params_vars] 919 if takes_context: 920 func_args = [context] + resolved_vars 921 else: 922 func_args = resolved_vars 923 context[self.target_var] = func(*func_args) 1117 resolved_args, resolved_kwargs = self.get_resolved_arguments(context) 1118 context[self.target_var] = func(*resolved_args, **resolved_kwargs) 924 1119 return '' 925 1120 1121 function_name = (name or 1122 getattr(func, '_decorated_function', func).__name__) 1123 926 1124 def compile_func(parser, token): 927 bits = token.split_contents() 928 tag_name = bits[0] 929 bits = bits[1:] 930 params_max = len(params) 931 defaults_length = defaults and len(defaults) or 0 932 params_min = params_max - defaults_length 933 if (len(bits) < 2 or bits[-2] != 'as'): 1125 bits = token.split_contents()[1:] 1126 if len(bits) < 2 or bits[-2] != 'as': 934 1127 raise TemplateSyntaxError( 935 1128 "'%s' tag takes at least 2 arguments and the " 936 "second last argument must be 'as'" % tag_name) 937 params_vars = bits[:-2] 1129 "second last argument must be 'as'" % function_name) 938 1130 target_var = bits[-1] 939 if (len(params_vars) < params_min or 940 len(params_vars) > params_max): 941 if params_min == params_max: 942 raise TemplateSyntaxError( 943 "%s takes %s arguments" % (tag_name, params_min)) 944 else: 945 raise TemplateSyntaxError( 946 "%s takes between %s and %s arguments" 947 % (tag_name, params_min, params_max)) 948 return AssignmentNode(params_vars, target_var) 1131 bits = bits[:-2] 1132 args, kwargs = parse_bits(parser, bits, params, 1133 varargs, varkw, defaults, takes_context, function_name) 1134 return AssignmentNode(takes_context, args, kwargs, target_var) 949 1135 950 function_name = name or getattr(func, '_decorated_function', func).__name__951 1136 compile_func.__doc__ = func.__doc__ 952 1137 self.tag(function_name, compile_func) 953 1138 return func … … class Library(object): 963 1148 964 1149 def inclusion_tag(self, file_name, context_class=Context, takes_context=False, name=None): 965 1150 def dec(func): 966 params, xx, xxx, defaults = getargspec(func) 967 if takes_context: 968 if params[0] == 'context': 969 params = params[1:] 970 else: 971 raise TemplateSyntaxError("Any tag function decorated with takes_context=True must have a first argument of 'context'") 1151 params, varargs, varkw, defaults = getargspec(func) 972 1152 973 class InclusionNode(Node): 974 def __init__(self, vars_to_resolve): 975 self.vars_to_resolve = map(Variable, vars_to_resolve) 1153 class InclusionNode(TagHelperNode): 976 1154 977 1155 def render(self, context): 978 resolved_vars = [var.resolve(context) for var in self.vars_to_resolve] 979 if takes_context: 980 args = [context] + resolved_vars 981 else: 982 args = resolved_vars 983 984 dict = func(*args) 1156 resolved_args, resolved_kwargs = self.get_resolved_arguments(context) 1157 _dict = func(*resolved_args, **resolved_kwargs) 985 1158 986 1159 if not getattr(self, 'nodelist', False): 987 1160 from django.template.loader import get_template, select_template … … class Library(object): 992 1165 else: 993 1166 t = get_template(file_name) 994 1167 self.nodelist = t.nodelist 995 new_context = context_class( dict, **{1168 new_context = context_class(_dict, **{ 996 1169 'autoescape': context.autoescape, 997 1170 'current_app': context.current_app, 998 1171 'use_l10n': context.use_l10n, 999 1172 }) 1000 # Copy across the CSRF token, if present, because inclusion 1001 # tags are often used for forms, and we need instructions 1002 # for using CSRF protection to be as simple as possible. 1173 # Copy across the CSRF token, if present, because 1174 # inclusion tags are often used for forms, and we need 1175 # instructions for using CSRF protection to be as simple 1176 # as possible. 1003 1177 csrf_token = context.get('csrf_token', None) 1004 1178 if csrf_token is not None: 1005 1179 new_context['csrf_token'] = csrf_token 1006 1180 return self.nodelist.render(new_context) 1007 1181 1008 function_name = name or getattr(func, '_decorated_function', func).__name__ 1009 compile_func = partial(generic_tag_compiler, params, defaults, function_name, InclusionNode) 1182 function_name = (name or 1183 getattr(func, '_decorated_function', func).__name__) 1184 compile_func = partial(generic_tag_compiler, 1185 params=params, varargs=varargs, varkw=varkw, 1186 defaults=defaults, name=function_name, 1187 takes_context=takes_context, node_class=InclusionNode) 1010 1188 compile_func.__doc__ = func.__doc__ 1011 1189 self.tag(function_name, compile_func) 1012 1190 return func 1013 1191 return dec 1014 1192 1015 1193 def import_library(taglib_module): 1016 """Load a template tag library module. 1194 """ 1195 Load a template tag library module. 1017 1196 1018 1197 Verifies that the library contains a 'register' attribute, and 1019 1198 returns that attribute as the representation of the library 1020 1199 """ 1021 app_path, taglib = taglib_module.rsplit('.', 1)1200 app_path, taglib = taglib_module.rsplit('.', 1) 1022 1201 app_module = import_module(app_path) 1023 1202 try: 1024 1203 mod = import_module(taglib_module) 1025 1204 except ImportError, e: 1026 # If the ImportError is because the taglib submodule does not exist, that's not 1027 # an error that should be raised. If the submodule exists and raised an ImportError 1028 # on the attempt to load it, that we want to raise. 1205 # If the ImportError is because the taglib submodule does not exist, 1206 # that's not an error that should be raised. If the submodule exists 1207 # and raised an ImportError on the attempt to load it, that we want 1208 # to raise. 1029 1209 if not module_has_submodule(app_module, taglib): 1030 1210 return None 1031 1211 else: 1032 raise InvalidTemplateLibrary("ImportError raised loading %s: %s" % (taglib_module, e)) 1212 raise InvalidTemplateLibrary("ImportError raised loading %s: %s" % 1213 (taglib_module, e)) 1033 1214 try: 1034 1215 return mod.register 1035 1216 except AttributeError: 1036 raise InvalidTemplateLibrary("Template library %s does not have a variable named 'register'" % taglib_module) 1217 raise InvalidTemplateLibrary("Template library %s does not have " 1218 "a variable named 'register'" % 1219 taglib_module) 1037 1220 1038 1221 templatetags_modules = [] 1039 1222 1040 1223 def get_templatetags_modules(): 1041 """Return the list of all available template tag modules. 1224 """ 1225 Return the list of all available template tag modules. 1042 1226 1043 1227 Caches the result for faster access. 1044 1228 """ 1045 1229 global templatetags_modules 1046 1230 if not templatetags_modules: 1047 1231 _templatetags_modules = [] 1048 # Populate list once per process. Mutate the local list first, and then1049 # assign it to the global name to ensure there are no cases where two1050 # t hreads try to populate it simultaneously.1232 # Populate list once per process. Mutate the local list first, and 1233 # then assign it to the global name to ensure there are no cases where 1234 # two threads try to populate it simultaneously. 1051 1235 for app_module in ['django'] + list(settings.INSTALLED_APPS): 1052 1236 try: 1053 1237 templatetag_module = '%s.templatetags' % app_module … … def get_library(library_name): 1062 1246 """ 1063 1247 Load the template library module with the given name. 1064 1248 1065 If library is not already loaded loop over all templatetags modules to locate it. 1249 If library is not already loaded loop over all templatetags modules 1250 to locate it. 1066 1251 1067 1252 {% load somelib %} and {% load someotherlib %} loops twice. 1068 1253 1069 Subsequent loads eg. {% load somelib %} in the same process will grab the cached1070 module from libraries.1254 Subsequent loads eg. {% load somelib %} in the same process will grab 1255 the cached module from libraries. 1071 1256 """ 1072 1257 lib = libraries.get(library_name, None) 1073 1258 if not lib: … … def get_library(library_name): 1081 1266 libraries[library_name] = lib 1082 1267 break 1083 1268 if not lib: 1084 raise InvalidTemplateLibrary("Template library %s not found, tried %s" % (library_name, ','.join(tried_modules))) 1269 raise InvalidTemplateLibrary("Template library %s not found, " 1270 "tried %s" % 1271 (library_name, 1272 ','.join(tried_modules))) 1085 1273 return lib 1086 1274 1275 1087 1276 def add_to_builtins(module): 1088 1277 builtins.append(import_library(module)) 1089 1278 1279 1090 1280 add_to_builtins('django.template.defaulttags') 1091 1281 add_to_builtins('django.template.defaultfilters') -
django/template/defaulttags.py
diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py index 0b039a5..9620d1c 100644
a b from django.template.base import (Node, NodeList, Template, Library, 10 10 TemplateSyntaxError, VariableDoesNotExist, InvalidTemplateLibrary, 11 11 BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, 12 12 SINGLE_BRACE_START, SINGLE_BRACE_END, COMMENT_TAG_START, COMMENT_TAG_END, 13 get_library )13 get_library, token_kwargs, kwarg_re) 14 14 from django.template.smartif import IfParser, Literal 15 15 from django.template.defaultfilters import date 16 16 from django.utils.encoding import smart_str, smart_unicode 17 17 from django.utils.safestring import mark_safe 18 18 19 19 register = Library() 20 # Regex for token keyword arguments21 kwarg_re = re.compile(r"(?:(\w+)=)?(.+)")22 23 def token_kwargs(bits, parser, support_legacy=False):24 """25 A utility method for parsing token keyword arguments.26 27 :param bits: A list containing remainder of the token (split by spaces)28 that is to be checked for arguments. Valid arguments will be removed29 from this list.30 31 :param support_legacy: If set to true ``True``, the legacy format32 ``1 as foo`` will be accepted. Otherwise, only the standard ``foo=1``33 format is allowed.34 35 :returns: A dictionary of the arguments retrieved from the ``bits`` token36 list.37 38 There is no requirement for all remaining token ``bits`` to be keyword39 arguments, so the dictionary will be returned as soon as an invalid40 argument format is reached.41 """42 if not bits:43 return {}44 match = kwarg_re.match(bits[0])45 kwarg_format = match and match.group(1)46 if not kwarg_format:47 if not support_legacy:48 return {}49 if len(bits) < 3 or bits[1] != 'as':50 return {}51 52 kwargs = {}53 while bits:54 if kwarg_format:55 match = kwarg_re.match(bits[0])56 if not match or not match.group(1):57 return kwargs58 key, value = match.groups()59 del bits[:1]60 else:61 if len(bits) < 3 or bits[1] != 'as':62 return kwargs63 key, value = bits[2], bits[0]64 del bits[:3]65 kwargs[key] = parser.compile_filter(value)66 if bits and not kwarg_format:67 if bits[0] != 'and':68 return kwargs69 del bits[:1]70 return kwargs71 20 72 21 class AutoEscapeControlNode(Node): 73 22 """Implements the actions of the autoescape tag.""" -
docs/howto/custom-template-tags.txt
diff --git a/docs/howto/custom-template-tags.txt b/docs/howto/custom-template-tags.txt index eaab36c..4001794 100644
a b If you need to rename your tag, you can provide a custom name for it:: 698 698 def some_function(value): 699 699 return value - 1 700 700 701 .. versionadded:: 1.4 702 703 ``simple_tag`` functions may accept any number of positional or keyword 704 arguments. For example: 705 706 .. code-block:: python 707 708 @register.simple_tag 709 def my_tag(a, b, *args, **kwargs): 710 warning = kwargs['warning'] 711 profile = kwargs['profile'] 712 ... 713 return ... 714 715 Then in the template any number of arguments, separated by spaces, may be 716 passed to the template tag. Like in Python, the values for keyword arguments 717 are set using the equal sign ("``=``") and must be provided after the positional 718 arguments. For example: 719 720 .. code-block:: html+django 721 722 {% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile %} 723 701 724 .. _howto-custom-template-tags-assignment-tags: 702 725 703 726 Assignment tags … … Or, using decorator syntax: 761 784 For more information on how the ``takes_context`` option works, see the section 762 785 on :ref:`inclusion tags<howto-custom-template-tags-inclusion-tags>`. 763 786 787 ``assignment_tag`` functions may accept any number of positional or keyword 788 arguments. For example: 789 790 .. code-block:: python 791 792 @register.assignment_tag 793 def my_tag(a, b, *args, **kwargs): 794 warning = kwargs['warning'] 795 profile = kwargs['profile'] 796 ... 797 return ... 798 799 Then in the template any number of arguments, separated by spaces, may be 800 passed to the template tag. Like in Python, the values for keyword arguments 801 are set using the equal sign ("``=``") and must be provided after the positional 802 arguments. For example: 803 804 .. code-block:: html+django 805 806 {% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile as the_result %} 807 764 808 .. _howto-custom-template-tags-inclusion-tags: 765 809 766 810 Inclusion tags … … The ``takes_context`` parameter defaults to ``False``. When it's set to *True*, 884 928 the tag is passed the context object, as in this example. That's the only 885 929 difference between this case and the previous ``inclusion_tag`` example. 886 930 931 .. versionadded:: 1.4 932 933 ``inclusion_tag`` functions may accept any number of positional or keyword 934 arguments. For example: 935 936 .. code-block:: python 937 938 @register.inclusion_tag('my_template.html') 939 def my_tag(a, b, *args, **kwargs): 940 warning = kwargs['warning'] 941 profile = kwargs['profile'] 942 ... 943 return ... 944 945 Then in the template any number of arguments, separated by spaces, may be 946 passed to the template tag. Like in Python, the values for keyword arguments 947 are set using the equal sign ("``=``") and must be provided after the positional 948 arguments. For example: 949 950 .. code-block:: html+django 951 952 {% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile %} 953 887 954 Setting a variable in the context 888 955 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 889 956 -
docs/releases/1.4.txt
diff --git a/docs/releases/1.4.txt b/docs/releases/1.4.txt index 9cf268a..29fd5ef 100644
a b A new helper function, 162 162 ``template.Library`` to ease the creation of template tags that store some 163 163 data in a specified context variable. 164 164 165 ``*args`` and ``**kwargs`` support for template tag helper functions 166 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 167 168 :ref:`simple_tag<howto-custom-template-tags-simple-tags>`, :ref:`inclusion_tag 169 <howto-custom-template-tags-inclusion-tags>` and the newly introduced 170 :ref:`assignment_tag<howto-custom-template-tags-assignment-tags>` template 171 helper functions may now accept any number of positional or keyword arguments. 172 For example: 173 174 .. code-block:: python 175 176 @register.simple_tag 177 def my_tag(a, b, *args, **kwargs): 178 warning = kwargs['warning'] 179 profile = kwargs['profile'] 180 ... 181 return ... 182 183 Then in the template any number of arguments may be passed to the template tag. 184 For example: 185 186 .. code-block:: html+django 187 188 {% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile %} 189 165 190 ``truncatechars`` template filter 166 191 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 167 192 -
tests/regressiontests/templates/custom.py
diff --git a/tests/regressiontests/templates/custom.py b/tests/regressiontests/templates/custom.py index d781874..c05229e 100644
a b class CustomTagTests(TestCase): 35 35 t = template.Template('{% load custom %}{% params_and_context 37 %}') 36 36 self.assertEqual(t.render(c), u'params_and_context - Expected result (context value: 42): 37') 37 37 38 t = template.Template('{% load custom %}{% simple_two_params 37 42 %}') 39 self.assertEqual(t.render(c), u'simple_two_params - Expected result: 37, 42') 40 41 t = template.Template('{% load custom %}{% simple_one_default 37 %}') 42 self.assertEqual(t.render(c), u'simple_one_default - Expected result: 37, hi') 43 44 t = template.Template('{% load custom %}{% simple_one_default 37 two="hello" %}') 45 self.assertEqual(t.render(c), u'simple_one_default - Expected result: 37, hello') 46 47 t = template.Template('{% load custom %}{% simple_one_default one=99 two="hello" %}') 48 self.assertEqual(t.render(c), u'simple_one_default - Expected result: 99, hello') 49 50 self.assertRaisesRegexp(template.TemplateSyntaxError, 51 "'simple_one_default' received unexpected keyword argument 'three'", 52 template.Template, '{% load custom %}{% simple_one_default 99 two="hello" three="foo" %}') 53 54 t = template.Template('{% load custom %}{% simple_one_default 37 42 %}') 55 self.assertEqual(t.render(c), u'simple_one_default - Expected result: 37, 42') 56 57 t = template.Template('{% load custom %}{% simple_unlimited_args 37 %}') 58 self.assertEqual(t.render(c), u'simple_unlimited_args - Expected result: 37, hi') 59 60 t = template.Template('{% load custom %}{% simple_unlimited_args 37 42 56 89 %}') 61 self.assertEqual(t.render(c), u'simple_unlimited_args - Expected result: 37, 42, 56, 89') 62 63 t = template.Template('{% load custom %}{% simple_only_unlimited_args %}') 64 self.assertEqual(t.render(c), u'simple_only_unlimited_args - Expected result: ') 65 66 t = template.Template('{% load custom %}{% simple_only_unlimited_args 37 42 56 89 %}') 67 self.assertEqual(t.render(c), u'simple_only_unlimited_args - Expected result: 37, 42, 56, 89') 68 69 self.assertRaisesRegexp(template.TemplateSyntaxError, 70 "'simple_two_params' received too many positional arguments", 71 template.Template, '{% load custom %}{% simple_two_params 37 42 56 %}') 72 73 self.assertRaisesRegexp(template.TemplateSyntaxError, 74 "'simple_one_default' received too many positional arguments", 75 template.Template, '{% load custom %}{% simple_one_default 37 42 56 %}') 76 77 t = template.Template('{% load custom %}{% simple_unlimited_args_kwargs 37 40|add:2 56 eggs="scrambled" four=1|add:3 %}') 78 self.assertEqual(t.render(c), u'simple_unlimited_args_kwargs - Expected result: 37, 42, 56 / eggs=scrambled, four=4') 79 80 self.assertRaisesRegexp(template.TemplateSyntaxError, 81 "'simple_unlimited_args_kwargs' received some positional argument\(s\) after some keyword argument\(s\)", 82 template.Template, '{% load custom %}{% simple_unlimited_args_kwargs 37 40|add:2 eggs="scrambled" 56 four=1|add:3 %}') 83 84 self.assertRaisesRegexp(template.TemplateSyntaxError, 85 "'simple_unlimited_args_kwargs' received multiple values for keyword argument 'eggs'", 86 template.Template, '{% load custom %}{% simple_unlimited_args_kwargs 37 eggs="scrambled" eggs="scrambled" %}') 87 38 88 def test_simple_tag_registration(self): 39 89 # Test that the decorators preserve the decorated function's docstring, name and attributes. 40 90 self.verify_tag(custom.no_params, 'no_params') … … class CustomTagTests(TestCase): 42 92 self.verify_tag(custom.explicit_no_context, 'explicit_no_context') 43 93 self.verify_tag(custom.no_params_with_context, 'no_params_with_context') 44 94 self.verify_tag(custom.params_and_context, 'params_and_context') 95 self.verify_tag(custom.simple_unlimited_args_kwargs, 'simple_unlimited_args_kwargs') 96 self.verify_tag(custom.simple_tag_without_context_parameter, 'simple_tag_without_context_parameter') 45 97 46 98 def test_simple_tag_missing_context(self): 47 # That the 'context' parameter must be present when takes_context is True 48 def a_simple_tag_without_parameters(arg): 49 """Expected __doc__""" 50 return "Expected result" 51 52 register = template.Library() 53 decorator = register.simple_tag(takes_context=True) 54 self.assertRaises(template.TemplateSyntaxError, decorator, a_simple_tag_without_parameters) 99 # The 'context' parameter must be present when takes_context is True 100 self.assertRaisesRegexp(template.TemplateSyntaxError, 101 "'simple_tag_without_context_parameter' is decorated with takes_context=True so it must have a first argument of 'context'", 102 template.Template, '{% load custom %}{% simple_tag_without_context_parameter 123 %}') 55 103 56 104 def test_inclusion_tags(self): 57 105 c = template.Context({'value': 42}) … … class CustomTagTests(TestCase): 71 119 t = template.Template('{% load custom %}{% inclusion_params_and_context 37 %}') 72 120 self.assertEqual(t.render(c), u'inclusion_params_and_context - Expected result (context value: 42): 37\n') 73 121 122 t = template.Template('{% load custom %}{% inclusion_two_params 37 42 %}') 123 self.assertEqual(t.render(c), u'inclusion_two_params - Expected result: 37, 42\n') 124 125 t = template.Template('{% load custom %}{% inclusion_one_default 37 %}') 126 self.assertEqual(t.render(c), u'inclusion_one_default - Expected result: 37, hi\n') 127 128 t = template.Template('{% load custom %}{% inclusion_one_default 37 two="hello" %}') 129 self.assertEqual(t.render(c), u'inclusion_one_default - Expected result: 37, hello\n') 130 131 t = template.Template('{% load custom %}{% inclusion_one_default one=99 two="hello" %}') 132 self.assertEqual(t.render(c), u'inclusion_one_default - Expected result: 99, hello\n') 133 134 self.assertRaisesRegexp(template.TemplateSyntaxError, 135 "'inclusion_one_default' received unexpected keyword argument 'three'", 136 template.Template, '{% load custom %}{% inclusion_one_default 99 two="hello" three="foo" %}') 137 138 t = template.Template('{% load custom %}{% inclusion_one_default 37 42 %}') 139 self.assertEqual(t.render(c), u'inclusion_one_default - Expected result: 37, 42\n') 140 141 t = template.Template('{% load custom %}{% inclusion_unlimited_args 37 %}') 142 self.assertEqual(t.render(c), u'inclusion_unlimited_args - Expected result: 37, hi\n') 143 144 t = template.Template('{% load custom %}{% inclusion_unlimited_args 37 42 56 89 %}') 145 self.assertEqual(t.render(c), u'inclusion_unlimited_args - Expected result: 37, 42, 56, 89\n') 146 147 t = template.Template('{% load custom %}{% inclusion_only_unlimited_args %}') 148 self.assertEqual(t.render(c), u'inclusion_only_unlimited_args - Expected result: \n') 149 150 t = template.Template('{% load custom %}{% inclusion_only_unlimited_args 37 42 56 89 %}') 151 self.assertEqual(t.render(c), u'inclusion_only_unlimited_args - Expected result: 37, 42, 56, 89\n') 152 153 self.assertRaisesRegexp(template.TemplateSyntaxError, 154 "'inclusion_two_params' received too many positional arguments", 155 template.Template, '{% load custom %}{% inclusion_two_params 37 42 56 %}') 156 157 self.assertRaisesRegexp(template.TemplateSyntaxError, 158 "'inclusion_one_default' received too many positional arguments", 159 template.Template, '{% load custom %}{% inclusion_one_default 37 42 56 %}') 160 161 self.assertRaisesRegexp(template.TemplateSyntaxError, 162 "'inclusion_one_default' did not receive value\(s\) for the argument\(s\): 'one'", 163 template.Template, '{% load custom %}{% inclusion_one_default %}') 164 165 self.assertRaisesRegexp(template.TemplateSyntaxError, 166 "'inclusion_unlimited_args' did not receive value\(s\) for the argument\(s\): 'one'", 167 template.Template, '{% load custom %}{% inclusion_unlimited_args %}') 168 169 t = template.Template('{% load custom %}{% inclusion_unlimited_args_kwargs 37 40|add:2 56 eggs="scrambled" four=1|add:3 %}') 170 self.assertEqual(t.render(c), u'inclusion_unlimited_args_kwargs - Expected result: 37, 42, 56 / eggs=scrambled, four=4\n') 171 172 self.assertRaisesRegexp(template.TemplateSyntaxError, 173 "'inclusion_unlimited_args_kwargs' received some positional argument\(s\) after some keyword argument\(s\)", 174 template.Template, '{% load custom %}{% inclusion_unlimited_args_kwargs 37 40|add:2 eggs="scrambled" 56 four=1|add:3 %}') 175 176 self.assertRaisesRegexp(template.TemplateSyntaxError, 177 "'inclusion_unlimited_args_kwargs' received multiple values for keyword argument 'eggs'", 178 template.Template, '{% load custom %}{% inclusion_unlimited_args_kwargs 37 eggs="scrambled" eggs="scrambled" %}') 179 180 def test_include_tag_missing_context(self): 181 # The 'context' parameter must be present when takes_context is True 182 self.assertRaisesRegexp(template.TemplateSyntaxError, 183 "'inclusion_tag_without_context_parameter' is decorated with takes_context=True so it must have a first argument of 'context'", 184 template.Template, '{% load custom %}{% inclusion_tag_without_context_parameter 123 %}') 185 74 186 def test_inclusion_tags_from_template(self): 75 187 c = template.Context({'value': 42}) 76 188 … … class CustomTagTests(TestCase): 89 201 t = template.Template('{% load custom %}{% inclusion_params_and_context_from_template 37 %}') 90 202 self.assertEqual(t.render(c), u'inclusion_params_and_context_from_template - Expected result (context value: 42): 37\n') 91 203 204 t = template.Template('{% load custom %}{% inclusion_two_params_from_template 37 42 %}') 205 self.assertEqual(t.render(c), u'inclusion_two_params_from_template - Expected result: 37, 42\n') 206 207 t = template.Template('{% load custom %}{% inclusion_one_default_from_template 37 %}') 208 self.assertEqual(t.render(c), u'inclusion_one_default_from_template - Expected result: 37, hi\n') 209 210 t = template.Template('{% load custom %}{% inclusion_one_default_from_template 37 42 %}') 211 self.assertEqual(t.render(c), u'inclusion_one_default_from_template - Expected result: 37, 42\n') 212 213 t = template.Template('{% load custom %}{% inclusion_unlimited_args_from_template 37 %}') 214 self.assertEqual(t.render(c), u'inclusion_unlimited_args_from_template - Expected result: 37, hi\n') 215 216 t = template.Template('{% load custom %}{% inclusion_unlimited_args_from_template 37 42 56 89 %}') 217 self.assertEqual(t.render(c), u'inclusion_unlimited_args_from_template - Expected result: 37, 42, 56, 89\n') 218 219 t = template.Template('{% load custom %}{% inclusion_only_unlimited_args_from_template %}') 220 self.assertEqual(t.render(c), u'inclusion_only_unlimited_args_from_template - Expected result: \n') 221 222 t = template.Template('{% load custom %}{% inclusion_only_unlimited_args_from_template 37 42 56 89 %}') 223 self.assertEqual(t.render(c), u'inclusion_only_unlimited_args_from_template - Expected result: 37, 42, 56, 89\n') 224 92 225 def test_inclusion_tag_registration(self): 93 226 # Test that the decorators preserve the decorated function's docstring, name and attributes. 94 227 self.verify_tag(custom.inclusion_no_params, 'inclusion_no_params') … … class CustomTagTests(TestCase): 96 229 self.verify_tag(custom.inclusion_explicit_no_context, 'inclusion_explicit_no_context') 97 230 self.verify_tag(custom.inclusion_no_params_with_context, 'inclusion_no_params_with_context') 98 231 self.verify_tag(custom.inclusion_params_and_context, 'inclusion_params_and_context') 232 self.verify_tag(custom.inclusion_two_params, 'inclusion_two_params') 233 self.verify_tag(custom.inclusion_one_default, 'inclusion_one_default') 234 self.verify_tag(custom.inclusion_unlimited_args, 'inclusion_unlimited_args') 235 self.verify_tag(custom.inclusion_only_unlimited_args, 'inclusion_only_unlimited_args') 236 self.verify_tag(custom.inclusion_tag_without_context_parameter, 'inclusion_tag_without_context_parameter') 237 self.verify_tag(custom.inclusion_tag_use_l10n, 'inclusion_tag_use_l10n') 238 self.verify_tag(custom.inclusion_tag_current_app, 'inclusion_tag_current_app') 239 self.verify_tag(custom.inclusion_unlimited_args_kwargs, 'inclusion_unlimited_args_kwargs') 99 240 100 241 def test_15070_current_app(self): 101 242 """ … … class CustomTagTests(TestCase): 139 280 t = template.Template('{% load custom %}{% assignment_params_and_context 37 as var %}The result is: {{ var }}') 140 281 self.assertEqual(t.render(c), u'The result is: assignment_params_and_context - Expected result (context value: 42): 37') 141 282 283 t = template.Template('{% load custom %}{% assignment_two_params 37 42 as var %}The result is: {{ var }}') 284 self.assertEqual(t.render(c), u'The result is: assignment_two_params - Expected result: 37, 42') 285 286 t = template.Template('{% load custom %}{% assignment_one_default 37 as var %}The result is: {{ var }}') 287 self.assertEqual(t.render(c), u'The result is: assignment_one_default - Expected result: 37, hi') 288 289 t = template.Template('{% load custom %}{% assignment_one_default 37 two="hello" as var %}The result is: {{ var }}') 290 self.assertEqual(t.render(c), u'The result is: assignment_one_default - Expected result: 37, hello') 291 292 t = template.Template('{% load custom %}{% assignment_one_default one=99 two="hello" as var %}The result is: {{ var }}') 293 self.assertEqual(t.render(c), u'The result is: assignment_one_default - Expected result: 99, hello') 294 295 self.assertRaisesRegexp(template.TemplateSyntaxError, 296 "'assignment_one_default' received unexpected keyword argument 'three'", 297 template.Template, '{% load custom %}{% assignment_one_default 99 two="hello" three="foo" as var %}') 298 299 t = template.Template('{% load custom %}{% assignment_one_default 37 42 as var %}The result is: {{ var }}') 300 self.assertEqual(t.render(c), u'The result is: assignment_one_default - Expected result: 37, 42') 301 302 t = template.Template('{% load custom %}{% assignment_unlimited_args 37 as var %}The result is: {{ var }}') 303 self.assertEqual(t.render(c), u'The result is: assignment_unlimited_args - Expected result: 37, hi') 304 305 t = template.Template('{% load custom %}{% assignment_unlimited_args 37 42 56 89 as var %}The result is: {{ var }}') 306 self.assertEqual(t.render(c), u'The result is: assignment_unlimited_args - Expected result: 37, 42, 56, 89') 307 308 t = template.Template('{% load custom %}{% assignment_only_unlimited_args as var %}The result is: {{ var }}') 309 self.assertEqual(t.render(c), u'The result is: assignment_only_unlimited_args - Expected result: ') 310 311 t = template.Template('{% load custom %}{% assignment_only_unlimited_args 37 42 56 89 as var %}The result is: {{ var }}') 312 self.assertEqual(t.render(c), u'The result is: assignment_only_unlimited_args - Expected result: 37, 42, 56, 89') 313 142 314 self.assertRaisesRegexp(template.TemplateSyntaxError, 143 315 "'assignment_one_param' tag takes at least 2 arguments and the second last argument must be 'as'", 144 316 template.Template, '{% load custom %}{% assignment_one_param 37 %}The result is: {{ var }}') … … class CustomTagTests(TestCase): 151 323 "'assignment_one_param' tag takes at least 2 arguments and the second last argument must be 'as'", 152 324 template.Template, '{% load custom %}{% assignment_one_param 37 ass var %}The result is: {{ var }}') 153 325 326 self.assertRaisesRegexp(template.TemplateSyntaxError, 327 "'assignment_two_params' received too many positional arguments", 328 template.Template, '{% load custom %}{% assignment_two_params 37 42 56 as var %}The result is: {{ var }}') 329 330 self.assertRaisesRegexp(template.TemplateSyntaxError, 331 "'assignment_one_default' received too many positional arguments", 332 template.Template, '{% load custom %}{% assignment_one_default 37 42 56 as var %}The result is: {{ var }}') 333 334 self.assertRaisesRegexp(template.TemplateSyntaxError, 335 "'assignment_one_default' did not receive value\(s\) for the argument\(s\): 'one'", 336 template.Template, '{% load custom %}{% assignment_one_default as var %}The result is: {{ var }}') 337 338 self.assertRaisesRegexp(template.TemplateSyntaxError, 339 "'assignment_unlimited_args' did not receive value\(s\) for the argument\(s\): 'one'", 340 template.Template, '{% load custom %}{% assignment_unlimited_args as var %}The result is: {{ var }}') 341 342 t = template.Template('{% load custom %}{% assignment_unlimited_args_kwargs 37 40|add:2 56 eggs="scrambled" four=1|add:3 as var %}The result is: {{ var }}') 343 self.assertEqual(t.render(c), u'The result is: assignment_unlimited_args_kwargs - Expected result: 37, 42, 56 / eggs=scrambled, four=4') 344 345 self.assertRaisesRegexp(template.TemplateSyntaxError, 346 "'assignment_unlimited_args_kwargs' received some positional argument\(s\) after some keyword argument\(s\)", 347 template.Template, '{% load custom %}{% assignment_unlimited_args_kwargs 37 40|add:2 eggs="scrambled" 56 four=1|add:3 as var %}The result is: {{ var }}') 348 349 self.assertRaisesRegexp(template.TemplateSyntaxError, 350 "'assignment_unlimited_args_kwargs' received multiple values for keyword argument 'eggs'", 351 template.Template, '{% load custom %}{% assignment_unlimited_args_kwargs 37 eggs="scrambled" eggs="scrambled" as var %}The result is: {{ var }}') 352 154 353 def test_assignment_tag_registration(self): 155 354 # Test that the decorators preserve the decorated function's docstring, name and attributes. 156 355 self.verify_tag(custom.assignment_no_params, 'assignment_no_params') … … class CustomTagTests(TestCase): 158 357 self.verify_tag(custom.assignment_explicit_no_context, 'assignment_explicit_no_context') 159 358 self.verify_tag(custom.assignment_no_params_with_context, 'assignment_no_params_with_context') 160 359 self.verify_tag(custom.assignment_params_and_context, 'assignment_params_and_context') 360 self.verify_tag(custom.assignment_one_default, 'assignment_one_default') 361 self.verify_tag(custom.assignment_two_params, 'assignment_two_params') 362 self.verify_tag(custom.assignment_unlimited_args, 'assignment_unlimited_args') 363 self.verify_tag(custom.assignment_only_unlimited_args, 'assignment_only_unlimited_args') 364 self.verify_tag(custom.assignment_unlimited_args, 'assignment_unlimited_args') 365 self.verify_tag(custom.assignment_unlimited_args_kwargs, 'assignment_unlimited_args_kwargs') 366 self.verify_tag(custom.assignment_tag_without_context_parameter, 'assignment_tag_without_context_parameter') 161 367 162 368 def test_assignment_tag_missing_context(self): 163 # That the 'context' parameter must be present when takes_context is True 164 def an_assignment_tag_without_parameters(arg): 165 """Expected __doc__""" 166 return "Expected result" 167 168 register = template.Library() 169 decorator = register.assignment_tag(takes_context=True) 170 369 # The 'context' parameter must be present when takes_context is True 171 370 self.assertRaisesRegexp(template.TemplateSyntaxError, 172 " Any tag function decorated with takes_context=Truemust have a first argument of 'context'",173 decorator, an_assignment_tag_without_parameters)371 "'assignment_tag_without_context_parameter' is decorated with takes_context=True so it must have a first argument of 'context'", 372 template.Template, '{% load custom %}{% assignment_tag_without_context_parameter 123 as var %}') -
tests/regressiontests/templates/templatetags/custom.py
diff --git a/tests/regressiontests/templates/templatetags/custom.py b/tests/regressiontests/templates/templatetags/custom.py index dfa4171..8620661 100644
a b 1 import operator 2 1 3 from django import template 2 4 from django.template.defaultfilters import stringfilter 3 5 from django.template.loader import get_template … … def params_and_context(context, arg): 40 42 return "params_and_context - Expected result (context value: %s): %s" % (context['value'], arg) 41 43 params_and_context.anything = "Expected params_and_context __dict__" 42 44 45 @register.simple_tag 46 def simple_two_params(one, two): 47 """Expected simple_two_params __doc__""" 48 return "simple_two_params - Expected result: %s, %s" % (one, two) 49 simple_two_params.anything = "Expected simple_two_params __dict__" 50 51 @register.simple_tag 52 def simple_one_default(one, two='hi'): 53 """Expected simple_one_default __doc__""" 54 return "simple_one_default - Expected result: %s, %s" % (one, two) 55 simple_one_default.anything = "Expected simple_one_default __dict__" 56 57 @register.simple_tag 58 def simple_unlimited_args(one, two='hi', *args): 59 """Expected simple_unlimited_args __doc__""" 60 return "simple_unlimited_args - Expected result: %s" % (', '.join([unicode(arg) for arg in [one, two] + list(args)])) 61 simple_unlimited_args.anything = "Expected simple_unlimited_args __dict__" 62 63 @register.simple_tag 64 def simple_only_unlimited_args(*args): 65 """Expected simple_only_unlimited_args __doc__""" 66 return "simple_only_unlimited_args - Expected result: %s" % ', '.join([unicode(arg) for arg in args]) 67 simple_only_unlimited_args.anything = "Expected simple_only_unlimited_args __dict__" 68 69 @register.simple_tag 70 def simple_unlimited_args_kwargs(one, two='hi', *args, **kwargs): 71 """Expected simple_unlimited_args_kwargs __doc__""" 72 # Sort the dictionary by key to guarantee the order for testing. 73 sorted_kwarg = sorted(kwargs.iteritems(), key=operator.itemgetter(0)) 74 return "simple_unlimited_args_kwargs - Expected result: %s / %s" % ( 75 ', '.join([unicode(arg) for arg in [one, two] + list(args)]), 76 ', '.join(['%s=%s' % (k, v) for (k, v) in sorted_kwarg]) 77 ) 78 simple_unlimited_args_kwargs.anything = "Expected simple_unlimited_args_kwargs __dict__" 79 80 @register.simple_tag(takes_context=True) 81 def simple_tag_without_context_parameter(arg): 82 """Expected simple_tag_without_context_parameter __doc__""" 83 return "Expected result" 84 simple_tag_without_context_parameter.anything = "Expected simple_tag_without_context_parameter __dict__" 85 86 @register.simple_tag(takes_context=True) 87 def current_app(context): 88 return "%s" % context.current_app 89 90 @register.simple_tag(takes_context=True) 91 def use_l10n(context): 92 return "%s" % context.use_l10n 93 94 @register.simple_tag(name='minustwo') 95 def minustwo_overridden_name(value): 96 return value - 2 97 98 register.simple_tag(lambda x: x - 1, name='minusone') 99 43 100 @register.inclusion_tag('inclusion.html') 44 101 def inclusion_no_params(): 45 102 """Expected inclusion_no_params __doc__""" … … def inclusion_params_and_context_from_template(context, arg): 100 157 return {"result" : "inclusion_params_and_context_from_template - Expected result (context value: %s): %s" % (context['value'], arg)} 101 158 inclusion_params_and_context_from_template.anything = "Expected inclusion_params_and_context_from_template __dict__" 102 159 103 @register.simple_tag(takes_context=True) 104 def current_app(context): 105 return "%s" % context.current_app 160 @register.inclusion_tag('inclusion.html') 161 def inclusion_two_params(one, two): 162 """Expected inclusion_two_params __doc__""" 163 return {"result": "inclusion_two_params - Expected result: %s, %s" % (one, two)} 164 inclusion_two_params.anything = "Expected inclusion_two_params __dict__" 165 166 @register.inclusion_tag(get_template('inclusion.html')) 167 def inclusion_two_params_from_template(one, two): 168 """Expected inclusion_two_params_from_template __doc__""" 169 return {"result": "inclusion_two_params_from_template - Expected result: %s, %s" % (one, two)} 170 inclusion_two_params_from_template.anything = "Expected inclusion_two_params_from_template __dict__" 171 172 @register.inclusion_tag('inclusion.html') 173 def inclusion_one_default(one, two='hi'): 174 """Expected inclusion_one_default __doc__""" 175 return {"result": "inclusion_one_default - Expected result: %s, %s" % (one, two)} 176 inclusion_one_default.anything = "Expected inclusion_one_default __dict__" 177 178 @register.inclusion_tag(get_template('inclusion.html')) 179 def inclusion_one_default_from_template(one, two='hi'): 180 """Expected inclusion_one_default_from_template __doc__""" 181 return {"result": "inclusion_one_default_from_template - Expected result: %s, %s" % (one, two)} 182 inclusion_one_default_from_template.anything = "Expected inclusion_one_default_from_template __dict__" 183 184 @register.inclusion_tag('inclusion.html') 185 def inclusion_unlimited_args(one, two='hi', *args): 186 """Expected inclusion_unlimited_args __doc__""" 187 return {"result": "inclusion_unlimited_args - Expected result: %s" % (', '.join([unicode(arg) for arg in [one, two] + list(args)]))} 188 inclusion_unlimited_args.anything = "Expected inclusion_unlimited_args __dict__" 189 190 @register.inclusion_tag(get_template('inclusion.html')) 191 def inclusion_unlimited_args_from_template(one, two='hi', *args): 192 """Expected inclusion_unlimited_args_from_template __doc__""" 193 return {"result": "inclusion_unlimited_args_from_template - Expected result: %s" % (', '.join([unicode(arg) for arg in [one, two] + list(args)]))} 194 inclusion_unlimited_args_from_template.anything = "Expected inclusion_unlimited_args_from_template __dict__" 195 196 @register.inclusion_tag('inclusion.html') 197 def inclusion_only_unlimited_args(*args): 198 """Expected inclusion_only_unlimited_args __doc__""" 199 return {"result": "inclusion_only_unlimited_args - Expected result: %s" % (', '.join([unicode(arg) for arg in args]))} 200 inclusion_only_unlimited_args.anything = "Expected inclusion_only_unlimited_args __dict__" 201 202 @register.inclusion_tag(get_template('inclusion.html')) 203 def inclusion_only_unlimited_args_from_template(*args): 204 """Expected inclusion_only_unlimited_args_from_template __doc__""" 205 return {"result": "inclusion_only_unlimited_args_from_template - Expected result: %s" % (', '.join([unicode(arg) for arg in args]))} 206 inclusion_only_unlimited_args_from_template.anything = "Expected inclusion_only_unlimited_args_from_template __dict__" 106 207 107 208 @register.inclusion_tag('test_incl_tag_current_app.html', takes_context=True) 108 209 def inclusion_tag_current_app(context): 210 """Expected inclusion_tag_current_app __doc__""" 109 211 return {} 110 111 @register.simple_tag(takes_context=True) 112 def use_l10n(context): 113 return "%s" % context.use_l10n 212 inclusion_tag_current_app.anything = "Expected inclusion_tag_current_app __dict__" 114 213 115 214 @register.inclusion_tag('test_incl_tag_use_l10n.html', takes_context=True) 116 215 def inclusion_tag_use_l10n(context): 216 """Expected inclusion_tag_use_l10n __doc__""" 217 return {} 218 inclusion_tag_use_l10n.anything = "Expected inclusion_tag_use_l10n __dict__" 219 220 @register.inclusion_tag('inclusion.html') 221 def inclusion_unlimited_args_kwargs(one, two='hi', *args, **kwargs): 222 """Expected inclusion_unlimited_args_kwargs __doc__""" 223 # Sort the dictionary by key to guarantee the order for testing. 224 sorted_kwarg = sorted(kwargs.iteritems(), key=operator.itemgetter(0)) 225 return {"result": "inclusion_unlimited_args_kwargs - Expected result: %s / %s" % ( 226 ', '.join([unicode(arg) for arg in [one, two] + list(args)]), 227 ', '.join(['%s=%s' % (k, v) for (k, v) in sorted_kwarg]) 228 )} 229 inclusion_unlimited_args_kwargs.anything = "Expected inclusion_unlimited_args_kwargs __dict__" 230 231 @register.inclusion_tag('inclusion.html', takes_context=True) 232 def inclusion_tag_without_context_parameter(arg): 233 """Expected inclusion_tag_without_context_parameter __doc__""" 117 234 return {} 235 inclusion_tag_without_context_parameter.anything = "Expected inclusion_tag_without_context_parameter __dict__" 118 236 119 237 @register.assignment_tag 120 238 def assignment_no_params(): … … def assignment_params_and_context(context, arg): 146 264 return "assignment_params_and_context - Expected result (context value: %s): %s" % (context['value'], arg) 147 265 assignment_params_and_context.anything = "Expected assignment_params_and_context __dict__" 148 266 149 register.simple_tag(lambda x: x - 1, name='minusone') 267 @register.assignment_tag 268 def assignment_two_params(one, two): 269 """Expected assignment_two_params __doc__""" 270 return "assignment_two_params - Expected result: %s, %s" % (one, two) 271 assignment_two_params.anything = "Expected assignment_two_params __dict__" 150 272 151 @register.simple_tag(name='minustwo') 152 def minustwo_overridden_name(value): 153 return value - 2 273 @register.assignment_tag 274 def assignment_one_default(one, two='hi'): 275 """Expected assignment_one_default __doc__""" 276 return "assignment_one_default - Expected result: %s, %s" % (one, two) 277 assignment_one_default.anything = "Expected assignment_one_default __dict__" 278 279 @register.assignment_tag 280 def assignment_unlimited_args(one, two='hi', *args): 281 """Expected assignment_unlimited_args __doc__""" 282 return "assignment_unlimited_args - Expected result: %s" % (', '.join([unicode(arg) for arg in [one, two] + list(args)])) 283 assignment_unlimited_args.anything = "Expected assignment_unlimited_args __dict__" 284 285 @register.assignment_tag 286 def assignment_only_unlimited_args(*args): 287 """Expected assignment_only_unlimited_args __doc__""" 288 return "assignment_only_unlimited_args - Expected result: %s" % ', '.join([unicode(arg) for arg in args]) 289 assignment_only_unlimited_args.anything = "Expected assignment_only_unlimited_args __dict__" 290 291 @register.assignment_tag 292 def assignment_unlimited_args_kwargs(one, two='hi', *args, **kwargs): 293 """Expected assignment_unlimited_args_kwargs __doc__""" 294 # Sort the dictionary by key to guarantee the order for testing. 295 sorted_kwarg = sorted(kwargs.iteritems(), key=operator.itemgetter(0)) 296 return "assignment_unlimited_args_kwargs - Expected result: %s / %s" % ( 297 ', '.join([unicode(arg) for arg in [one, two] + list(args)]), 298 ', '.join(['%s=%s' % (k, v) for (k, v) in sorted_kwarg]) 299 ) 300 assignment_unlimited_args_kwargs.anything = "Expected assignment_unlimited_args_kwargs __dict__" 301 302 @register.assignment_tag(takes_context=True) 303 def assignment_tag_without_context_parameter(arg): 304 """Expected assignment_tag_without_context_parameter __doc__""" 305 return "Expected result" 306 assignment_tag_without_context_parameter.anything = "Expected assignment_tag_without_context_parameter __dict__" 307 No newline at end of file