=== django/core/template/defaultfilters.py
==================================================================
--- django/core/template/defaultfilters.py	(/django/trunk)	(revision 3709)
+++ django/core/template/defaultfilters.py	(/local/filters-multi)	(revision 3709)
@@ -178,9 +178,9 @@
     "Converts newlines into <br />s"
     return value.replace('\n', '<br />')
 
-def removetags(value, tags):
-    "Removes a space separated list of [X]HTML tags from the output"
-    tags = [re.escape(tag) for tag in tags.split()]
+def removetags(value, *tags):
+    "Removes a list of [X]HTML tags from the output"
+    tags = [re.escape(tag) for tag in tags]
     tags_re = '(%s)' % '|'.join(tags)
     starttag_re = re.compile(r'<%s(/?>|(\s+[^>]*>))' % tags_re)
     endtag_re = re.compile('</%s>' % tags_re)
@@ -196,6 +196,13 @@
     return strip_tags(value)
 
 ###################
+# COLLECTIONS     #
+###################
+
+def lookup(value, arg):
+    return value[arg]
+
+###################
 # LISTS           #
 ###################
 
@@ -383,19 +390,20 @@
 # MISC            #
 ###################
 
-def filesizeformat(bytes):
+def filesizeformat(bytes, format=None):
     """
     Format the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB, 102
     bytes, etc).
     """
+    base = format=='iso' and 1000 or 1024
     bytes = float(bytes)
-    if bytes < 1024:
+    if bytes < base:
         return "%d byte%s" % (bytes, bytes != 1 and 's' or '')
-    if bytes < 1024 * 1024:
+    if bytes < base * base:
         return "%.1f KB" % (bytes / 1024)
-    if bytes < 1024 * 1024 * 1024:
-        return "%.1f MB" % (bytes / (1024 * 1024))
-    return "%.1f GB" % (bytes / (1024 * 1024 * 1024))
+    if bytes < base * base * base:
+        return "%.1f MB" % (bytes / (base * base))
+    return "%.1f GB" % (bytes / (base * base * base))
 
 def pluralize(value):
     "Returns 's' if the value is not 1, for '1 vote' vs. '2 votes'"
@@ -423,6 +431,7 @@
     return pformat(value)
 
 # Syntax: register.filter(name of filter, callback)
+
 register.filter(add)
 register.filter(addslashes)
 register.filter(capfirst)
@@ -447,6 +456,7 @@
 register.filter(linebreaksbr)
 register.filter(linenumbers)
 register.filter(ljust)
+register.filter(lookup)
 register.filter(lower)
 register.filter(make_list)
 register.filter(phone2numeric)
=== django/core/template/__init__.py
==================================================================
--- django/core/template/__init__.py	(/django/trunk)	(revision 3709)
+++ django/core/template/__init__.py	(/local/filters-multi)	(revision 3709)
@@ -67,7 +67,8 @@
 
 # template syntax constants
 FILTER_SEPARATOR = '|'
-FILTER_ARGUMENT_SEPARATOR = ':'
+FILTER_ARGUMENT_INTRODUCTION = ':'
+FILTER_ARGUMENT_SEPARATOR = ','
 VARIABLE_ATTRIBUTE_SEPARATOR = '.'
 BLOCK_TAG_START = '{%'
 BLOCK_TAG_END = '%}'
@@ -494,38 +495,45 @@
             self.pointer = i
             return s
 
+def prepare_filter_res():
+        re_args = {
+        'str': r"""[^"\\]*(?:\\.[^"\\]*)*""",
+        'var_chars': "A-Za-z0-9\_\." ,
+        'filter_sep': re.escape(FILTER_SEPARATOR),
+        'arg_intro': re.escape(FILTER_ARGUMENT_INTRODUCTION),
+        'arg_sep' : re.escape(FILTER_ARGUMENT_SEPARATOR), 
+        'i18n_open' : re.escape("_("),
+        'i18n_close' : re.escape(")"),
+        }
+       
+        opening_rs = r"""
+        ^%(i18n_open)s"(?P<i18n_constant>%(str)s)"%(i18n_close)s|
+        ^"(?P<constant>%(str)s)"|
+        ^(?P<var>[%(var_chars)s]+)
+        """
+    
+        filter_name_rs = r"""
+        ^%(filter_sep)s(?P<filter_name>\w+)
+        """
+ 
+        arg_common = r"""
+         (?:
+          %(i18n_open)s"(?P<i18n_arg>%(str)s)"%(i18n_close)s|
+          "(?P<constant_arg>%(str)s)"|
+          (?P<var_arg>[%(var_chars)s]+)
+         )
+        """
+        
+        first_arg_rs = r"""^%(arg_intro)s""" + arg_common
+        further_arg_rs = r"""^%(arg_sep)s""" + arg_common
+    
+        raw_strings = [opening_rs, filter_name_rs, first_arg_rs, further_arg_rs]
+        return [re.compile((s % re_args).replace("\n", "").replace(" ", "")) for s in raw_strings] 
 
