#
-# 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
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')
"""
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)
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
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],
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:
import re
import sys
-from babel._compat import Decimal
+from babel._compat import decimal
_plural_tags = ('zero', 'one', 'two', 'few', 'many', 'other')
# 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 ()
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.
from datetime import date
from babel import numbers
-from babel._compat import Decimal
+from babel._compat import decimal
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')
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)
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)
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'))
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')
import pytest
from babel import plural, localedata
-from babel._compat import Decimal
+from babel._compat import decimal
def test_plural_rule():
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'
@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'))
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
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