Fixed #27486 -- Fixed Python 3.7 DeprecationWarning in intword and filesizeformat filters.

intword and filesizeformat passed floats to ngettext() which is
deprecated in Python 3.7. The rationale for this warning is documented
in BPO-28692: https://bugs.python.org/issue28692.

For filesizeformat, the filesize value is expected to be an int -- it
fills %d string formatting placeholders. It was likely coerced to a
float to ensure floating point division on Python 2. Python 3 always
does floating point division, so coerce to an int instead of a float to
fix the warning.

For intword, the number may contain a decimal component. In English, a
decimal component makes the noun plural. A helper function,
round_away_from_one(), was added to convert the float to an integer that
is appropriate for ngettext().
This commit is contained in:
Jon Dufresne 2019-06-09 12:48:20 -07:00 committed by Mariusz Felisiak
parent 175656e166
commit 9e38ed0536
6 changed files with 51 additions and 7 deletions

View File

@ -10,7 +10,7 @@ from django.utils.safestring import mark_safe
from django.utils.timezone import is_aware, utc
from django.utils.translation import (
gettext as _, gettext_lazy, ngettext, ngettext_lazy, npgettext_lazy,
pgettext,
pgettext, round_away_from_one,
)
register = template.Library()
@ -158,7 +158,8 @@ def intword(value):
large_number = 10 ** exponent
if value < large_number * 1000:
new_value = value / large_number
return _check_for_i18n(new_value, *converters(new_value))
rounded_value = round_away_from_one(new_value)
return _check_for_i18n(new_value, *converters(rounded_value))
return value

View File

@ -812,7 +812,7 @@ def filesizeformat(bytes_):
102 bytes, etc.).
"""
try:
bytes_ = float(bytes_)
bytes_ = int(bytes_)
except (TypeError, ValueError, UnicodeDecodeError):
value = ngettext("%(size)d byte", "%(size)d bytes", 0) % {'size': 0}
return avoid_wrapping(value)

View File

@ -4,6 +4,7 @@ Internationalization support.
import re
import warnings
from contextlib import ContextDecorator
from decimal import ROUND_UP, Decimal
from django.utils.autoreload import autoreload_started, file_changed
from django.utils.deprecation import RemovedInDjango40Warning
@ -332,3 +333,7 @@ trim_whitespace_re = re.compile(r'\s*\n\s*')
def trim_whitespace(s):
return trim_whitespace_re.sub(' ', s.strip())
def round_away_from_one(value):
return int(Decimal(value - 1).quantize(Decimal('0'), rounding=ROUND_UP)) + 1

View File

@ -57,7 +57,9 @@ e.g. with the ``'de'`` language:
===========
Converts a large integer (or a string representation of an integer) to a
friendly text representation. Works best for numbers over 1 million.
friendly text representation. Translates ``1.0`` as a singular phrase and all
other numeric values as plural, this may be incorrect for some languages. Works
best for numbers over 1 million.
Examples:
@ -74,6 +76,11 @@ e.g. with the ``'de'`` language:
* ``1200000`` becomes ``'1,2 Millionen'``.
* ``1200000000`` becomes ``'1,2 Milliarden'``.
.. versionchanged:: 3.0
All numeric values are now translated as plural, except ``1.0`` which is
translated as a singular phrase. This may be incorrect for some languages.
.. templatefilter:: naturalday
``naturalday``

View File

@ -416,6 +416,9 @@ Miscellaneous
when ``doseq=False``, rather than iterating them, bringing it into line with
the standard library :func:`urllib.parse.urlencode` function.
* ``intword`` template filter now translates ``1.0`` as a singular phrase and
all other numeric values as plural. This may be incorrect for some languages.
.. _deprecated-features-3.0:
Features deprecated in 3.0

View File

@ -34,9 +34,9 @@ from django.utils.translation import (
LANGUAGE_SESSION_KEY, activate, check_for_language, deactivate,
get_language, get_language_bidi, get_language_from_request,
get_language_info, gettext, gettext_lazy, ngettext, ngettext_lazy,
npgettext, npgettext_lazy, pgettext, to_language, to_locale, trans_null,
trans_real, ugettext, ugettext_lazy, ugettext_noop, ungettext,
ungettext_lazy,
npgettext, npgettext_lazy, pgettext, round_away_from_one, to_language,
to_locale, trans_null, trans_real, ugettext, ugettext_lazy, ugettext_noop,
ungettext, ungettext_lazy,
)
from django.utils.translation.reloader import (
translation_file_changed, watch_for_translation_changes,
@ -1883,3 +1883,31 @@ class TranslationFileChangedTests(SimpleTestCase):
self.assertEqual(trans_real._translations, {})
self.assertIsNone(trans_real._default)
self.assertIsInstance(trans_real._active, _thread._local)
class UtilsTests(SimpleTestCase):
def test_round_away_from_one(self):
tests = [
(0, 0),
(0., 0),
(0.25, 0),
(0.5, 0),
(0.75, 0),
(1, 1),
(1., 1),
(1.25, 2),
(1.5, 2),
(1.75, 2),
(-0., 0),
(-0.25, -1),
(-0.5, -1),
(-0.75, -1),
(-1, -1),
(-1., -1),
(-1.25, -2),
(-1.5, -2),
(-1.75, -2),
]
for value, expected in tests:
with self.subTest(value=value):
self.assertEqual(round_away_from_one(value), expected)