[1.2.X] Fixed #14290 -- Made format localization faster by caching the format modules. Thanks, Teemu Kurppa and Anssi Kääriäinen for the report and initial patches.

Backport from trunk (r13898).

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.2.X@13903 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Jannis Leidel 2010-09-27 15:27:07 +00:00
parent 6ee04e0f97
commit d99f19fb6a
7 changed files with 68 additions and 29 deletions

View File

@ -7,29 +7,36 @@ from django.utils.importlib import import_module
from django.utils.encoding import smart_str
from django.utils import dateformat, numberformat, datetime_safe
def get_format_modules(reverse=False):
# format_cache is a mapping from (format_type, lang) to the format string.
# By using the cache, it is possible to avoid running get_format_modules
# repeatedly.
_format_cache = {}
_format_modules_cache = {}
def iter_format_modules(lang):
"""
Returns an iterator over the format modules found in the project and Django
Does the heavy lifting of finding format modules.
"""
modules = []
if not check_for_language(get_language()) or not settings.USE_L10N:
return modules
locale = to_locale(get_language())
if check_for_language(lang) or settings.USE_L10N:
format_locations = ['django.conf.locale.%s']
if settings.FORMAT_MODULE_PATH:
format_locations = [settings.FORMAT_MODULE_PATH + '.%s']
else:
format_locations = []
format_locations.append('django.conf.locale.%s')
format_locations.append(settings.FORMAT_MODULE_PATH + '.%s')
format_locations.reverse()
locale = to_locale(lang)
locales = set((locale, locale.split('_')[0]))
for location in format_locations:
for l in (locale, locale.split('_')[0]):
for loc in locales:
try:
mod = import_module('.formats', location % l)
yield import_module('.formats', location % loc)
except ImportError:
pass
else:
# Don't return duplicates
if mod not in modules:
modules.append(mod)
def get_format_modules(reverse=False):
"""
Returns an iterator over the format modules found
"""
lang = get_language()
modules = _format_modules_cache.setdefault(lang, list(iter_format_modules(lang)))
if reverse:
modules.reverse()
return modules
@ -42,11 +49,18 @@ def get_format(format_type):
"""
format_type = smart_str(format_type)
if settings.USE_L10N:
cache_key = (format_type, get_language())
try:
return _format_cache[cache_key] or getattr(settings, format_type)
except KeyError:
for module in get_format_modules():
try:
return getattr(module, format_type)
val = getattr(module, format_type)
_format_cache[cache_key] = val
return val
except AttributeError:
pass
_format_cache[cache_key] = None
return getattr(settings, format_type)
def date_format(value, format=None):

View File

@ -1,4 +1,6 @@
from django.conf import settings
from django.utils.safestring import mark_safe
def format(number, decimal_sep, decimal_pos, grouping=0, thousand_sep=''):
"""
@ -11,15 +13,20 @@ def format(number, decimal_sep, decimal_pos, grouping=0, thousand_sep=''):
* thousand_sep: Thousand separator symbol (for example ",")
"""
use_grouping = settings.USE_L10N and \
settings.USE_THOUSAND_SEPARATOR and grouping
# Make the common case fast:
if isinstance(number, int) and not use_grouping and not decimal_pos:
return mark_safe(unicode(number))
# sign
if float(number) < 0:
sign = '-'
else:
sign = ''
# decimal part
str_number = unicode(number)
if str_number[0] == '-':
str_number = str_number[1:]
# decimal part
if '.' in str_number:
int_part, dec_part = str_number.split('.')
if decimal_pos:
@ -30,13 +37,12 @@ def format(number, decimal_sep, decimal_pos, grouping=0, thousand_sep=''):
dec_part = dec_part + ('0' * (decimal_pos - len(dec_part)))
if dec_part: dec_part = decimal_sep + dec_part
# grouping
if settings.USE_L10N and settings.USE_THOUSAND_SEPARATOR and grouping:
if use_grouping:
int_part_gd = ''
for cnt, digit in enumerate(int_part[::-1]):
if cnt and not cnt % grouping:
int_part_gd += thousand_sep
int_part_gd += digit
int_part = int_part_gd[::-1]
return sign + int_part + dec_part

View File

@ -8,10 +8,11 @@ import pickle
from django.conf import settings
from django.template import Template, Context
from django.test import TestCase
from django.utils.formats import get_format, date_format, time_format, localize, localize_input
from django.utils.formats import get_format, date_format, time_format, localize, localize_input, iter_format_modules
from django.utils.numberformat import format as nformat
from django.utils.safestring import mark_safe, SafeString, SafeUnicode
from django.utils.translation import ugettext, ugettext_lazy, activate, deactivate, gettext_lazy, to_locale
from django.utils.importlib import import_module
from forms import I18nForm, SelectDateForm, SelectDateWidget, CompanyForm
@ -423,6 +424,24 @@ class FormattingTests(TestCase):
finally:
deactivate()
def test_iter_format_modules(self):
"""
Tests the iter_format_modules function.
"""
activate('de-at')
old_format_module_path = settings.FORMAT_MODULE_PATH
try:
settings.USE_L10N = True
de_format_mod = import_module('django.conf.locale.de.formats')
self.assertEqual(list(iter_format_modules('de')), [de_format_mod])
settings.FORMAT_MODULE_PATH = 'regressiontests.i18n.other.locale'
test_de_format_mod = import_module('regressiontests.i18n.other.locale.de.formats')
self.assertEqual(list(iter_format_modules('de')), [test_de_format_mod, de_format_mod])
finally:
settings.FORMAT_MODULE_PATH = old_format_module_path
deactivate()
class MiscTests(TestCase):
def test_parse_spec_http_header(self):