kopia lustrzana https://github.com/wagtail/wagtail
Patch number formatting functions during tests to flag up potential USE_THOUSAND_SEPARATOR issues
rodzic
85c0047268
commit
ff7e016eb5
|
@ -39,6 +39,9 @@ rules:
|
|||
- metavariable-regex:
|
||||
metavariable: $STRING_ID
|
||||
regex: ".*%\\w.*"
|
||||
paths:
|
||||
exclude:
|
||||
- 'wagtail/test/numberformat.py'
|
||||
message: >
|
||||
Do not use anonymous placeholders for translations.
|
||||
Use printf style formatting with named placeholders instead.
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
# Patch Django's number formatting functions during tests so that outputting a number onto a
|
||||
# template without explicitly passing it through one of |intcomma, |localize, |unlocalize or
|
||||
# |filesizeformat will raise an exception. This helps to catch bugs where
|
||||
# USE_THOUSAND_SEPARATOR = True incorrectly reformats numbers that are not intended to be
|
||||
# human-readable (such as image dimensions, or IDs within data attributes).
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
import django.contrib.humanize.templatetags.humanize
|
||||
import django.template.defaultfilters
|
||||
import django.templatetags.l10n
|
||||
import django.utils.numberformat
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.utils import formats
|
||||
from django.utils.html import avoid_wrapping
|
||||
from django.utils.translation import gettext, ngettext
|
||||
|
||||
original_numberformat = django.utils.numberformat.format
|
||||
original_intcomma = django.contrib.humanize.templatetags.humanize.intcomma
|
||||
|
||||
|
||||
def patched_numberformat(*args, use_l10n=None, **kwargs):
|
||||
if use_l10n is False or use_l10n == "explicit":
|
||||
return original_numberformat(*args, use_l10n=use_l10n, **kwargs)
|
||||
|
||||
raise ImproperlyConfigured(
|
||||
"A number was used directly on a template. "
|
||||
"Numbers output on templates should be passed through one of |intcomma, |localize, "
|
||||
"|unlocalize or |filesizeformat to avoid issues with USE_THOUSAND_SEPARATOR."
|
||||
)
|
||||
|
||||
|
||||
def patched_intcomma(value, use_l10n=True):
|
||||
if use_l10n:
|
||||
try:
|
||||
if not isinstance(value, (float, Decimal)):
|
||||
value = int(value)
|
||||
except (TypeError, ValueError):
|
||||
return original_intcomma(value, False)
|
||||
else:
|
||||
return formats.number_format(
|
||||
value, use_l10n="explicit", force_grouping=True
|
||||
)
|
||||
|
||||
return original_intcomma(value, use_l10n=use_l10n)
|
||||
|
||||
|
||||
def patched_filesizeformat(bytes_):
|
||||
"""
|
||||
Format the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB,
|
||||
102 bytes, etc.).
|
||||
"""
|
||||
try:
|
||||
bytes_ = int(bytes_)
|
||||
except (TypeError, ValueError, UnicodeDecodeError):
|
||||
value = ngettext("%(size)d byte", "%(size)d bytes", 0) % {"size": 0}
|
||||
return avoid_wrapping(value)
|
||||
|
||||
def filesize_number_format(value):
|
||||
return formats.number_format(round(value, 1), 1, use_l10n="explicit")
|
||||
|
||||
KB = 1 << 10
|
||||
MB = 1 << 20
|
||||
GB = 1 << 30
|
||||
TB = 1 << 40
|
||||
PB = 1 << 50
|
||||
|
||||
negative = bytes_ < 0
|
||||
if negative:
|
||||
bytes_ = -bytes_ # Allow formatting of negative numbers.
|
||||
|
||||
if bytes_ < KB:
|
||||
value = ngettext("%(size)d byte", "%(size)d bytes", bytes_) % {"size": bytes_}
|
||||
elif bytes_ < MB:
|
||||
value = gettext("%s KB") % filesize_number_format(bytes_ / KB)
|
||||
elif bytes_ < GB:
|
||||
value = gettext("%s MB") % filesize_number_format(bytes_ / MB)
|
||||
elif bytes_ < TB:
|
||||
value = gettext("%s GB") % filesize_number_format(bytes_ / GB)
|
||||
elif bytes_ < PB:
|
||||
value = gettext("%s TB") % filesize_number_format(bytes_ / TB)
|
||||
else:
|
||||
value = gettext("%s PB") % filesize_number_format(bytes_ / PB)
|
||||
|
||||
if negative:
|
||||
value = "-%s" % value
|
||||
return avoid_wrapping(value)
|
||||
|
||||
|
||||
def patched_localize(value):
|
||||
return str(formats.localize(value, use_l10n="explicit"))
|
||||
|
||||
|
||||
def patch_number_formats():
|
||||
django.utils.numberformat.format = patched_numberformat
|
||||
django.contrib.humanize.templatetags.humanize.intcomma = patched_intcomma
|
||||
django.template.defaultfilters.filesizeformat = patched_filesizeformat
|
||||
django.template.defaultfilters.register.filter(
|
||||
"filesizeformat", patched_filesizeformat, is_safe=True
|
||||
)
|
||||
django.templatetags.l10n.localize = patched_localize
|
||||
django.templatetags.l10n.register.filter(
|
||||
"localize", patched_localize, is_safe=False
|
||||
)
|
|
@ -3,6 +3,10 @@ import os
|
|||
from django.contrib.messages import constants as message_constants
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from wagtail.test.numberformat import patch_number_formats
|
||||
|
||||
patch_number_formats()
|
||||
|
||||
DEBUG = os.environ.get("DJANGO_DEBUG", "false").lower() == "true"
|
||||
WAGTAIL_ROOT = os.path.dirname(os.path.dirname(__file__))
|
||||
WAGTAILADMIN_BASE_URL = "http://testserver"
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import json
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.template import Context, Template
|
||||
from django.test import TestCase
|
||||
|
||||
from wagtail.admin.tests.test_contentstate import content_state_equal
|
||||
|
@ -460,3 +462,21 @@ class TestDummyExternalStorage(WagtailTestUtils, TestCase):
|
|||
"Content file pointer should be at 0 - got 70 instead",
|
||||
):
|
||||
DummyExternalStorage().save("test.png", simple_png)
|
||||
|
||||
|
||||
class TestPatchedNumberFormat(TestCase):
|
||||
def test_outputting_number_directly_is_disallowed(self):
|
||||
context = Context({"num": 42})
|
||||
template = Template("the answer is {{ num }}")
|
||||
with self.assertRaises(ImproperlyConfigured):
|
||||
template.render(context)
|
||||
|
||||
def test_outputting_number_via_intcomma(self):
|
||||
context = Context({"num": 9000})
|
||||
template = Template("{% load wagtailadmin_tags %}It's over {{ num|intcomma }}!")
|
||||
self.assertEqual(template.render(context), "It's over 9,000!")
|
||||
|
||||
def test_outputting_number_via_unlocalize(self):
|
||||
context = Context({"num": 9000})
|
||||
template = Template("{% load l10n %}It's over {{ num|unlocalize }}!")
|
||||
self.assertEqual(template.render(context), "It's over 9000!")
|
||||
|
|
Ładowanie…
Reference in New Issue