-
-
-filter_raw_string = r"""
-^%(i18n_open)s"(?P<i18n_constant>%(str)s)"%(i18n_close)s|
-^"(?P<constant>%(str)s)"|
-^(?P<var>[%(var_chars)s]+)|
- (?:%(filter_sep)s
-     (?P<filter_name>\w+)
-         (?:%(arg_sep)s
-             (?:
-              %(i18n_open)s"(?P<i18n_arg>%(str)s)"%(i18n_close)s|
-              "(?P<constant_arg>%(str)s)"|
-              (?P<var_arg>[%(var_chars)s]+)
-             )
-         )?
- )""" % {
-    'str': r"""[^"\\]*(?:\\.[^"\\]*)*""",
-    'var_chars': "A-Za-z0-9\_\." ,
-    'filter_sep': re.escape(FILTER_SEPARATOR),
-    'arg_sep': re.escape(FILTER_ARGUMENT_SEPARATOR),
-    'i18n_open' : re.escape("_("),
-    'i18n_close' : re.escape(")"),
-  }
-
-filter_raw_string = filter_raw_string.replace("\n", "").replace(" ", "")
-filter_re = re.compile(filter_raw_string)
-
 class FilterExpression(object):
     """
     Parses a variable token and its optional filters (all as a single string),
-    and return a list of tuples of the filter name and arguments.
+    and return a list of tuples of the filter function and arguments.
     Sample:
         >>> token = 'variable|default:"Default value"|date:"Y-m-d"'
         >>> p = FilterParser(token)
@@ -537,46 +545,72 @@
     This class should never be instantiated outside of the
     get_filters_from_token helper function.
     """
+    
+    opening_re, filter_name_re, first_arg_re, further_arg_re = prepare_filter_res()
+    
+    
     def __init__(self, token, parser):
         self.token = token
-        matches = filter_re.finditer(token)
         var = None
         filters = []
-        upto = 0
-        for match in matches:
-            start = match.start()
-            if upto != start:
-                raise TemplateSyntaxError, "Could not parse some characters: %s|%s|%s"  % \
-                                           (token[:upto], token[upto:start], token[start:])
+        
+        remainder = token
+        # match an opening
+        match = FilterExpression.opening_re.match(remainder)
+        if match:
+            var, constant, i18n_constant = match.group("var", "constant", "i18n_constant")
+            if var and (var.find(VARIABLE_ATTRIBUTE_SEPARATOR + '_') > -1 or var[0] == '_'):
+                raise TemplateSyntaxError, "Variables and attributes may not begin with underscores: '%s'" % var
+            
+            if i18n_constant:
+                var = '"%s"' %  _(i18n_constant.replace('\\', '') )
+            elif constant:
+                var = '"%s"' % constant.replace('\\','')
+            
+            remainder = remainder[match.end():]
             if var == None:
-                var, constant, i18n_constant = match.group("var", "constant", "i18n_constant")
-                if i18n_constant:
-                    var = '"%s"' %  _(i18n_constant)
-                elif constant:
-                    var = '"%s"' % constant
-                upto = match.end()
-                if var == None:
-                    raise TemplateSyntaxError, "Could not find variable at start of %s" % token
-                elif var.find(VARIABLE_ATTRIBUTE_SEPARATOR + '_') > -1 or var[0] == '_':
-                    raise TemplateSyntaxError, "Variables and attributes may not begin with underscores: '%s'" % var
-            else:
-                filter_name = match.group("filter_name")
-                args = []
-                constant_arg, i18n_arg, var_arg = match.group("constant_arg", "i18n_arg", "var_arg")
-                if i18n_arg:
-                    args.append((False, _(i18n_arg.replace('\\', ''))))
-                elif constant_arg:
-                    args.append((False, constant_arg.replace('\\', '')))
-                elif var_arg:
-                    args.append((True, var_arg))
-                filter_func = parser.find_filter(filter_name)
-                self.args_check(filter_name,filter_func, args)
-                filters.append( (filter_func,args))
-                upto = match.end()
-        if upto != len(token):
-            raise TemplateSyntaxError, "Could not parse the remainder: %s" % token[upto:]
+                raise TemplateSyntaxError, "Could not find variable at start of %s" % token
+        else:
+            raise TemplateSyntaxError, "Could not find variable at start of |%s|" % token
+        
+        #match a number of filters
+        # first, maybe match a filter name 
+        match = FilterExpression.filter_name_re.match(remainder)
+        while match:
+            filter_name = match.group("filter_name")
+            filter_func = parser.find_filter(filter_name)
+            remainder = remainder[match.end():]
+            args = []
+            # maybe match a first argument
+            match = FilterExpression.first_arg_re.match(remainder)
+            if match:
+            
+                args.append(self.getarg(match))
+                remainder = remainder[match.end():]
+                # match a number of further arguments
+                match = FilterExpression.further_arg_re.match(remainder)
+                while match:
+                    args.append(self.getarg(match))
+                    remainder = remainder[match.end():]
+                    match = FilterExpression.further_arg_re.match(remainder)
+            self.args_check(filter_name,filter_func, args)
+            filters.append((filter_func, args))    
+            # maybe match another filter name
+            match = FilterExpression.filter_name_re.match(remainder)
+        
+        if len(remainder) != 0:
+            raise TemplateSyntaxError, "Could not parse the remainder of the filter expression: %s" % remainder
         self.var , self.filters = var, filters
 
