From: Isaac Jurado Date: Sun, 29 May 2016 07:40:24 +0000 (+0200) Subject: Simplify the selection of decimal implementation X-Git-Tag: v2.4.0~16^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8341064c151b50135ac083b33117420efeda4d96;p=thirdparty%2Fbabel.git Simplify the selection of decimal implementation Instead of selecting individual symbols, expose only the chosen decimal module through babel._compat. This forces Babel to always use "decimal." prefix. Howver, it will simplify the code for users that need to manipulate the decimal context. --- diff --git a/babel/_compat.py b/babel/_compat.py index ae290f42..aea33893 100644 --- a/babel/_compat.py +++ b/babel/_compat.py @@ -58,15 +58,13 @@ number_types = integer_types + (float,) # -# Use cdecimal when available +# Since Python 3.3, a fast decimal implementation is already included in the +# standard library. Otherwise use cdecimal when available # -from decimal import (Decimal as _dec, - InvalidOperation as _invop) -try: - from cdecimal import (Decimal as _cdec, - InvalidOperation as _cinvop) - Decimal = _cdec - InvalidOperation = (_invop, _cinvop) -except ImportError: - Decimal = _dec - InvalidOperation = _invop +if sys.version_info[:2] >= (3, 3): + import decimal +else: + try: + import cdecimal as decimal + except ImportError: + import decimal diff --git a/babel/numbers.py b/babel/numbers.py index 67881ae5..f9be88dd 100644 --- a/babel/numbers.py +++ b/babel/numbers.py @@ -22,7 +22,7 @@ import re from datetime import date as date_, datetime as datetime_ from babel.core import default_locale, Locale, get_global -from babel._compat import Decimal, InvalidOperation +from babel._compat import decimal LC_NUMERIC = default_locale('LC_NUMERIC') @@ -437,9 +437,9 @@ def parse_decimal(string, locale=LC_NUMERIC): """ locale = Locale.parse(locale) try: - return Decimal(string.replace(get_group_symbol(locale), '') - .replace(get_decimal_symbol(locale), '.')) - except InvalidOperation: + return decimal.Decimal(string.replace(get_group_symbol(locale), '') + .replace(get_decimal_symbol(locale), '.')) + except decimal.InvalidOperation: raise NumberFormatError('%r is not a valid decimal number' % string) @@ -566,8 +566,8 @@ class NumberPattern(object): def apply(self, value, locale, currency=None, force_frac=None): frac_prec = force_frac or self.frac_prec - if not isinstance(value, Decimal): - value = Decimal(str(value)) + if not isinstance(value, decimal.Decimal): + value = decimal.Decimal(str(value)) value = value.scaleb(self.scale) is_negative = int(value.is_signed()) if self.exp_prec: # Scientific notation @@ -603,7 +603,7 @@ class NumberPattern(object): if sep: number += get_decimal_symbol(locale) + b else: # A normal number pattern - precision = Decimal('1.' + '1' * frac_prec[1]) + precision = decimal.Decimal('1.' + '1' * frac_prec[1]) rounded = value.quantize(precision) a, sep, b = str(abs(rounded)).partition(".") number = (self._format_int(a, self.int_prec[0], @@ -641,7 +641,7 @@ class NumberPattern(object): def _format_significant(self, value, minimum, maximum): exp = value.adjusted() scale = maximum - 1 - exp - digits = str(value.scaleb(scale).quantize(Decimal(1))) + digits = str(value.scaleb(scale).quantize(decimal.Decimal(1))) if scale <= 0: result = digits + '0' * -scale else: diff --git a/babel/plural.py b/babel/plural.py index 980629dc..0b8e425d 100644 --- a/babel/plural.py +++ b/babel/plural.py @@ -11,7 +11,7 @@ import re import sys -from babel._compat import Decimal +from babel._compat import decimal _plural_tags = ('zero', 'one', 'two', 'few', 'many', 'other') @@ -33,9 +33,9 @@ def extract_operands(source): # 2.6's Decimal cannot convert from float directly if sys.version_info < (2, 7): n = str(n) - n = Decimal(n) + n = decimal.Decimal(n) - if isinstance(n, Decimal): + if isinstance(n, decimal.Decimal): dec_tuple = n.as_tuple() exp = dec_tuple.exponent fraction_digits = dec_tuple.digits[exp:] if exp < 0 else () diff --git a/babel/units.py b/babel/units.py index 798ade20..1ea5b17c 100644 --- a/babel/units.py +++ b/babel/units.py @@ -80,8 +80,8 @@ def format_unit(value, measurement_unit, length='long', format=None, locale=LC_N Number formats may be overridden with the ``format`` parameter. - >>> from babel._compat import Decimal - >>> format_unit(Decimal("-42.774"), 'temperature-celsius', 'short', format='#.0', locale='fr') + >>> from babel._compat import decimal + >>> format_unit(decimal.Decimal("-42.774"), 'temperature-celsius', 'short', format='#.0', locale='fr') u'-42,8 \\xb0C' The locale's usual pluralization rules are respected. diff --git a/tests/test_numbers.py b/tests/test_numbers.py index 19f9bc76..32a969d1 100644 --- a/tests/test_numbers.py +++ b/tests/test_numbers.py @@ -17,7 +17,7 @@ import pytest from datetime import date from babel import numbers -from babel._compat import Decimal +from babel._compat import decimal class FormatDecimalTestCase(unittest.TestCase): @@ -94,16 +94,16 @@ class FormatDecimalTestCase(unittest.TestCase): def test_decimals(self): """Test significant digits patterns""" - self.assertEqual(numbers.format_decimal(Decimal('1.2345'), + self.assertEqual(numbers.format_decimal(decimal.Decimal('1.2345'), '#.00', locale='en_US'), '1.23') - self.assertEqual(numbers.format_decimal(Decimal('1.2345000'), + self.assertEqual(numbers.format_decimal(decimal.Decimal('1.2345000'), '#.00', locale='en_US'), '1.23') - self.assertEqual(numbers.format_decimal(Decimal('1.2345000'), + self.assertEqual(numbers.format_decimal(decimal.Decimal('1.2345000'), '@@', locale='en_US'), '1.2') - self.assertEqual(numbers.format_decimal(Decimal('12345678901234567890.12345'), + self.assertEqual(numbers.format_decimal(decimal.Decimal('12345678901234567890.12345'), '#.00', locale='en_US'), '12345678901234567890.12') @@ -136,7 +136,7 @@ class FormatDecimalTestCase(unittest.TestCase): self.assertEqual(fmt, '1.23E02 m/s') fmt = numbers.format_scientific(0.012345, '#.##E00 m/s', locale='en_US') self.assertEqual(fmt, '1.23E-02 m/s') - fmt = numbers.format_scientific(Decimal('12345'), '#.##E+00 m/s', + fmt = numbers.format_scientific(decimal.Decimal('12345'), '#.##E+00 m/s', locale='en_US') self.assertEqual(fmt, '1.23E+04 m/s') # 0 (see ticket #99) @@ -146,7 +146,7 @@ class FormatDecimalTestCase(unittest.TestCase): def test_formatting_of_very_small_decimals(self): # previously formatting very small decimals could lead to a type error # because the Decimal->string conversion was too simple (see #214) - number = Decimal("7E-7") + number = decimal.Decimal("7E-7") fmt = numbers.format_decimal(number, format="@@@", locale='en_US') self.assertEqual('0.000000700', fmt) @@ -154,9 +154,9 @@ class FormatDecimalTestCase(unittest.TestCase): class NumberParsingTestCase(unittest.TestCase): def test_can_parse_decimals(self): - self.assertEqual(Decimal('1099.98'), + self.assertEqual(decimal.Decimal('1099.98'), numbers.parse_decimal('1,099.98', locale='en_US')) - self.assertEqual(Decimal('1099.98'), + self.assertEqual(decimal.Decimal('1099.98'), numbers.parse_decimal('1.099,98', locale='de')) self.assertRaises(numbers.NumberFormatError, lambda: numbers.parse_decimal('2,109,998', locale='de')) @@ -302,8 +302,8 @@ def test_parse_number(): def test_parse_decimal(): assert (numbers.parse_decimal('1,099.98', locale='en_US') - == Decimal('1099.98')) - assert numbers.parse_decimal('1.099,98', locale='de') == Decimal('1099.98') + == decimal.Decimal('1099.98')) + assert numbers.parse_decimal('1.099,98', locale='de') == decimal.Decimal('1099.98') with pytest.raises(numbers.NumberFormatError) as excinfo: numbers.parse_decimal('2,109,998', locale='de') diff --git a/tests/test_plural.py b/tests/test_plural.py index 60a2de4a..6406af87 100644 --- a/tests/test_plural.py +++ b/tests/test_plural.py @@ -14,7 +14,7 @@ import unittest import pytest from babel import plural, localedata -from babel._compat import Decimal +from babel._compat import decimal def test_plural_rule(): @@ -34,29 +34,29 @@ def test_plural_rule_operands_i(): def test_plural_rule_operands_v(): rule = plural.PluralRule({'one': 'v is 2'}) - assert rule(Decimal('1.20')) == 'one' - assert rule(Decimal('1.2')) == 'other' + assert rule(decimal.Decimal('1.20')) == 'one' + assert rule(decimal.Decimal('1.2')) == 'other' assert rule(2) == 'other' def test_plural_rule_operands_w(): rule = plural.PluralRule({'one': 'w is 2'}) - assert rule(Decimal('1.23')) == 'one' - assert rule(Decimal('1.20')) == 'other' + assert rule(decimal.Decimal('1.23')) == 'one' + assert rule(decimal.Decimal('1.20')) == 'other' assert rule(1.2) == 'other' def test_plural_rule_operands_f(): rule = plural.PluralRule({'one': 'f is 20'}) - assert rule(Decimal('1.23')) == 'other' - assert rule(Decimal('1.20')) == 'one' + assert rule(decimal.Decimal('1.23')) == 'other' + assert rule(decimal.Decimal('1.20')) == 'one' assert rule(1.2) == 'other' def test_plural_rule_operands_t(): rule = plural.PluralRule({'one': 't = 5'}) - assert rule(Decimal('1.53')) == 'other' - assert rule(Decimal('1.50')) == 'one' + assert rule(decimal.Decimal('1.53')) == 'other' + assert rule(decimal.Decimal('1.50')) == 'one' assert rule(1.5) == 'one' @@ -253,9 +253,9 @@ EXTRACT_OPERANDS_TESTS = ( @pytest.mark.parametrize('source,n,i,v,w,f,t', EXTRACT_OPERANDS_TESTS) def test_extract_operands(source, n, i, v, w, f, t): - source = Decimal(source) if isinstance(source, str) else source + source = decimal.Decimal(source) if isinstance(source, str) else source assert (plural.extract_operands(source) == - Decimal(n), i, v, w, f, t) + decimal.Decimal(n), i, v, w, f, t) @pytest.mark.parametrize('locale', ('ru', 'pl')) diff --git a/tests/test_smoke.py b/tests/test_smoke.py index eda10ed3..3993f608 100644 --- a/tests/test_smoke.py +++ b/tests/test_smoke.py @@ -11,7 +11,7 @@ import pytest from babel import Locale from babel import dates from babel import numbers -from babel._compat import Decimal +from babel._compat import decimal @pytest.mark.all_locales @@ -28,8 +28,8 @@ def test_smoke_dates(locale): def test_smoke_numbers(locale): locale = Locale.parse(locale) for number in ( - Decimal("-33.76"), # Negative Decimal - Decimal("13.37"), # Positive Decimal + decimal.Decimal("-33.76"), # Negative Decimal + decimal.Decimal("13.37"), # Positive Decimal 1.2 - 1.0, # Inaccurate float 10, # Plain old integer 0, # Zero