Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

[1.0.X] Fixed #5748 -- Made floatformat filter round properly on all …

…platforms and handle NaN input correctly on Windows. Also added tests for these cases. Thanks for the report and initial patch to SmileyChris and PJCrosier.

[9369] from trunk.


git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.0.X@9370 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit dc0389cca04bf84dbc32ca2c9e1e6d5c3267e51a 1 parent f56df04
Karen Tracey authored
59  django/template/defaultfilters.py
... ...
@@ -1,6 +1,12 @@
1 1
 """Default variable filters."""
2 2
 
3 3
 import re
  4
+
  5
+try:
  6
+    from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
  7
+except ImportError:
  8
+    from django.utils._decimal import Decimal, InvalidOperation, ROUND_HALF_UP
  9
+
4 10
 import random as random_module
5 11
 try:
6 12
     from functools import wraps
@@ -45,7 +51,6 @@ def _dec(*args, **kwargs):
45 51
 # STRINGS         #
46 52
 ###################
47 53
 
48  
-
49 54
 def addslashes(value):
50 55
     """
51 56
     Adds slashes before quotes. Useful for escaping strings in CSV, for
@@ -92,6 +97,18 @@ def fix_ampersands(value):
92 97
 fix_ampersands.is_safe=True
93 98
 fix_ampersands = stringfilter(fix_ampersands)
94 99
 
  100
+# Values for testing floatformat input against infinity and NaN representations,
  101
+# which differ across platforms and Python versions.  Some (i.e. old Windows
  102
+# ones) are not recognized by Decimal but we want to return them unchanged vs. 
  103
+# returning an empty string as we do for completley invalid input.  Note these
  104
+# need to be built up from values that are not inf/nan, since inf/nan values do 
  105
+# not reload properly from .pyc files on Windows prior to some level of Python 2.5 
  106
+# (see Python Issue757815 and Issue1080440).
  107
+pos_inf = 1e200 * 1e200
  108
+neg_inf = -1e200 * 1e200
  109
+nan = (1e200 * 1e200) / (1e200 * 1e200)
  110
+special_floats = [str(pos_inf), str(neg_inf), str(nan)]
  111
+
95 112
 def floatformat(text, arg=-1):
96 113
     """
97 114
     Displays a float to a specified number of decimal places.
@@ -119,24 +136,42 @@ def floatformat(text, arg=-1):
119 136
     * {{ num1|floatformat:"-3" }} displays "34.232"
120 137
     * {{ num2|floatformat:"-3" }} displays "34"
121 138
     * {{ num3|floatformat:"-3" }} displays "34.260"
  139
+    
  140
+    If the input float is infinity or NaN, the (platform-dependent) string
  141
+    representation of that value will be displayed.
122 142
     """
  143
+        
123 144
     try:
124  
-        f = float(text)
125  
-    except (ValueError, TypeError):
  145
+        input_val = force_unicode(text)
  146
+        d = Decimal(input_val)
  147
+    except UnicodeEncodeError:
126 148
         return u''
  149
+    except InvalidOperation:
  150
+        if input_val in special_floats:
  151
+            return input_val
  152
+        else:
  153
+            return u''
127 154
     try:
128  
-        d = int(arg)
  155
+        p = int(arg)
129 156
     except ValueError:
130  
-        return force_unicode(f)
  157
+        return input_val
  158
+    
131 159
     try:
132  
-        m = f - int(f)
133  
-    except OverflowError:
134  
-        return force_unicode(f)
135  
-    if not m and d < 0:
136  
-        return mark_safe(u'%d' % int(f))
  160
+        m = int(d) - d
  161
+    except (OverflowError, InvalidOperation):
  162
+        return input_val
  163
+    
  164
+    if not m and p < 0:
  165
+        return mark_safe(u'%d' % (int(d)))
  166
+    
  167
+    if p == 0:
  168
+        exp = Decimal(1)
137 169
     else:
138  
-        formatstr = u'%%.%df' % abs(d)
139  
-        return mark_safe(formatstr % f)
  170
+        exp = Decimal('1.0') / (Decimal(10) ** abs(p))
  171
+    try:
  172
+        return mark_safe(u'%s' % str(d.quantize(exp, ROUND_HALF_UP)))
  173
+    except InvalidOperation:
  174
+        return input_val
140 175
 floatformat.is_safe = True
141 176
 
142 177
 def iriencode(value):
19  tests/regressiontests/defaultfilters/tests.py
@@ -13,15 +13,15 @@
13 13
 u'0.0'
14 14
 >>> floatformat(0.0)
15 15
 u'0'
16  
->>> floatformat(7.7,3)
  16
+>>> floatformat(7.7, 3)
17 17
 u'7.700'
18  
->>> floatformat(6.000000,3)
  18
+>>> floatformat(6.000000, 3)
19 19
 u'6.000'
20 20
 >>> floatformat(6.200000, 3)
21 21
 u'6.200'
22 22
 >>> floatformat(6.200000, -3)
23 23
 u'6.200'
24  
->>> floatformat(13.1031,-3)
  24
+>>> floatformat(13.1031, -3)
25 25
 u'13.103'
26 26
 >>> floatformat(11.1197, -2)
27 27
 u'11.12'
@@ -35,10 +35,23 @@
35 35
 u''
36 36
 >>> floatformat(13.1031, u'bar')
37 37
 u'13.1031'
  38
+>>> floatformat(18.125, 2) 
  39
+u'18.13' 
38 40
 >>> floatformat(u'foo', u'bar')
39 41
 u''
  42
+>>> floatformat(u'¿Cómo esta usted?')
  43
+u''
40 44
 >>> floatformat(None)
41 45
 u''
  46
+>>> pos_inf = float(1e30000)
  47
+>>> floatformat(pos_inf) == unicode(pos_inf)
  48
+True
  49
+>>> neg_inf = float(-1e30000)
  50
+>>> floatformat(neg_inf) == unicode(neg_inf)
  51
+True
  52
+>>> nan = pos_inf / pos_inf
  53
+>>> floatformat(nan) == unicode(nan)
  54
+True
42 55
 
43 56
 >>> addslashes(u'"double quotes" and \'single quotes\'')
44 57
 u'\\"double quotes\\" and \\\'single quotes\\\''

0 notes on commit dc0389c

Please sign in to comment.
Something went wrong with that request. Please try again.