+    def getarg(self, match):
+        constant_arg, i18n_arg, var_arg = match.group("constant_arg", "i18n_arg", "var_arg")
+        if i18n_arg:
+            return (False, _(i18n_arg.replace('\\', '')))
+        elif constant_arg:
+            return (False, constant_arg.replace('\\', ''))
+        elif var_arg:
+            return (True, var_arg)
+
     def resolve(self, context):
         try:
             obj = resolve_variable(self.var, context)
@@ -602,6 +636,7 @@
             nondefs = args[:-len(defaults)]
         else:
             nondefs = args
+        
         # Args without defaults must be provided.
         try:
             for arg in nondefs:
@@ -610,14 +645,16 @@
             # Not enough
             raise TemplateSyntaxError, "%s requires %d arguments, %d provided" % (name, len(nondefs), plen)
 
-        # Defaults can be overridden.
-        defaults = defaults and list(defaults) or []
-        try:
-            for parg in provided:
-                defaults.pop(0)
-        except IndexError:
-            # Too many.
-            raise TemplateSyntaxError, "%s requires %d arguments, %d provided" % (name, len(nondefs), plen)
+        #if varargs is true, it will eat up the rest of the args. 
+        if not varargs:
+            # Defaults can be overridden.
+            defaults = defaults and list(defaults) or []
+            try:
+                for parg in provided:
+                    defaults.pop(0)
+            except IndexError:
+                # Too many.
+                raise TemplateSyntaxError, "%s requires %d arguments, %d provided" % (name, len(nondefs), plen)
 
         return True
     args_check = staticmethod(args_check)
@@ -629,7 +666,7 @@
     """
     Returns the resolved variable, which may contain attribute syntax, within
     the given context. The variable may be a hard-coded string (if it begins
-    and ends with single or double quote marks).
+    and ends with single or double quote marks), or an integer or float literal.
 
     >>> c = {'article': {'section':'News'}}
     >>> resolve_variable('article.section', c)
@@ -645,7 +682,13 @@
 
     (The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.')
     """
-    if path[0] in ('"', "'") and path[0] == path[-1]:
+    if path[0] in '0123456789':
+        number_type = '.' in path and float or int
+        try:
+           current = number_type(path)
+        except ValueError:
+           current = ''
+    elif path[0] in ('"', "'") and path[0] == path[-1]:
         current = path[1:-1]
     else:
         current = context
=== tests/othertests/templates.py
==================================================================
--- tests/othertests/templates.py	(/django/trunk)	(revision 3709)
+++ tests/othertests/templates.py	(/local/filters-multi)	(revision 3709)
@@ -101,7 +101,7 @@
     'basic-syntax28': ("{% %}", {}, template.TemplateSyntaxError),
 
     # Chained filters, with an argument to the first one
-    'basic-syntax29': ('{{ var|removetags:"b i"|upper|lower }}', {"var": "<b><i>Yes</i></b>"}, "yes"),
+    'basic-syntax29': ('{{ var|removetags:"b","i"|upper|lower }}', {"var": "<b><i>Yes</i></b>"}, "yes"),
 
     # Escaped string as argument
     'basic-syntax30': (r'{{ var|default_if_none:" endquote\" hah" }}', {"var": None}, ' endquote" hah'),
@@ -112,6 +112,14 @@
     # Default argument testing
     'basic-syntax32' : (r'{{ var|yesno:"yup,nup,mup" }} {{ var|yesno }}', {"var": True}, 'yup yes'),
 
+    # Lookup
+    'basic-syntax33' : (r'{{ nicks|lookup:"robert"}} {{nicks|lookup:lead}}', 
+                         {'nicks': {'robert': 'rjwittams', 'adrian': 'adrian_h'}, 
+                                    'lead': 'adrian', }, 'rjwittams adrian_h'  ),
+                                    
+    #Numbers 
+    'basic-syntax34' : (r'{{ 1|add:2 }} {{1.0|add:2.0}}', {}, '3 3'),
+
     ### IF TAG ################################################################
     'if-tag01': ("{% if foo %}yes{% else %}no{% endif %}", {"foo": True}, "yes"),
     'if-tag02': ("{% if foo %}yes{% else %}no{% endif %}", {"foo": False}, "no"),
@@ -347,4 +355,6 @@
         raise Exception, msg
 
 if __name__ == "__main__":
+    template.libraries['django.templatetags.testtags'] = \
+       template.get_library('tests.testapp.templatetags.testtags')
     run_tests(1, True)
=== tests/__init__.py
==================================================================

Property changes on: 
___________________________________________________________________
Name: svk:merge
 -bcc190cf-cafb-0310-a4f2-bffc1f526a37:/django/trunk:1054
 +bcc190cf-cafb-0310-a4f2-bffc1f526a37:/django/trunk:1483

