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.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@9369 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
cadbf66635
commit
b81bc22ad2
|
@ -1,6 +1,12 @@
|
|||
"""Default variable filters."""
|
||||
|
||||
import re
|
||||
|
||||
try:
|
||||
from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
|
||||
except ImportError:
|
||||
from django.utils._decimal import Decimal, InvalidOperation, ROUND_HALF_UP
|
||||
|
||||
import random as random_module
|
||||
try:
|
||||
from functools import wraps
|
||||
|
@ -45,7 +51,6 @@ def stringfilter(func):
|
|||
# STRINGS #
|
||||
###################
|
||||
|
||||
|
||||
def addslashes(value):
|
||||
"""
|
||||
Adds slashes before quotes. Useful for escaping strings in CSV, for
|
||||
|
@ -92,6 +97,18 @@ def fix_ampersands(value):
|
|||
fix_ampersands.is_safe=True
|
||||
fix_ampersands = stringfilter(fix_ampersands)
|
||||
|
||||
# Values for testing floatformat input against infinity and NaN representations,
|
||||
# which differ across platforms and Python versions. Some (i.e. old Windows
|
||||
# ones) are not recognized by Decimal but we want to return them unchanged vs.
|
||||
# returning an empty string as we do for completley invalid input. Note these
|
||||
# need to be built up from values that are not inf/nan, since inf/nan values do
|
||||
# not reload properly from .pyc files on Windows prior to some level of Python 2.5
|
||||
# (see Python Issue757815 and Issue1080440).
|
||||
pos_inf = 1e200 * 1e200
|
||||
neg_inf = -1e200 * 1e200
|
||||
nan = (1e200 * 1e200) / (1e200 * 1e200)
|
||||
special_floats = [str(pos_inf), str(neg_inf), str(nan)]
|
||||
|
||||
def floatformat(text, arg=-1):
|
||||
"""
|
||||
Displays a float to a specified number of decimal places.
|
||||
|
@ -119,24 +136,42 @@ def floatformat(text, arg=-1):
|
|||
* {{ num1|floatformat:"-3" }} displays "34.232"
|
||||
* {{ num2|floatformat:"-3" }} displays "34"
|
||||
* {{ num3|floatformat:"-3" }} displays "34.260"
|
||||
|
||||
If the input float is infinity or NaN, the (platform-dependent) string
|
||||
representation of that value will be displayed.
|
||||
"""
|
||||
|
||||
try:
|
||||
f = float(text)
|
||||
except (ValueError, TypeError):
|
||||
input_val = force_unicode(text)
|
||||
d = Decimal(input_val)
|
||||
except UnicodeEncodeError:
|
||||
return u''
|
||||
except InvalidOperation:
|
||||
if input_val in special_floats:
|
||||
return input_val
|
||||
else:
|
||||
return u''
|
||||
try:
|
||||
d = int(arg)
|
||||
p = int(arg)
|
||||
except ValueError:
|
||||
return force_unicode(f)
|
||||
return input_val
|
||||
|
||||
try:
|
||||
m = f - int(f)
|
||||
except OverflowError:
|
||||
return force_unicode(f)
|
||||
if not m and d < 0:
|
||||
return mark_safe(u'%d' % int(f))
|
||||
m = int(d) - d
|
||||
except (OverflowError, InvalidOperation):
|
||||
return input_val
|
||||
|
||||
if not m and p < 0:
|
||||
return mark_safe(u'%d' % (int(d)))
|
||||
|
||||
if p == 0:
|
||||
exp = Decimal(1)
|
||||
else:
|
||||
formatstr = u'%%.%df' % abs(d)
|
||||
return mark_safe(formatstr % f)
|
||||
exp = Decimal('1.0') / (Decimal(10) ** abs(p))
|
||||
try:
|
||||
return mark_safe(u'%s' % str(d.quantize(exp, ROUND_HALF_UP)))
|
||||
except InvalidOperation:
|
||||
return input_val
|
||||
floatformat.is_safe = True
|
||||
|
||||
def iriencode(value):
|
||||
|
|
|
@ -13,15 +13,15 @@ u'0.1'
|
|||
u'0.0'
|
||||
>>> floatformat(0.0)
|
||||
u'0'
|
||||
>>> floatformat(7.7,3)
|
||||
>>> floatformat(7.7, 3)
|
||||
u'7.700'
|
||||
>>> floatformat(6.000000,3)
|
||||
>>> floatformat(6.000000, 3)
|
||||
u'6.000'
|
||||
>>> floatformat(6.200000, 3)
|
||||
u'6.200'
|
||||
>>> floatformat(6.200000, -3)
|
||||
u'6.200'
|
||||
>>> floatformat(13.1031,-3)
|
||||
>>> floatformat(13.1031, -3)
|
||||
u'13.103'
|
||||
>>> floatformat(11.1197, -2)
|
||||
u'11.12'
|
||||
|
@ -35,10 +35,23 @@ u'8.280'
|
|||
u''
|
||||
>>> floatformat(13.1031, u'bar')
|
||||
u'13.1031'
|
||||
>>> floatformat(18.125, 2)
|
||||
u'18.13'
|
||||
>>> floatformat(u'foo', u'bar')
|
||||
u''
|
||||
>>> floatformat(u'¿Cómo esta usted?')
|
||||
u''
|
||||
>>> floatformat(None)
|
||||
u''
|
||||
>>> pos_inf = float(1e30000)
|
||||
>>> floatformat(pos_inf) == unicode(pos_inf)
|
||||
True
|
||||
>>> neg_inf = float(-1e30000)
|
||||
>>> floatformat(neg_inf) == unicode(neg_inf)
|
||||
True
|
||||
>>> nan = pos_inf / pos_inf
|
||||
>>> floatformat(nan) == unicode(nan)
|
||||
True
|
||||
|
||||
>>> addslashes(u'"double quotes" and \'single quotes\'')
|
||||
u'\\"double quotes\\" and \\\'single quotes\\\''
|
||||
|
|
Loading…
Reference in New Issue