summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephan Richter <stephan.richter@gmail.com>2017-05-23 11:32:25 -0400
committerGitHub <noreply@github.com>2017-05-23 11:32:25 -0400
commit87775ca4f7b75461ecc475f1f485115240104310 (patch)
treec2ee28b2b8769cb4314df328f3b496b6d130c381
parent4355a23dc341b243aa09368f74602fc774b747db (diff)
parent63e752a934476377e9d7c01401f53d042e357725 (diff)
downloadzope-i18n-87775ca4f7b75461ecc475f1f485115240104310.tar.gz
Merge pull request #2 from Shoobx/master
Support formatting really small numbers and explicit rounding.
-rw-r--r--CHANGES.rst17
-rw-r--r--setup.py4
-rw-r--r--src/zope/i18n/format.py37
-rw-r--r--src/zope/i18n/tests/locale3/en/LC_MESSAGES/zope-i18n.inbin364 -> 315 bytes
-rw-r--r--src/zope/i18n/tests/locale3/en/LC_MESSAGES/zope-i18n.mobin295 -> 315 bytes
-rw-r--r--src/zope/i18n/tests/test_formats.py26
-rw-r--r--tox.ini4
7 files changed, 74 insertions, 14 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index 7162443..1f40674 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -5,12 +5,20 @@ CHANGES
4.2.0 (unreleased)
------------------
-- Better error message on PO-File Syntax Errors.
- [SyZn]
+- Better error message on PO-File Syntax Errors. [SyZn]
-- Add support for Python 3.5.
+- Add support for Python 3.5 and 3.6.
-- Drop support for Python 2.6 and 3.2.
+- Drop support for Python 2.6, 3.2 and 3.3.
+
+- Support for formatting really small numbers, e.g. 1e-9. These numbers needs
+ special treatment, because standard str(x) collapses them to scientific
+ representation.
+
+- Support for specifying rounding in NumberFormatter. This is required in some
+ cases, e.g. when you format a Decimal('0.9999') that sould not be rounded.
+ Currently, formatting Decimal('0.99999') will raise a TypeError if rounding
+ is not set to False
4.1.0 (2015-11-06)
@@ -19,6 +27,7 @@ CHANGES
- ``interpolate()`` now works recursively, if the mapping has a value which is
a ``zope.i18nmessageid.Message`` itself.
+
4.0.1 (2015-06-05)
--------------------
diff --git a/setup.py b/setup.py
index 006e0b1..8eae3ef 100644
--- a/setup.py
+++ b/setup.py
@@ -63,9 +63,9 @@ setup(
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
+ 'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
'Natural Language :: English',
@@ -85,6 +85,7 @@ setup(
],
extras_require=dict(
test=[
+ 'python-gettext',
'zope.component [zcml]',
'zope.configuration',
'zope.security',
@@ -99,6 +100,7 @@ setup(
],
),
tests_require = [
+ 'python-gettext',
'zope.component [zcml]',
'zope.configuration',
'zope.security',
diff --git a/src/zope/i18n/format.py b/src/zope/i18n/format.py
index 7095ece..494d09c 100644
--- a/src/zope/i18n/format.py
+++ b/src/zope/i18n/format.py
@@ -31,6 +31,9 @@ from ._compat import _u
PY3 = sys.version_info[0] == 3
if PY3:
unicode = str
+ NATIVE_NUMBER_TYPES = (int, float)
+else:
+ NATIVE_NUMBER_TYPES = (int, float, long)
def roundHalfUp(n):
"""Works like round() in python2.x
@@ -327,8 +330,11 @@ class NumberFormat(object):
integer = self.symbols['nativeZeroDigit']*(min_size-size) + integer
return integer
- def _format_fraction(self, fraction, pattern):
- max_precision = len(pattern)
+ def _format_fraction(self, fraction, pattern, rounding=True):
+ if rounding:
+ max_precision = len(pattern)
+ else:
+ max_precision = sys.maxsize
min_precision = pattern.count('0')
precision = len(fraction)
roundInt = False
@@ -356,7 +362,7 @@ class NumberFormat(object):
fraction = self.symbols['decimal'] + fraction
return fraction, roundInt
- def format(self, obj, pattern=None):
+ def format(self, obj, pattern=None, rounding=True):
"See zope.i18n.interfaces.IFormat"
# Make or get binary form of datetime pattern
if pattern is not None:
@@ -369,9 +375,24 @@ class NumberFormat(object):
else:
bin_pattern = bin_pattern[1]
+ if isinstance(obj, NATIVE_NUMBER_TYPES):
+ # repr() handles high-precision numbers correctly in
+ # Python 2 and 3. str() is only correct in Python 3.
+ strobj = repr(obj)
+ else:
+ strobj = str(obj)
+ if 'e' in strobj:
+ # Str(obj) # returned scientific representation of a number (e.g.
+ # 1e-7). We can't rely on str() to format fraction.
+ decimalprec = len(bin_pattern[FRACTION]) or 1
+ obj_int, obj_frac = ("%.*f" % (decimalprec, obj)).split('.')
+ # Remove trailing 0, but leave at least one
+ obj_frac = obj_frac.rstrip("0") or "0"
+ obj_int_frac = [obj_int, obj_frac]
+ else:
+ obj_int_frac = strobj.split('.')
if bin_pattern[EXPONENTIAL] != '':
- obj_int_frac = str(obj).split('.')
# The exponential might have a mandatory sign; remove it from the
# bin_pattern and remember the setting
exp_bin_pattern = bin_pattern[EXPONENTIAL]
@@ -400,7 +421,8 @@ class NumberFormat(object):
number = ''.join(obj_int_frac)
fraction, roundInt = self._format_fraction(number[1:],
- bin_pattern[FRACTION])
+ bin_pattern[FRACTION],
+ rounding=rounding)
if roundInt:
number = str(int(number[0]) + 1) + fraction
else:
@@ -415,10 +437,9 @@ class NumberFormat(object):
number += self.symbols['exponential'] + exponent
else:
- obj_int_frac = str(obj).split('.')
if len(obj_int_frac) > 1:
- fraction, roundInt = self._format_fraction(obj_int_frac[1],
- bin_pattern[FRACTION])
+ fraction, roundInt = self._format_fraction(
+ obj_int_frac[1], bin_pattern[FRACTION], rounding=rounding)
else:
fraction = ''
roundInt = False
diff --git a/src/zope/i18n/tests/locale3/en/LC_MESSAGES/zope-i18n.in b/src/zope/i18n/tests/locale3/en/LC_MESSAGES/zope-i18n.in
index d58a7d7..56ec799 100644
--- a/src/zope/i18n/tests/locale3/en/LC_MESSAGES/zope-i18n.in
+++ b/src/zope/i18n/tests/locale3/en/LC_MESSAGES/zope-i18n.in
Binary files differ
diff --git a/src/zope/i18n/tests/locale3/en/LC_MESSAGES/zope-i18n.mo b/src/zope/i18n/tests/locale3/en/LC_MESSAGES/zope-i18n.mo
index 8bdb36f..56ec799 100644
--- a/src/zope/i18n/tests/locale3/en/LC_MESSAGES/zope-i18n.mo
+++ b/src/zope/i18n/tests/locale3/en/LC_MESSAGES/zope-i18n.mo
Binary files differ
diff --git a/src/zope/i18n/tests/test_formats.py b/src/zope/i18n/tests/test_formats.py
index aa7910c..ce235c3 100644
--- a/src/zope/i18n/tests/test_formats.py
+++ b/src/zope/i18n/tests/test_formats.py
@@ -1176,6 +1176,32 @@ class TestNumberFormat(TestCase):
self.assertEqual(self.format.format(41.02, '(0.0##E0##* )* '),
'(4.102E1 ) ')
+ def testFormatSmallNumbers(self):
+ self.assertEqual(self.format.format(
+ -1e-7, '(#0.00#####);(-#0.00#####)'), '(-0.0000001)')
+ self.assertEqual(self.format.format(1e-9, '(#0.00###)'), '(0.00)')
+ self.assertEqual(self.format.format(1e-9, '(#0.00###)'), '(0.00)')
+
+ def testFormatHighPrecisionNumbers(self):
+ self.assertEqual(self.format.format(
+ 1+1e-7, '(#0.00#####);(-#0.00#####)'), '(1.0000001)')
+ self.assertEqual(self.format.format(
+ 1+1e-7, '(#0.00###)'), '(1.00000)')
+ self.assertEqual(self.format.format(
+ 1+1e-9, '(#0.00#######);(-#0.00#######)'), '(1.000000001)')
+ self.assertEqual(self.format.format(
+ 1+1e-9, '(#0.00###)'), '(1.00000)')
+ self.assertEqual(self.format.format(
+ 1+1e-12, '(#0.00##########);(-#0.00##########)'),
+ '(1.000000000001)')
+ self.assertEqual(self.format.format(
+ 1+1e-12, '(#0.00###)'), '(1.00000)')
+
+ def testNoRounding(self):
+ # Witout Rounding
+ self.assertEqual(self.format.format(
+ decimal.Decimal('0.99999'), '0.###', rounding=False), '0.99999')
+
def test_suite():
return TestSuite((
diff --git a/tox.ini b/tox.ini
index 17f33ed..cd5b081 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,7 +1,9 @@
[tox]
-envlist = py27,py33,py34,py35,pypy,pypy3,coverage,docs
+envlist = py27,py34,py35,py36,pypy,pypy3,coverage,docs
[testenv]
+# We need to use develop egg to get namespace directories right.
+usedevelop = true
commands =
python setup.py -q test -q
deps =